Something went wrong. Try again.
Bluesky app fork with some witchin' additions 💫 forked from jollywhoppers.com/witchsky.app
Something went wrong. Try again.
11 kB · 398 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399import {useReducer} from 'react'import {View} from 'react-native'import {msg, Trans} from '@lingui/macro'import {useLingui} from '@lingui/react'
import {wait} from '#/lib/async/wait'import {useCleanError} from '#/lib/hooks/useCleanError'import {logger} from '#/logger'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 {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'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'import { ScreenID, type ScreenProps,} from '#/components/dialogs/EmailDialog/types'import {Divider} from '#/components/Divider'import {CheckThick_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '#/components/icons/Envelope'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, showScreen}: ScreenProps<ScreenID.Verify>) { const t = useTheme() const {_} = useLingui() const cleanError = useCleanError() const {currentAccount} = useSession() const [state, dispatch] = useReducer(reducer, { step: 'email', mutationStatus: 'default', error: '', token: '', })
const {mutateAsync: requestEmailVerification} = useRequestEmailVerification() const {mutateAsync: confirmEmail} = useConfirmEmail()
useOnEmailVerified(() => { if (config.onVerify) { config.onVerify() } else { dispatch({ type: 'setStep', step: 'success', }) } })
const handleRequestEmailVerification = async () => { dispatch({ type: 'setMutationStatus', status: 'pending', })
try { await wait(1000, requestEmailVerification()) dispatch({ type: 'setMutationStatus', status: 'success', }) } catch (e) { logger.error('EmailDialog: sending verification email failed', { safeMessage: e, }) const {clean} = cleanError(e) dispatch({ type: 'setError', error: clean || _(msg`Failed to send email, please try again.`), }) } }
const handleConfirmEmail = async () => { if (!isValidCode(state.token)) { dispatch({ type: 'setError', error: _(msg`Please enter a valid code.`), }) return }
dispatch({ type: 'setMutationStatus', status: 'pending', })
try { await wait(1000, confirmEmail({token: state.token})) dispatch({ type: 'setStep', step: 'success', }) } catch (e) { logger.error('EmailDialog: confirming email failed', { safeMessage: e, }) const {clean} = cleanError(e) dispatch({ type: 'setError', error: clean || _(msg`Failed to verify email, please try again.`), }) } }
if (state.step === 'success') { return ( <View style={[a.gap_lg]}> <View style={[a.gap_sm]}> <Text style={[a.text_xl, a.font_bold]}> <Span style={{top: 1}}> <Check size="sm" fill={t.palette.positive_500} /> </Span> {' '} <Trans>Email verification complete!</Trans> </Text>
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> <Trans> You have successfully verified your email address. You can close this dialog. </Trans> </Text> </View> </View> ) }
return ( <View style={[a.gap_lg]}> <View style={[a.gap_sm]}> <Text style={[a.text_xl, a.font_bold]}> {state.step === 'email' ? ( state.mutationStatus === 'success' ? ( <> <Span style={{top: 1}}> <Check size="sm" fill={t.palette.positive_500} /> </Span> {' '} <Trans>Email sent!</Trans> </> ) : ( <Trans>Verify your email</Trans> ) ) : ( <Trans comment="Dialog title when a user is verifying their email address by entering a code they have been sent"> Verify email code </Trans> )} </Text>
{state.step === 'email' && state.mutationStatus !== 'success' && ( <> {config.instructions?.map((int, i) => ( <Text key={i} style={[ a.italic, a.text_sm, a.leading_snug, t.atoms.text_contrast_medium, ]}> {int} </Text> ))} </> )}
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> {state.step === 'email' ? ( state.mutationStatus === 'success' ? ( <Trans> We sent an email to{' '} <Span style={[a.font_semi_bold, t.atoms.text]}> {currentAccount!.email} </Span>{' '} containing a link. Please click on it to complete the email verification process. </Trans> ) : ( <Trans> We'll send an email to{' '} <Span style={[a.font_semi_bold, t.atoms.text]}> {currentAccount!.email} </Span>{' '} containing a link. Please click on it to complete the email verification process. </Trans> ) ) : ( <Trans> Please enter the code we sent to{' '} <Span style={[a.font_semi_bold, t.atoms.text]}> {currentAccount!.email} </Span>{' '} below. </Trans> )} </Text>
{state.step === 'email' && state.mutationStatus !== 'success' && ( <Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> <Trans> If you need to update your email,{' '} <InlineLinkText label={_(msg`Click here to update your email`)} {...createStaticClick(() => { showScreen({id: ScreenID.Update}) })}> click here </InlineLinkText> . </Trans> </Text> )}
{state.step === 'email' && state.mutationStatus === 'success' && ( <ResendEmailText onPress={requestEmailVerification} /> )} </View>
{state.step === 'email' && state.mutationStatus !== 'success' ? ( <> {state.error && <Admonition type="error">{state.error}</Admonition>} <Button label={_(msg`Send verification email`)} size="large" variant="solid" color="primary" onPress={handleRequestEmailVerification} disabled={state.mutationStatus === 'pending'}> <ButtonText> <Trans>Send email</Trans> </ButtonText> <ButtonIcon icon={state.mutationStatus === 'pending' ? Loader : Envelope} /> </Button> </> ) : null}
{state.step === 'email' && ( <> <Divider />
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> <Trans> Have a code?{' '} <InlineLinkText label={_(msg`Enter code`)} {...createStaticClick(() => { dispatch({ type: 'setStep', step: 'token', }) })}> Click here. </InlineLinkText> </Trans> </Text> </> )}
{state.step === 'token' ? ( <> <TokenField value={state.token} onChangeText={token => { dispatch({ type: 'setToken', value: token, }) }} onSubmitEditing={handleConfirmEmail} />
{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`, }), )} size="large" variant="solid" color="primary" onPress={handleConfirmEmail} disabled={ !state.token || state.token.length !== 11 || state.mutationStatus === 'pending' }> <ButtonText> <Trans context="action" comment="Button text and accessibility label for action to verify the user's email address using the code entered"> Verify code </Trans> </ButtonText> {state.mutationStatus === 'pending' && <ButtonIcon icon={Loader} />} </Button>
<Divider />
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}> <Trans> Don't have a code or need a new one?{' '} <InlineLinkText label={_(msg`Click here to restart the verification process.`)} {...createStaticClick(() => { dispatch({ type: 'setStep', step: 'email', }) })}> Click here. </InlineLinkText> </Trans> </Text> </> ) : null} </View> )}