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