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