Fix email verification dialog (#10657)

This commit is contained in:
Samuel Newman
2026-05-29 21:03:00 +03:00
committed by GitHub
parent ce4efc511b
commit b0708dcefb
9 changed files with 62 additions and 86 deletions
-5
View File
@@ -124,11 +124,6 @@
"count": 1
}
},
"src/components/dialogs/EmailDialog/components/ResendEmailText.tsx": {
"@typescript-eslint/no-explicit-any": {
"count": 1
}
},
"src/components/dialogs/LanguageSelectDialog.tsx": {
"@typescript-eslint/no-explicit-any": {
"count": 1
@@ -1,7 +1,5 @@
import {useState} from 'react'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {wait} from '#/lib/async/wait'
import {atoms as a, type TextStyleProp, useTheme} from '#/alf'
@@ -14,10 +12,10 @@ export function ResendEmailText({
onPress,
style,
}: TextStyleProp & {
onPress: () => Promise<any>
onPress: () => Promise<unknown>
}) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const [status, setStatus] = useState<'sending' | 'success' | null>(null)
const handleOnPress = async () => {
@@ -38,7 +36,7 @@ export function ResendEmailText({
<Trans>
Don't see an email?{' '}
<InlineLinkText
label={_(msg`Resend`)}
label={l`Resend`}
{...createStaticClick(() => {
handleOnPress()
})}>
@@ -1,6 +1,5 @@
import {type TextInputProps, View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useLingui} from '@lingui/react/macro'
import * as TextField from '#/components/forms/TextField'
import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield'
@@ -20,7 +19,7 @@ export function TokenField({
onChangeText,
onSubmitEditing,
}: Pick<TextInputProps, 'value' | 'onChangeText' | 'onSubmitEditing'>) {
const {_} = useLingui()
const {t: l} = useLingui()
const isInvalid = Boolean(value && value.length > 10 && !isValidCode(value))
const handleOnChangeText = (v: string) => {
@@ -35,7 +34,7 @@ export function TokenField({
autoComplete="off"
autoCorrect={false}
isInvalid={isInvalid}
label={_(msg`Confirmation code`)}
label={l`Confirmation code`}
maxLength={11}
placeholder="XXXXX-XXXXX"
value={value}
+3 -5
View File
@@ -1,6 +1,5 @@
import {useCallback, useState} from 'react'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useLingui} from '@lingui/react/macro'
import {web} from '#/alf'
import * as Dialog from '#/components/Dialog'
@@ -21,7 +20,7 @@ export function useEmailDialogControl() {
}
export function EmailDialog() {
const {_} = useLingui()
const {t: l} = useLingui()
const emailDialogControl = useEmailDialogControl()
const {isEmailVerified} = useAccountEmailState()
const onClose = useCallback(() => {
@@ -36,9 +35,8 @@ export function EmailDialog() {
return (
<Dialog.Outer control={emailDialogControl.control} onClose={onClose}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`Make adjustments to email settings for your account`)}
label={l`Make adjustments to email settings for your account`}
style={web({maxWidth: 400})}>
<Inner control={emailDialogControl} />
<Dialog.Close />
@@ -1,8 +1,6 @@
import {useReducer, useState} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {wait} from '#/lib/async/wait'
import {useCleanError} from '#/lib/hooks/useCleanError'
@@ -90,7 +88,7 @@ function reducer(state: State, action: Action): State {
export function Disable() {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const cleanError = useCleanError()
const {currentAccount} = useSession()
const {mutateAsync: requestEmailUpdate} = useRequestEmailUpdate()
@@ -120,7 +118,7 @@ export function Disable() {
const {clean} = cleanError(e)
dispatch({
type: 'setError',
error: clean || _(msg`Failed to send email, please try again.`),
error: clean || l`Failed to send email, please try again.`,
})
}
}
@@ -129,7 +127,7 @@ export function Disable() {
if (!isValidCode(token)) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid code.`),
error: l`Please enter a valid code.`,
})
return
}
@@ -147,7 +145,7 @@ export function Disable() {
const {clean} = cleanError(e)
dispatch({
type: 'setError',
error: clean || _(msg`Failed to update email 2FA settings`),
error: clean || l`Failed to update email 2FA settings`,
})
}
}
@@ -157,7 +155,6 @@ export function Disable() {
<Text style={[a.text_xl, a.font_bold, a.leading_snug]}>
<Trans>Disable email 2FA</Trans>
</Text>
{state.step === 'email' ? (
<>
<Text
@@ -172,7 +169,7 @@ export function Disable() {
{state.error && <Admonition type="error">{state.error}</Admonition>}
<Button
label={_(msg`Send email`)}
label={l`Send email`}
size="large"
variant="solid"
color="primary"
@@ -199,7 +196,7 @@ export function Disable() {
<Trans>
Have a code?{' '}
<InlineLinkText
label={_(msg`Enter code`)}
label={l`Enter code`}
{...createStaticClick(() => {
dispatch({type: 'setStep', step: 'token'})
})}>
@@ -231,7 +228,7 @@ export function Disable() {
{state.error && <Admonition type="error">{state.error}</Admonition>}
<Button
label={_(msg`Disable 2FA`)}
label={l`Disable 2FA`}
size="large"
variant="solid"
color="primary"
@@ -1,8 +1,6 @@
import {useReducer} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {wait} from '#/lib/async/wait'
import {useCleanError} from '#/lib/hooks/useCleanError'
@@ -56,7 +54,7 @@ function reducer(state: State, action: Action): State {
export function Enable() {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const cleanError = useCleanError()
const {gtPhone} = useBreakpoints()
const {mutateAsync: manageEmail2FA} = useManageEmail2FA()
@@ -81,7 +79,7 @@ export function Enable() {
const {clean} = cleanError(e)
dispatch({
type: 'setError',
error: clean || _(msg`Failed to update email 2FA settings`),
error: clean || l`Failed to update email 2FA settings`,
})
}
}
@@ -97,12 +95,10 @@ export function Enable() {
<Trans>Require an email code to sign in to your account.</Trans>
</Text>
</View>
{state.error && <Admonition type="error">{state.error}</Admonition>}
<View style={[a.gap_sm, gtPhone && [a.flex_row_reverse]]}>
<Button
label={_(msg`Enable`)}
label={l`Enable`}
size="large"
variant="solid"
color="primary"
@@ -123,7 +119,7 @@ export function Enable() {
/>
</Button>
<Button
label={_(msg`Cancel`)}
label={l`Cancel`}
size="large"
variant="solid"
color="secondary"
@@ -1,8 +1,6 @@
import {useReducer} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {validate as validateEmail} from 'email-validator'
import {wait} from '#/lib/async/wait'
@@ -106,7 +104,7 @@ function reducer(state: State, action: Action): State {
export function Update(_props: ScreenProps<ScreenID.Update>) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const cleanError = useCleanError()
const {currentAccount} = useSession()
const [state, dispatch] = useReducer(reducer, {
@@ -133,7 +131,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
if (state.step === 'token' && !isValidCode(state.token)) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid code.`),
error: l`Please enter a valid code.`,
})
return
}
@@ -146,7 +144,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
if (state.emailValid === false) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid email address.`),
error: l`Please enter a valid email address.`,
})
return
}
@@ -154,7 +152,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
if (state.email === currentAccount!.email) {
dispatch({
type: 'setError',
error: _(msg`This email is already associated with your account.`),
error: l`This email is already associated with your account.`,
})
return
}
@@ -193,7 +191,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
const {clean} = cleanError(e)
dispatch({
type: 'setError',
error: clean || _(msg`Failed to update email, please try again.`),
error: clean || l`Failed to update email, please try again.`,
})
}
}
@@ -203,7 +201,6 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
<Text style={[a.text_xl, a.font_bold]}>
<Trans>Update your email</Trans>
</Text>
{currentAccount?.emailAuthFactor && (
<Admonition type="warning">
<Trans>
@@ -211,7 +208,6 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
</Trans>
</Admonition>
)}
<View style={[a.gap_md]}>
<View>
<Text style={[a.pb_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
@@ -220,8 +216,8 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
<TextField.Root>
<TextField.Icon icon={Envelope} />
<TextField.Input
label={_(msg`New email address`)}
placeholder={_(msg`alice@example.com`)}
label={l`New email address`}
placeholder={l`alice@example.com`}
defaultValue={state.email}
onChangeText={
state.mutationStatus === 'success'
@@ -276,7 +272,6 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
{state.error && <Admonition type="error">{state.error}</Admonition>}
</View>
{state.mutationStatus === 'success' ? (
<>
<Divider />
@@ -298,7 +293,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
</>
) : (
<Button
label={_(msg`Update email`)}
label={l`Update email`}
size="large"
variant="solid"
color="primary"
@@ -1,7 +1,5 @@
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {atoms as a, platform, tokens, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
@@ -19,7 +17,7 @@ export function VerificationReminder({
showScreen,
}: ScreenProps<ScreenID.VerificationReminder>) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const {gtPhone, gtMobile} = useBreakpoints()
const control = useDialogContext()
@@ -55,9 +53,7 @@ export function VerificationReminder({
<ShieldIcon width={64} fill="white" style={[a.z_10]} />
</View>
</View>
<View style={[a.mb_xs, {height: 150 - dialogPadding}]} />
<View style={[a.gap_sm]}>
<Text style={[a.text_xl, a.font_bold]}>
<Trans>Please verify your email</Trans>
@@ -69,12 +65,10 @@ export function VerificationReminder({
</Trans>
</Text>
</View>
<Divider />
<View style={[a.gap_sm, gtPhone && [a.flex_row_reverse]]}>
<Button
label={_(msg`Get started`)}
label={l`Get started`}
variant="solid"
color="primary"
size="large"
@@ -88,8 +82,8 @@ export function VerificationReminder({
</ButtonText>
</Button>
<Button
label={_(msg`Maybe later`)}
accessibilityHint={_(msg`Snoozes the reminder`)}
label={l`Maybe later`}
accessibilityHint={l`Snoozes the reminder`}
variant="ghost"
color="secondary"
size="large"
@@ -1,8 +1,6 @@
import {useReducer} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {wait} from '#/lib/async/wait'
import {useCleanError} from '#/lib/hooks/useCleanError'
@@ -11,6 +9,7 @@ import {useSession} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogContext} from '#/components/Dialog/context'
import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'
import {
isValidCode,
@@ -91,7 +90,8 @@ function reducer(state: State, action: Action): State {
export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const control = useDialogContext()
const cleanError = useCleanError()
const {currentAccount} = useSession()
const [state, dispatch] = useReducer(reducer, {
@@ -134,7 +134,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
const {clean} = cleanError(e)
dispatch({
type: 'setError',
error: clean || _(msg`Failed to send email, please try again.`),
error: clean || l`Failed to send email, please try again.`,
})
}
}
@@ -143,7 +143,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
if (!isValidCode(state.token)) {
dispatch({
type: 'setError',
error: _(msg`Please enter a valid code.`),
error: l`Please enter a valid code.`,
})
return
}
@@ -166,7 +166,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
const {clean} = cleanError(e)
dispatch({
type: 'setError',
error: clean || _(msg`Failed to verify email, please try again.`),
error: clean || l`Failed to verify email, please try again.`,
})
}
}
@@ -191,6 +191,15 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
</Trans>
</Text>
</View>
<Button
label={l`Done`}
size="large"
color="primary"
onPress={() => control.close()}>
<ButtonText>
<Trans>Done</Trans>
</ButtonText>
</Button>
</View>
)
}
@@ -273,7 +282,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
<Trans>
If you need to update your email,{' '}
<InlineLinkText
label={_(msg`Click here to update your email`)}
label={l`Click here to update your email`}
{...createStaticClick(() => {
showScreen({id: ScreenID.Update})
})}>
@@ -288,12 +297,11 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
<ResendEmailText onPress={requestEmailVerification} />
)}
</View>
{state.step === 'email' && state.mutationStatus !== 'success' ? (
<>
{state.error && <Admonition type="error">{state.error}</Admonition>}
<Button
label={_(msg`Send verification email`)}
label={l`Send verification email`}
size="large"
variant="solid"
color="primary"
@@ -308,7 +316,6 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
</Button>
</>
) : null}
{state.step === 'email' && (
<>
<Divider />
@@ -318,7 +325,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
<Trans>
Have a code?{' '}
<InlineLinkText
label={_(msg`Enter code`)}
label={l`Enter code`}
{...createStaticClick(() => {
dispatch({
type: 'setStep',
@@ -331,7 +338,6 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
</Text>
</>
)}
{state.step === 'token' ? (
<>
<TokenField
@@ -348,13 +354,11 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
{state.error && <Admonition type="error">{state.error}</Admonition>}
<Button
label={_(
msg({
message: `Verify code`,
context: `action`,
comment: `Button text and accessibility label for action to verify the user's email address using the code entered`,
}),
)}
label={l({
message: `Verify code`,
context: `action`,
comment: `Button text and accessibility label for action to verify the user's email address using the code entered`,
})}
size="large"
variant="solid"
color="primary"
@@ -381,7 +385,7 @@ export function Verify({config, showScreen}: ScreenProps<ScreenID.Verify>) {
<Trans>
Don't have a code or need a new one?{' '}
<InlineLinkText
label={_(msg`Click here to restart the verification process.`)}
label={l`Click here to restart the verification process.`}
{...createStaticClick(() => {
dispatch({
type: 'setStep',