Basic minimum password requirements, plus field-specific errors (#7811)
* add min password requirement * add field specific errors * move email tld check to after other email checks * add password length check to change password dialog * Update src/view/com/modals/ChangePassword.tsx Co-authored-by: Hailey <me@haileyok.com> * Update src/screens/Signup/StepInfo/index.tsx Co-authored-by: Hailey <me@haileyok.com> * fix lint --------- Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
@@ -75,22 +75,6 @@ export function StepInfo({
|
||||
const emailChanged = prevEmailValueRef.current !== email
|
||||
const password = passwordValueRef.current
|
||||
|
||||
if (emailChanged && tldtsRef.current) {
|
||||
if (isEmailMaybeInvalid(email, tldtsRef.current)) {
|
||||
prevEmailValueRef.current = email
|
||||
setHasWarnedEmail(true)
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(
|
||||
msg`It looks like you may have entered your email address incorrectly. Are you sure it's right?`,
|
||||
),
|
||||
})
|
||||
}
|
||||
} else if (hasWarnedEmail) {
|
||||
setHasWarnedEmail(false)
|
||||
}
|
||||
prevEmailValueRef.current = email
|
||||
|
||||
if (!is13(state.dateOfBirth)) {
|
||||
return
|
||||
}
|
||||
@@ -99,24 +83,50 @@ export function StepInfo({
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please enter your invite code.`),
|
||||
field: 'invite-code',
|
||||
})
|
||||
}
|
||||
if (!email) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please enter your email.`),
|
||||
field: 'email',
|
||||
})
|
||||
}
|
||||
if (!EmailValidator.validate(email)) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Your email appears to be invalid.`),
|
||||
field: 'email',
|
||||
})
|
||||
}
|
||||
if (emailChanged && tldtsRef.current) {
|
||||
if (isEmailMaybeInvalid(email, tldtsRef.current)) {
|
||||
prevEmailValueRef.current = email
|
||||
setHasWarnedEmail(true)
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(
|
||||
msg`Please double-check that you have entered your email address correctly.`,
|
||||
),
|
||||
})
|
||||
}
|
||||
} else if (hasWarnedEmail) {
|
||||
setHasWarnedEmail(false)
|
||||
}
|
||||
prevEmailValueRef.current = email
|
||||
if (!password) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Please choose your password.`),
|
||||
field: 'password',
|
||||
})
|
||||
}
|
||||
if (password.length < 8) {
|
||||
return dispatch({
|
||||
type: 'setError',
|
||||
value: _(msg`Your password must be at least 8 characters long.`),
|
||||
field: 'password',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -149,11 +159,17 @@ export function StepInfo({
|
||||
<TextField.LabelText>
|
||||
<Trans>Invite code</Trans>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Root isInvalid={state.errorField === 'invite-code'}>
|
||||
<TextField.Icon icon={Ticket} />
|
||||
<TextField.Input
|
||||
onChangeText={value => {
|
||||
inviteCodeValueRef.current = value.trim()
|
||||
if (
|
||||
state.errorField === 'invite-code' &&
|
||||
value.trim().length > 0
|
||||
) {
|
||||
dispatch({type: 'clearError'})
|
||||
}
|
||||
}}
|
||||
label={_(msg`Required for this provider`)}
|
||||
defaultValue={state.inviteCode}
|
||||
@@ -173,7 +189,7 @@ export function StepInfo({
|
||||
<TextField.LabelText>
|
||||
<Trans>Email</Trans>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Root isInvalid={state.errorField === 'email'}>
|
||||
<TextField.Icon icon={Envelope} />
|
||||
<TextField.Input
|
||||
testID="emailInput"
|
||||
@@ -183,6 +199,13 @@ export function StepInfo({
|
||||
if (hasWarnedEmail) {
|
||||
setHasWarnedEmail(false)
|
||||
}
|
||||
if (
|
||||
state.errorField === 'email' &&
|
||||
value.trim().length > 0 &&
|
||||
EmailValidator.validate(value.trim())
|
||||
) {
|
||||
dispatch({type: 'clearError'})
|
||||
}
|
||||
}}
|
||||
label={_(msg`Enter your email address`)}
|
||||
defaultValue={state.email}
|
||||
@@ -201,13 +224,16 @@ export function StepInfo({
|
||||
<TextField.LabelText>
|
||||
<Trans>Password</Trans>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root>
|
||||
<TextField.Root isInvalid={state.errorField === 'password'}>
|
||||
<TextField.Icon icon={Lock} />
|
||||
<TextField.Input
|
||||
testID="passwordInput"
|
||||
inputRef={passwordInputRef}
|
||||
onChangeText={value => {
|
||||
passwordValueRef.current = value
|
||||
if (state.errorField === 'password' && value.length >= 8) {
|
||||
dispatch({type: 'clearError'})
|
||||
}
|
||||
}}
|
||||
label={_(msg`Choose your password`)}
|
||||
defaultValue={state.password}
|
||||
@@ -219,6 +245,7 @@ export function StepInfo({
|
||||
onSubmitEditing={native(() =>
|
||||
birthdateInputRef.current?.focus(),
|
||||
)}
|
||||
passwordRules="minlength: 8;"
|
||||
/>
|
||||
</TextField.Root>
|
||||
</View>
|
||||
|
||||
@@ -31,6 +31,13 @@ type SubmitTask = {
|
||||
mutableProcessed: boolean // OK to mutate assuming it's never read in render.
|
||||
}
|
||||
|
||||
type ErrorField =
|
||||
| 'invite-code'
|
||||
| 'email'
|
||||
| 'handle'
|
||||
| 'password'
|
||||
| 'date-of-birth'
|
||||
|
||||
export type SignupState = {
|
||||
hasPrev: boolean
|
||||
activeStep: SignupStep
|
||||
@@ -45,6 +52,7 @@ export type SignupState = {
|
||||
handle: string
|
||||
|
||||
error: string
|
||||
errorField?: ErrorField
|
||||
isLoading: boolean
|
||||
|
||||
pendingSubmit: null | SubmitTask
|
||||
@@ -62,7 +70,8 @@ export type SignupAction =
|
||||
| {type: 'setDateOfBirth'; value: Date}
|
||||
| {type: 'setInviteCode'; value: string}
|
||||
| {type: 'setHandle'; value: string}
|
||||
| {type: 'setError'; value: string}
|
||||
| {type: 'setError'; value: string; field?: ErrorField}
|
||||
| {type: 'clearError'}
|
||||
| {type: 'setIsLoading'; value: boolean}
|
||||
| {type: 'submit'; task: SubmitTask}
|
||||
|
||||
@@ -80,6 +89,7 @@ export const initialState: SignupState = {
|
||||
inviteCode: '',
|
||||
|
||||
error: '',
|
||||
errorField: undefined,
|
||||
isLoading: false,
|
||||
|
||||
pendingSubmit: null,
|
||||
@@ -102,6 +112,7 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||
next.activeStep--
|
||||
next.error = ''
|
||||
next.errorField = undefined
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -110,6 +121,7 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||
next.activeStep++
|
||||
next.error = ''
|
||||
next.errorField = undefined
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -156,6 +168,12 @@ export function reducer(s: SignupState, a: SignupAction): SignupState {
|
||||
}
|
||||
case 'setError': {
|
||||
next.error = a.value
|
||||
next.errorField = a.field
|
||||
break
|
||||
}
|
||||
case 'clearError': {
|
||||
next.error = ''
|
||||
next.errorField = undefined
|
||||
break
|
||||
}
|
||||
case 'submit': {
|
||||
|
||||
@@ -81,8 +81,7 @@ export function Component() {
|
||||
|
||||
const onChangePassword = async () => {
|
||||
const formattedCode = checkAndFormatResetCode(resetCode)
|
||||
// TODO Better password strength check
|
||||
if (!formattedCode || !newPassword) {
|
||||
if (!formattedCode) {
|
||||
setError(
|
||||
_(
|
||||
msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`,
|
||||
@@ -90,6 +89,16 @@ export function Component() {
|
||||
)
|
||||
return
|
||||
}
|
||||
if (!newPassword) {
|
||||
setError(
|
||||
_(msg`Please enter a password. It must be at least 8 characters long.`),
|
||||
)
|
||||
return
|
||||
}
|
||||
if (newPassword.length < 8) {
|
||||
setError(_(msg`Password must be at least 8 characters long.`))
|
||||
return
|
||||
}
|
||||
|
||||
setError('')
|
||||
setIsProcessing(true)
|
||||
@@ -104,7 +113,9 @@ export function Component() {
|
||||
logger.warn('Failed to set new password', {error: e})
|
||||
if (isNetworkError(e)) {
|
||||
setError(
|
||||
'Unable to contact your service. Please check your Internet connection.',
|
||||
_(
|
||||
msg`Unable to contact your service. Please check your Internet connection.`,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
setError(cleanError(errMsg))
|
||||
|
||||
Reference in New Issue
Block a user