diff --git a/src/components/dialogs/EmailDialog/data/useIsEmailVerified.ts b/src/components/dialogs/EmailDialog/data/useIsEmailVerified.ts index 226cbc859d..5355786d3b 100644 --- a/src/components/dialogs/EmailDialog/data/useIsEmailVerified.ts +++ b/src/components/dialogs/EmailDialog/data/useIsEmailVerified.ts @@ -16,9 +16,9 @@ export function useInvalidateIsEmailVerified() { } export function useIsEmailVerified({ - onEmailVerified, + onVerify, }: { - onEmailVerified?: () => void + onVerify?: () => void } = {}) { const agent = useAgent() const prevIsEmailVerified = useRef(!!agent.session?.emailConfirmed) @@ -39,7 +39,7 @@ export function useIsEmailVerified({ if (query.data.isEmailVerified && !prevIsEmailVerified.current) { console.log('fire') prevIsEmailVerified.current = true - onEmailVerified?.() + onVerify?.() } else if (prevIsEmailVerified.current && !query.data.isEmailVerified) { console.log('reset') prevIsEmailVerified.current = false diff --git a/src/components/dialogs/EmailDialog/screens/Update.tsx b/src/components/dialogs/EmailDialog/screens/Update.tsx index d7722edb71..49ba902269 100644 --- a/src/components/dialogs/EmailDialog/screens/Update.tsx +++ b/src/components/dialogs/EmailDialog/screens/Update.tsx @@ -89,6 +89,7 @@ function reducer(state: State, action: Action): State { case 'setToken': { return { ...state, + error: '', token: action.value, } } @@ -99,7 +100,6 @@ export function Update(_props: {config: Exclude}) { const t = useTheme() const {_} = useLingui() const {currentAccount} = useSession() - const [state, dispatch] = useReducer(reducer, { step: 'email', mutationStatus: 'default', diff --git a/src/components/dialogs/EmailDialog/screens/Verify.tsx b/src/components/dialogs/EmailDialog/screens/Verify.tsx index 223d51d8b6..e95097a971 100644 --- a/src/components/dialogs/EmailDialog/screens/Verify.tsx +++ b/src/components/dialogs/EmailDialog/screens/Verify.tsx @@ -1,4 +1,4 @@ -import {useState} from 'react' +import {useCallback, useReducer} from 'react' import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -22,93 +22,187 @@ import {createStaticClick, InlineLinkText} from '#/components/Link' import {Loader} from '#/components/Loader' import {Span, Text} from '#/components/Typography' +type State = { + step: 'email' | 'token' | 'success' + mutationStatus: 'pending' | 'success' | 'error' | 'default' + error: string + token: string +} + +type Action = + | { + type: 'setStep' + step: State['step'] + } + | { + type: 'setError' + error: string + } + | { + type: 'setMutationStatus' + status: State['mutationStatus'] + } + | { + type: 'setToken' + value: string + } + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'setStep': { + return { + ...state, + error: '', + mutationStatus: 'default', + step: action.step, + } + } + case 'setError': { + return { + ...state, + error: action.error, + mutationStatus: 'error', + } + } + case 'setMutationStatus': { + return { + ...state, + error: '', + mutationStatus: action.status, + } + } + case 'setToken': { + return { + ...state, + error: '', + token: action.value, + } + } + } +} + export function Verify({config}: {config: Extract}) { const t = useTheme() const {_} = useLingui() const {currentAccount} = useSession() - const [error, setError] = useState('') - - const [step, setStep] = useState< - 'default' | 'sent' | 'token' | 'tokenConfirmed' - >('default') - const [sendingStatus, setSendingStatus] = useState<'sending' | null>(null) - const {mutateAsync: requestEmailVerification} = useRequestEmailVerification() - - const [token, setToken] = useState('') - const [confirmingStatus, setConfirmingStatus] = useState<'sending' | null>( - null, - ) - const {mutateAsync: confirmEmail} = useConfirmEmail() - - useIsEmailVerified({ - onEmailVerified: () => { - config.onVerify?.() - }, + const [state, dispatch] = useReducer(reducer, { + step: 'email', + mutationStatus: 'default', + error: '', + token: '', }) + const {mutateAsync: requestEmailVerification} = useRequestEmailVerification() + const {mutateAsync: confirmEmail} = useConfirmEmail() + + const onVerify = useCallback(() => { + if (config.onVerify) { + config.onVerify() + } else { + dispatch({ + type: 'setStep', + step: 'success', + }) + } + }, [config, dispatch]) + useIsEmailVerified({onVerify}) + const handleRequestEmailVerification = async () => { - setError('') - setSendingStatus('sending') + dispatch({ + type: 'setMutationStatus', + status: 'pending', + }) try { await wait(1000, requestEmailVerification()) - setStep('sent') + dispatch({ + type: 'setMutationStatus', + status: 'success', + }) } catch (e) { logger.error('EmailDialog: sending verification email failed', { safeMessage: e, }) - setError(_(msg`Failed to send email, please try again.`)) - } finally { - setSendingStatus(null) + dispatch({ + type: 'setError', + error: _(msg`Failed to send email, please try again.`), + }) } } const handleConfirmEmail = async () => { - setError('') - setConfirmingStatus('sending') + dispatch({ + type: 'setMutationStatus', + status: 'pending', + }) try { - await wait(1000, confirmEmail({token})) - setStep('tokenConfirmed') + await wait(1000, confirmEmail({token: state.token})) + dispatch({ + type: 'setStep', + step: 'success', + }) } catch (e) { logger.error('EmailDialog: confirming email failed', { safeMessage: e, }) - setError(_(msg`Failed to verify email, please try again.`)) - } finally { - setConfirmingStatus(null) + dispatch({ + type: 'setError', + error: _(msg`Failed to verify email, please try again.`), + }) } } + if (state.step === 'success') { + return ( + + + + + + {' '} + Email verification complete! + + + + + You have successfully verified your email address. You can close + this dialog. + + + + + ) + } + return ( - {step === 'sent' ? ( - <> - - - {' '} - Email sent! - - ) : step === 'tokenConfirmed' ? ( - <> - - - {' '} - Email verification complete - + {state.step === 'email' ? ( + state.mutationStatus === 'success' ? ( + <> + + + {' '} + Email sent! + + ) : ( + Verify email + ) ) : ( - Verify email + Confirm email code )} - {step === 'default' && ( + {state.step === 'email' && state.mutationStatus !== 'success' && ( <> {config.instructions?.map((int, i) => ( }) { )} - {step === 'sent' ? ( - - We sent an email to{' '} - - {currentAccount!.email} - {' '} - containing a link. Click on it to complete the email verification - process. - - ) : step === 'token' ? ( + {state.step === 'email' ? ( + state.mutationStatus === 'success' ? ( + + We sent an email to{' '} + + {currentAccount!.email} + {' '} + containing a link. Click on it to complete the email + verification process. + + ) : ( + + We'll send you an email to{' '} + + {currentAccount!.email} + {' '} + containing a link. Click on it to complete the email + verification process. + + ) + ) : ( Enter the code we sent to{' '} @@ -137,29 +242,35 @@ export function Verify({config}: {config: Extract}) { {' '} below. - ) : step === 'tokenConfirmed' ? ( - - You have successfully verified your email address. You can close - this dialog. - - ) : ( - - We'll send you an email to{' '} - - {currentAccount!.email} - {' '} - containing a link. Click on it to complete the email verification - process. - )} - {step === 'sent' && ( + {state.step === 'email' && state.mutationStatus === 'success' && ( )} - {step === 'sent' && ( + {state.step === 'email' && state.mutationStatus !== 'success' ? ( + <> + {state.error && {state.error}} + + + ) : null} + + {state.step === 'email' && ( <> @@ -170,7 +281,10 @@ export function Verify({config}: {config: Extract}) { { - setStep('token') + dispatch({ + type: 'setStep', + step: 'token', + }) })}> Click here. @@ -179,58 +293,20 @@ export function Verify({config}: {config: Extract}) { )} - {step === 'default' ? ( - <> - {error && {error} } - - - - {!config.hideInitialCodeButton && ( - <> - - - - - Have a code?{' '} - { - setStep('token') - })}> - Click here. - - - - - )} - - ) : step === 'token' ? ( + {state.step === 'token' ? ( <> { + dispatch({ + type: 'setToken', + value: token, + }) + }} onSubmitEditing={() => {}} /> - {error && {error} } + {state.error && {state.error}} + + + + + + Don't have a code?{' '} + { + dispatch({ + type: 'setStep', + step: 'email', + }) + })}> + Click here. + + + ) : null}