Handle token field validation

This commit is contained in:
Eric Bailey
2025-05-07 10:15:45 -05:00
parent b0247968fb
commit 75b8679249
4 changed files with 58 additions and 11 deletions
@@ -8,7 +8,11 @@ import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shiel
export function normalizeCode(value: string) {
const normalized = value.toUpperCase().replace(/[^A-Z2-7]/g, '')
if (normalized.length <= 5) return normalized
return `${normalized.slice(0, 5)}-${normalized.slice(5, 10)}`
return `${normalized.slice(0, 5)}-${normalized.slice(5)}`
}
export function isValidCode(value?: string) {
return Boolean(value && /^[A-Z2-7]{5}-[A-Z2-7]{5}$/.test(value))
}
export function TokenField({
@@ -17,6 +21,7 @@ export function TokenField({
onSubmitEditing,
}: Pick<TextInputProps, 'value' | 'onChangeText' | 'onSubmitEditing'>) {
const {_} = useLingui()
const isInvalid = Boolean(value && value.length > 10 && !isValidCode(value))
const handleOnChangeText = (v: string) => {
onChangeText?.(normalizeCode(v))
@@ -27,6 +32,7 @@ export function TokenField({
<TextField.Root>
<TextField.Icon icon={Shield} />
<TextField.Input
isInvalid={isInvalid}
label={_(msg`Confirmation code`)}
placeholder="XXXXX-XXXXX"
value={value}
@@ -12,7 +12,10 @@ import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogContext} from '#/components/Dialog'
import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'
import {TokenField} from '#/components/dialogs/EmailDialog/components/TokenField'
import {
isValidCode,
TokenField,
} from '#/components/dialogs/EmailDialog/components/TokenField'
import {useManageEmail2FA} from '#/components/dialogs/EmailDialog/data/useManageEmail2FA'
import {useRequestEmailUpdate} from '#/components/dialogs/EmailDialog/data/useRequestEmailUpdate'
import {Divider} from '#/components/Divider'
@@ -122,6 +125,14 @@ export function Disable() {
}
const handleManageEmail2FA = async () => {
if (!isValidCode(token)) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid code.`),
})
return
}
dispatch({type: 'setTokenStatus', status: 'pending'})
try {
@@ -211,7 +222,7 @@ export function Disable() {
<TokenField
value={token}
onChangeText={setToken}
onSubmitEditing={() => {}}
onSubmitEditing={handleManageEmail2FA}
/>
<ResendEmailText onPress={handleSendEmail} />
</View>
@@ -224,7 +235,9 @@ export function Disable() {
variant="solid"
color="primary"
onPress={handleManageEmail2FA}
disabled={!token || state.tokenStatus === 'pending'}>
disabled={
!token || token.length !== 11 || state.tokenStatus === 'pending'
}>
<ButtonText>
<Trans>Disable 2FA</Trans>
</ButtonText>
@@ -12,7 +12,10 @@ import {atoms as a, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'
import {TokenField} from '#/components/dialogs/EmailDialog/components/TokenField'
import {
isValidCode,
TokenField,
} from '#/components/dialogs/EmailDialog/components/TokenField'
import {useRequestEmailUpdate} from '#/components/dialogs/EmailDialog/data/useRequestEmailUpdate'
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
import {useUpdateEmail} from '#/components/dialogs/EmailDialog/data/useUpdateEmail'
@@ -126,6 +129,14 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
}
const handleUpdateEmail = async () => {
if (state.step === 'token' && !isValidCode(state.token)) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid code.`),
})
return
}
dispatch({
type: 'setMutationStatus',
status: 'pending',
@@ -202,7 +213,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
<View style={[a.gap_md]}>
<View>
<Text style={[a.pb_sm, t.atoms.text_contrast_medium]}>
<Text style={[a.pb_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>Please enter your new email address.</Trans>
</Text>
<TextField.Root>
@@ -231,7 +242,8 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
<Text style={[a.text_md, a.pb_sm, a.font_bold]}>
<Trans>Security step required</Trans>
</Text>
<Text style={[a.pb_sm, t.atoms.text_contrast_medium]}>
<Text
style={[a.pb_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
Please enter the security code we sent to your previous email
address.
@@ -292,7 +304,8 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
onPress={handleUpdateEmail}
disabled={
!state.email ||
(state.step === 'token' && !state.token) ||
(state.step === 'token' &&
(!state.token || state.token.length !== 11)) ||
state.mutationStatus === 'pending'
}>
<ButtonText>
@@ -11,7 +11,10 @@ import {atoms as a, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'
import {TokenField} from '#/components/dialogs/EmailDialog/components/TokenField'
import {
isValidCode,
TokenField,
} from '#/components/dialogs/EmailDialog/components/TokenField'
import {useConfirmEmail} from '#/components/dialogs/EmailDialog/data/useConfirmEmail'
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
import {useOnEmailVerified} from '#/components/dialogs/EmailDialog/events'
@@ -136,6 +139,14 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
}
const handleConfirmEmail = async () => {
if (!isValidCode(state.token)) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid code.`),
})
return
}
dispatch({
type: 'setMutationStatus',
status: 'pending',
@@ -328,7 +339,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
value: token,
})
}}
onSubmitEditing={() => {}}
onSubmitEditing={handleConfirmEmail}
/>
{state.error && <Admonition type="error">{state.error}</Admonition>}
@@ -339,7 +350,11 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
variant="solid"
color="primary"
onPress={handleConfirmEmail}
disabled={!state.token || state.mutationStatus === 'pending'}>
disabled={
!state.token ||
state.token.length !== 11 ||
state.mutationStatus === 'pending'
}>
<ButtonText>
<Trans>Verify code</Trans>
</ButtonText>