Changeset View
Changeset View
Standalone View
Standalone View
native/account/registration/password-selection.react.js
// @flow | // @flow | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { Text } from 'react-native'; | import { View, Text } from 'react-native'; | ||||
import RegistrationButtonContainer from './registration-button-container.react.js'; | import RegistrationButtonContainer from './registration-button-container.react.js'; | ||||
import RegistrationButton from './registration-button.react.js'; | import RegistrationButton from './registration-button.react.js'; | ||||
import RegistrationContainer from './registration-container.react.js'; | import RegistrationContainer from './registration-container.react.js'; | ||||
import RegistrationContentContainer from './registration-content-container.react.js'; | import RegistrationContentContainer from './registration-content-container.react.js'; | ||||
import type { RegistrationNavigationProp } from './registration-navigator.react.js'; | import type { RegistrationNavigationProp } from './registration-navigator.react.js'; | ||||
import RegistrationTextInput from './registration-text-input.react.js'; | |||||
import type { CoolOrNerdMode } from './registration-types.js'; | import type { CoolOrNerdMode } from './registration-types.js'; | ||||
import type { NavigationRoute } from '../../navigation/route-names.js'; | import type { NavigationRoute } from '../../navigation/route-names.js'; | ||||
import { useStyles } from '../../themes/colors.js'; | import { useStyles } from '../../themes/colors.js'; | ||||
export type PasswordSelectionParams = { | export type PasswordSelectionParams = { | ||||
+userSelections: { | +userSelections: { | ||||
+coolOrNerdMode: CoolOrNerdMode, | +coolOrNerdMode: CoolOrNerdMode, | ||||
+keyserverUsername: string, | +keyserverUsername: string, | ||||
+username: string, | +username: string, | ||||
}, | }, | ||||
}; | }; | ||||
type PasswordError = 'passwords_dont_match' | 'empty_password'; | |||||
type Props = { | type Props = { | ||||
+navigation: RegistrationNavigationProp<'PasswordSelection'>, | +navigation: RegistrationNavigationProp<'PasswordSelection'>, | ||||
+route: NavigationRoute<'PasswordSelection'>, | +route: NavigationRoute<'PasswordSelection'>, | ||||
}; | }; | ||||
// eslint-disable-next-line no-unused-vars | // eslint-disable-next-line no-unused-vars | ||||
function PasswordSelection(props: Props): React.Node { | function PasswordSelection(props: Props): React.Node { | ||||
const onProceed = React.useCallback(() => {}, []); | const [password, setPassword] = React.useState(''); | ||||
const [confirmPassword, setConfirmPassword] = React.useState(''); | |||||
const passwordsMatch = password === confirmPassword; | |||||
const passwordIsEmpty = password === ''; | |||||
const [passwordError, setPasswordError] = React.useState<?PasswordError>(); | |||||
const potentiallyClearErrors = React.useCallback(() => { | |||||
if (!passwordsMatch || passwordIsEmpty) { | |||||
return false; | |||||
} | |||||
setPasswordError(null); | |||||
return true; | |||||
}, [passwordsMatch, passwordIsEmpty]); | |||||
const checkPasswordValidity = React.useCallback(() => { | |||||
if (!passwordsMatch) { | |||||
setPasswordError('passwords_dont_match'); | |||||
return false; | |||||
} else if (passwordIsEmpty) { | |||||
setPasswordError('empty_password'); | |||||
return false; | |||||
} | |||||
return potentiallyClearErrors(); | |||||
}, [passwordsMatch, passwordIsEmpty, potentiallyClearErrors]); | |||||
const onProceed = React.useCallback(() => { | |||||
if (!checkPasswordValidity()) { | |||||
return; | |||||
} | |||||
}, [checkPasswordValidity]); | |||||
const styles = useStyles(unboundStyles); | const styles = useStyles(unboundStyles); | ||||
let errorText; | |||||
if (passwordError === 'passwords_dont_match') { | |||||
errorText = ( | |||||
<Text style={styles.errorText}>Passwords don’t match</Text> | |||||
); | |||||
} else if (passwordError === 'empty_password') { | |||||
errorText = <Text style={styles.errorText}>Password cannot be empty</Text>; | |||||
} | |||||
const confirmPasswordInputRef = React.useRef(); | |||||
const focusConfirmPasswordInput = React.useCallback(() => { | |||||
confirmPasswordInputRef.current?.focus(); | |||||
}, []); | |||||
return ( | return ( | ||||
<RegistrationContainer> | <RegistrationContainer> | ||||
<RegistrationContentContainer> | <RegistrationContentContainer> | ||||
<Text style={styles.header}>Pick a password</Text> | <Text style={styles.header}>Pick a password</Text> | ||||
<RegistrationTextInput | |||||
value={password} | |||||
onChangeText={setPassword} | |||||
placeholder="Password" | |||||
autoFocus={true} | |||||
secureTextEntry={true} | |||||
textContentType="newPassword" | |||||
autoComplete="password-new" | |||||
returnKeyType="next" | |||||
onSubmitEditing={focusConfirmPasswordInput} | |||||
onBlur={potentiallyClearErrors} | |||||
/> | |||||
<RegistrationTextInput | |||||
value={confirmPassword} | |||||
onChangeText={setConfirmPassword} | |||||
placeholder="Confirm password" | |||||
secureTextEntry={true} | |||||
textContentType="newPassword" | |||||
autoComplete="password-new" | |||||
returnKeyType="go" | |||||
onSubmitEditing={onProceed} | |||||
onBlur={checkPasswordValidity} | |||||
style={styles.confirmPassword} | |||||
ref={confirmPasswordInputRef} | |||||
/> | |||||
<View style={styles.error}>{errorText}</View> | |||||
</RegistrationContentContainer> | </RegistrationContentContainer> | ||||
<RegistrationButtonContainer> | <RegistrationButtonContainer> | ||||
<RegistrationButton | <RegistrationButton | ||||
onPress={onProceed} | onPress={onProceed} | ||||
label="Next" | label="Next" | ||||
variant="disabled" | variant={passwordsMatch ? 'enabled' : 'disabled'} | ||||
/> | /> | ||||
</RegistrationButtonContainer> | </RegistrationButtonContainer> | ||||
</RegistrationContainer> | </RegistrationContainer> | ||||
); | ); | ||||
} | } | ||||
const unboundStyles = { | const unboundStyles = { | ||||
header: { | header: { | ||||
fontSize: 24, | fontSize: 24, | ||||
color: 'panelForegroundLabel', | color: 'panelForegroundLabel', | ||||
paddingBottom: 16, | paddingBottom: 16, | ||||
}, | }, | ||||
error: { | |||||
marginTop: 16, | |||||
}, | |||||
errorText: { | |||||
fontFamily: 'Arial', | |||||
fontSize: 15, | |||||
lineHeight: 20, | |||||
color: 'redText', | |||||
}, | |||||
confirmPassword: { | |||||
marginTop: 16, | |||||
}, | |||||
}; | }; | ||||
export default PasswordSelection; | export default PasswordSelection; |