Handle token step

This commit is contained in:
Eric Bailey
2025-04-25 15:34:56 -05:00
parent 4a7c4fd215
commit 90cb1ac41f
2 changed files with 105 additions and 9 deletions
@@ -0,0 +1,22 @@
import {useMutation} from '@tanstack/react-query'
import {useAgent, useSession} from '#/state/session'
export function useConfirmEmail() {
const agent = useAgent()
const {currentAccount} = useSession()
return useMutation({
mutationFn: async ({token}: {token: string}) => {
if (!currentAccount?.email) {
throw new Error('No email found for the current account')
}
await agent.com.atproto.server.confirmEmail({
email: currentAccount.email,
token: token.trim(),
})
await agent.resumeSession(agent.session!)
},
})
}
@@ -10,6 +10,8 @@ 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 {TokenField} from '#/components/dialogs/EmailDialog/components/TokenField'
import {useConfirmEmail} from '#/components/dialogs/EmailDialog/data/useConfirmEmail'
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
import {type Screen} from '#/components/dialogs/EmailDialog/types'
import {Divider} from '#/components/Divider'
@@ -25,17 +27,25 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
const {currentAccount} = useSession()
const [error, setError] = useState('')
const [sentEmail, setSentEmail] = useState(false)
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()
const handleRequestEmailVerification = async () => {
setError('')
setSendingStatus('sending')
try {
await wait(1000, requestEmailVerification())
setSentEmail(true)
setStep('sent')
} catch (e) {
logger.error('EmailDialog: sending verification email failed', {
safeMessage: e,
@@ -46,24 +56,48 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
}
}
const handleConfirmEmail = async () => {
setError('')
setConfirmingStatus('sending')
try {
await wait(1000, confirmEmail({token}))
setStep('tokenConfirmed')
} catch (e) {
logger.error('EmailDialog: confirming email failed', {
safeMessage: e,
})
setError(_(msg`Failed to verify email, please try again.`))
} finally {
setConfirmingStatus(null)
}
}
return (
<View style={[a.gap_lg]}>
<View style={[a.gap_sm]}>
<Text style={[a.text_xl, a.font_heavy]}>
{sentEmail ? (
{step === 'sent' ? (
<>
<Span style={{top: 1}}>
<Check size="sm" fill={t.palette.positive_500} />
</Span>{' '}
<Trans>Email sent!</Trans>
</>
) : step === 'tokenConfirmed' ? (
<>
<Span style={{top: 1}}>
<Check size="sm" fill={t.palette.positive_500} />
</Span>{' '}
<Trans>Email verification complete</Trans>
</>
) : (
<Trans>Verify email</Trans>
)}
</Text>
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
{sentEmail ? (
{step === 'sent' ? (
<Trans>
We sent an email to{' '}
<Span style={[a.font_bold, t.atoms.text]}>
@@ -72,6 +106,19 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
containing a link. Click on it to complete the email verification
process.
</Trans>
) : step === 'token' ? (
<Trans>
Enter the code we sent to{' '}
<Span style={[a.font_bold, t.atoms.text]}>
{currentAccount!.email}
</Span>{' '}
below.
</Trans>
) : step === 'tokenConfirmed' ? (
<Trans>
You have successfully verified your email address. You can close
this dialog.
</Trans>
) : (
<Trans>
We'll send you an email to{' '}
@@ -84,10 +131,12 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
)}
</Text>
{sentEmail && <ResendEmailText onPress={requestEmailVerification} />}
{step === 'sent' && (
<ResendEmailText onPress={requestEmailVerification} />
)}
</View>
{!sentEmail && (
{step === 'default' ? (
<>
{error && <Admonition type="error"> {error} </Admonition>}
@@ -95,7 +144,7 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
label={_(msg`Send verification email`)}
size="large"
variant="solid"
color={'primary'}
color="primary"
onPress={handleRequestEmailVerification}
disabled={sendingStatus === 'sending'}>
<ButtonText>
@@ -114,13 +163,38 @@ export function Verify(_props: {config: Exclude<Screen, {id: 'Update'}>}) {
Have a code?{' '}
<InlineLinkText
label={_(msg`Enter code`)}
{...createStaticClick(() => {})}>
{...createStaticClick(() => {
setStep('token')
})}>
Click here.
</InlineLinkText>
</Trans>
</Text>
</>
)}
) : step === 'token' ? (
<>
<TokenField
value={token}
onChangeText={setToken}
onSubmitEditing={() => {}}
/>
{error && <Admonition type="error"> {error} </Admonition>}
<Button
label={_(msg`Verify`)}
size="large"
variant="solid"
color="primary"
onPress={handleConfirmEmail}
disabled={!token || confirmingStatus === 'sending'}>
<ButtonText>
<Trans>Verify code</Trans>
</ButtonText>
{confirmingStatus === 'sending' && <ButtonIcon icon={Loader} />}
</Button>
</>
) : null}
</View>
)
}