Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Samuel Newman
Hailey
and committed by
GitHub
ae9176c9 3d954a00

+79 -23
+46 -19
src/screens/Signup/StepInfo/index.tsx
··· 75 75 const emailChanged = prevEmailValueRef.current !== email 76 76 const password = passwordValueRef.current 77 77 78 - if (emailChanged && tldtsRef.current) { 79 - if (isEmailMaybeInvalid(email, tldtsRef.current)) { 80 - prevEmailValueRef.current = email 81 - setHasWarnedEmail(true) 82 - return dispatch({ 83 - type: 'setError', 84 - value: _( 85 - msg`It looks like you may have entered your email address incorrectly. Are you sure it's right?`, 86 - ), 87 - }) 88 - } 89 - } else if (hasWarnedEmail) { 90 - setHasWarnedEmail(false) 91 - } 92 - prevEmailValueRef.current = email 93 - 94 78 if (!is13(state.dateOfBirth)) { 95 79 return 96 80 } ··· 99 83 return dispatch({ 100 84 type: 'setError', 101 85 value: _(msg`Please enter your invite code.`), 86 + field: 'invite-code', 102 87 }) 103 88 } 104 89 if (!email) { 105 90 return dispatch({ 106 91 type: 'setError', 107 92 value: _(msg`Please enter your email.`), 93 + field: 'email', 108 94 }) 109 95 } 110 96 if (!EmailValidator.validate(email)) { 111 97 return dispatch({ 112 98 type: 'setError', 113 99 value: _(msg`Your email appears to be invalid.`), 100 + field: 'email', 114 101 }) 115 102 } 103 + if (emailChanged && tldtsRef.current) { 104 + if (isEmailMaybeInvalid(email, tldtsRef.current)) { 105 + prevEmailValueRef.current = email 106 + setHasWarnedEmail(true) 107 + return dispatch({ 108 + type: 'setError', 109 + value: _( 110 + msg`Please double-check that you have entered your email address correctly.`, 111 + ), 112 + }) 113 + } 114 + } else if (hasWarnedEmail) { 115 + setHasWarnedEmail(false) 116 + } 117 + prevEmailValueRef.current = email 116 118 if (!password) { 117 119 return dispatch({ 118 120 type: 'setError', 119 121 value: _(msg`Please choose your password.`), 122 + field: 'password', 123 + }) 124 + } 125 + if (password.length < 8) { 126 + return dispatch({ 127 + type: 'setError', 128 + value: _(msg`Your password must be at least 8 characters long.`), 129 + field: 'password', 120 130 }) 121 131 } 122 132 ··· 149 159 <TextField.LabelText> 150 160 <Trans>Invite code</Trans> 151 161 </TextField.LabelText> 152 - <TextField.Root> 162 + <TextField.Root isInvalid={state.errorField === 'invite-code'}> 153 163 <TextField.Icon icon={Ticket} /> 154 164 <TextField.Input 155 165 onChangeText={value => { 156 166 inviteCodeValueRef.current = value.trim() 167 + if ( 168 + state.errorField === 'invite-code' && 169 + value.trim().length > 0 170 + ) { 171 + dispatch({type: 'clearError'}) 172 + } 157 173 }} 158 174 label={_(msg`Required for this provider`)} 159 175 defaultValue={state.inviteCode} ··· 173 189 <TextField.LabelText> 174 190 <Trans>Email</Trans> 175 191 </TextField.LabelText> 176 - <TextField.Root> 192 + <TextField.Root isInvalid={state.errorField === 'email'}> 177 193 <TextField.Icon icon={Envelope} /> 178 194 <TextField.Input 179 195 testID="emailInput" ··· 183 199 if (hasWarnedEmail) { 184 200 setHasWarnedEmail(false) 185 201 } 202 + if ( 203 + state.errorField === 'email' && 204 + value.trim().length > 0 && 205 + EmailValidator.validate(value.trim()) 206 + ) { 207 + dispatch({type: 'clearError'}) 208 + } 186 209 }} 187 210 label={_(msg`Enter your email address`)} 188 211 defaultValue={state.email} ··· 201 224 <TextField.LabelText> 202 225 <Trans>Password</Trans> 203 226 </TextField.LabelText> 204 - <TextField.Root> 227 + <TextField.Root isInvalid={state.errorField === 'password'}> 205 228 <TextField.Icon icon={Lock} /> 206 229 <TextField.Input 207 230 testID="passwordInput" 208 231 inputRef={passwordInputRef} 209 232 onChangeText={value => { 210 233 passwordValueRef.current = value 234 + if (state.errorField === 'password' && value.length >= 8) { 235 + dispatch({type: 'clearError'}) 236 + } 211 237 }} 212 238 label={_(msg`Choose your password`)} 213 239 defaultValue={state.password} ··· 219 245 onSubmitEditing={native(() => 220 246 birthdateInputRef.current?.focus(), 221 247 )} 248 + passwordRules="minlength: 8;" 222 249 /> 223 250 </TextField.Root> 224 251 </View>
+19 -1
src/screens/Signup/state.ts
··· 31 31 mutableProcessed: boolean // OK to mutate assuming it's never read in render. 32 32 } 33 33 34 + type ErrorField = 35 + | 'invite-code' 36 + | 'email' 37 + | 'handle' 38 + | 'password' 39 + | 'date-of-birth' 40 + 34 41 export type SignupState = { 35 42 hasPrev: boolean 36 43 activeStep: SignupStep ··· 45 52 handle: string 46 53 47 54 error: string 55 + errorField?: ErrorField 48 56 isLoading: boolean 49 57 50 58 pendingSubmit: null | SubmitTask ··· 62 70 | {type: 'setDateOfBirth'; value: Date} 63 71 | {type: 'setInviteCode'; value: string} 64 72 | {type: 'setHandle'; value: string} 65 - | {type: 'setError'; value: string} 73 + | {type: 'setError'; value: string; field?: ErrorField} 74 + | {type: 'clearError'} 66 75 | {type: 'setIsLoading'; value: boolean} 67 76 | {type: 'submit'; task: SubmitTask} 68 77 ··· 80 89 inviteCode: '', 81 90 82 91 error: '', 92 + errorField: undefined, 83 93 isLoading: false, 84 94 85 95 pendingSubmit: null, ··· 102 112 LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 103 113 next.activeStep-- 104 114 next.error = '' 115 + next.errorField = undefined 105 116 } 106 117 break 107 118 } ··· 110 121 LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 111 122 next.activeStep++ 112 123 next.error = '' 124 + next.errorField = undefined 113 125 } 114 126 break 115 127 } ··· 156 168 } 157 169 case 'setError': { 158 170 next.error = a.value 171 + next.errorField = a.field 172 + break 173 + } 174 + case 'clearError': { 175 + next.error = '' 176 + next.errorField = undefined 159 177 break 160 178 } 161 179 case 'submit': {
+14 -3
src/view/com/modals/ChangePassword.tsx
··· 81 81 82 82 const onChangePassword = async () => { 83 83 const formattedCode = checkAndFormatResetCode(resetCode) 84 - // TODO Better password strength check 85 - if (!formattedCode || !newPassword) { 84 + if (!formattedCode) { 86 85 setError( 87 86 _( 88 87 msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, ··· 90 89 ) 91 90 return 92 91 } 92 + if (!newPassword) { 93 + setError( 94 + _(msg`Please enter a password. It must be at least 8 characters long.`), 95 + ) 96 + return 97 + } 98 + if (newPassword.length < 8) { 99 + setError(_(msg`Password must be at least 8 characters long.`)) 100 + return 101 + } 93 102 94 103 setError('') 95 104 setIsProcessing(true) ··· 104 113 logger.warn('Failed to set new password', {error: e}) 105 114 if (isNetworkError(e)) { 106 115 setError( 107 - 'Unable to contact your service. Please check your Internet connection.', 116 + _( 117 + msg`Unable to contact your service. Please check your Internet connection.`, 118 + ), 108 119 ) 109 120 } else { 110 121 setError(cleanError(errMsg))