highlight error field

This commit is contained in:
Samuel Newman
2025-11-10 19:05:09 +02:00
parent 4b382644f9
commit be2226d079
+18 -7
View File
@@ -57,9 +57,11 @@ export const LoginForm = ({
onAttemptFailed: () => void onAttemptFailed: () => void
}) => { }) => {
const t = useTheme() const t = useTheme()
const [isProcessing, setIsProcessing] = useState<boolean>(false) const [isProcessing, setIsProcessing] = useState(false)
const [isAuthFactorTokenNeeded, setIsAuthFactorTokenNeeded] = const [errorField, setErrorField] = useState<
useState<boolean>(false) 'none' | 'identifier' | 'password' | '2fa'
>('none')
const [isAuthFactorTokenNeeded, setIsAuthFactorTokenNeeded] = useState(false)
const identifierValueRef = useRef<string>(initialHandle || '') const identifierValueRef = useRef<string>(initialHandle || '')
const passwordValueRef = useRef<string>('') const passwordValueRef = useRef<string>('')
const [authFactorToken, setAuthFactorToken] = useState('') const [authFactorToken, setAuthFactorToken] = useState('')
@@ -80,17 +82,20 @@ export const LoginForm = ({
if (isProcessing) return if (isProcessing) return
Keyboard.dismiss() Keyboard.dismiss()
setError('') setError('')
setErrorField('none')
const identifier = identifierValueRef.current.toLowerCase().trim() const identifier = identifierValueRef.current.toLowerCase().trim()
const password = passwordValueRef.current const password = passwordValueRef.current
if (!identifier) { if (!identifier) {
setError(_(msg`Please enter your username`)) setError(_(msg`Please enter your username`))
setErrorField('identifier')
return return
} }
if (!password) { if (!password) {
setError(_(msg`Please enter your password`)) setError(_(msg`Please enter your password`))
setErrorField('password')
return return
} }
@@ -147,6 +152,7 @@ export const LoginForm = ({
error: errMsg, error: errMsg,
}) })
setError(_(msg`Invalid 2FA confirmation code.`)) setError(_(msg`Invalid 2FA confirmation code.`))
setErrorField('2fa')
} else if ( } else if (
errMsg.includes('Authentication Required') || errMsg.includes('Authentication Required') ||
errMsg.includes('Invalid identifier or password') errMsg.includes('Invalid identifier or password')
@@ -187,7 +193,7 @@ export const LoginForm = ({
<Trans>Account</Trans> <Trans>Account</Trans>
</TextField.LabelText> </TextField.LabelText>
<View style={[a.gap_sm]}> <View style={[a.gap_sm]}>
<TextField.Root> <TextField.Root isInvalid={errorField === 'identifier'}>
<TextField.Icon icon={At} /> <TextField.Icon icon={At} />
<TextField.Input <TextField.Input
testID="loginUsernameInput" testID="loginUsernameInput"
@@ -202,6 +208,7 @@ export const LoginForm = ({
defaultValue={initialHandle || ''} defaultValue={initialHandle || ''}
onChangeText={v => { onChangeText={v => {
identifierValueRef.current = v identifierValueRef.current = v
if (errorField) setErrorField('none')
}} }}
onSubmitEditing={() => { onSubmitEditing={() => {
passwordRef.current?.focus() passwordRef.current?.focus()
@@ -214,7 +221,7 @@ export const LoginForm = ({
/> />
</TextField.Root> </TextField.Root>
<TextField.Root> <TextField.Root isInvalid={errorField === 'password'}>
<TextField.Icon icon={Lock} /> <TextField.Icon icon={Lock} />
<TextField.Input <TextField.Input
testID="loginPasswordInput" testID="loginPasswordInput"
@@ -229,6 +236,7 @@ export const LoginForm = ({
clearButtonMode="while-editing" clearButtonMode="while-editing"
onChangeText={v => { onChangeText={v => {
passwordValueRef.current = v passwordValueRef.current = v
if (errorField) setErrorField('none')
}} }}
onSubmitEditing={onPressNext} onSubmitEditing={onPressNext}
blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing
@@ -270,7 +278,7 @@ export const LoginForm = ({
<TextField.LabelText> <TextField.LabelText>
<Trans>2FA Confirmation</Trans> <Trans>2FA Confirmation</Trans>
</TextField.LabelText> </TextField.LabelText>
<TextField.Root> <TextField.Root isInvalid={errorField === '2fa'}>
<TextField.Icon icon={Ticket} /> <TextField.Icon icon={Ticket} />
<TextField.Input <TextField.Input
testID="loginAuthFactorTokenInput" testID="loginAuthFactorTokenInput"
@@ -281,8 +289,11 @@ export const LoginForm = ({
autoComplete="one-time-code" autoComplete="one-time-code"
returnKeyType="done" returnKeyType="done"
blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field
onChangeText={setAuthFactorToken}
value={authFactorToken} // controlled input due to uncontrolled input not receiving pasted values properly value={authFactorToken} // controlled input due to uncontrolled input not receiving pasted values properly
onChangeText={text => {
setAuthFactorToken(text)
if (errorField) setErrorField('none')
}}
onSubmitEditing={onPressNext} onSubmitEditing={onPressNext}
editable={!isProcessing} editable={!isProcessing}
accessibilityHint={_( accessibilityHint={_(