Clean up verify state, handle normal callback
This commit is contained in:
@@ -16,9 +16,9 @@ export function useInvalidateIsEmailVerified() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useIsEmailVerified({
|
export function useIsEmailVerified({
|
||||||
onEmailVerified,
|
onVerify,
|
||||||
}: {
|
}: {
|
||||||
onEmailVerified?: () => void
|
onVerify?: () => void
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const prevIsEmailVerified = useRef(!!agent.session?.emailConfirmed)
|
const prevIsEmailVerified = useRef(!!agent.session?.emailConfirmed)
|
||||||
@@ -39,7 +39,7 @@ export function useIsEmailVerified({
|
|||||||
if (query.data.isEmailVerified && !prevIsEmailVerified.current) {
|
if (query.data.isEmailVerified && !prevIsEmailVerified.current) {
|
||||||
console.log('fire')
|
console.log('fire')
|
||||||
prevIsEmailVerified.current = true
|
prevIsEmailVerified.current = true
|
||||||
onEmailVerified?.()
|
onVerify?.()
|
||||||
} else if (prevIsEmailVerified.current && !query.data.isEmailVerified) {
|
} else if (prevIsEmailVerified.current && !query.data.isEmailVerified) {
|
||||||
console.log('reset')
|
console.log('reset')
|
||||||
prevIsEmailVerified.current = false
|
prevIsEmailVerified.current = false
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ function reducer(state: State, action: Action): State {
|
|||||||
case 'setToken': {
|
case 'setToken': {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
error: '',
|
||||||
token: action.value,
|
token: action.value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +100,6 @@ export function Update(_props: {config: Exclude<Screen, {id: 'Verify'}>}) {
|
|||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(reducer, {
|
const [state, dispatch] = useReducer(reducer, {
|
||||||
step: 'email',
|
step: 'email',
|
||||||
mutationStatus: 'default',
|
mutationStatus: 'default',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {useState} from 'react'
|
import {useCallback, useReducer} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
@@ -22,93 +22,187 @@ import {createStaticClick, InlineLinkText} from '#/components/Link'
|
|||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Span, Text} from '#/components/Typography'
|
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<Screen, {id: 'Verify'}>}) {
|
export function Verify({config}: {config: Extract<Screen, {id: 'Verify'}>}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const [error, setError] = useState('')
|
const [state, dispatch] = useReducer(reducer, {
|
||||||
|
step: 'email',
|
||||||
const [step, setStep] = useState<
|
mutationStatus: 'default',
|
||||||
'default' | 'sent' | 'token' | 'tokenConfirmed'
|
error: '',
|
||||||
>('default')
|
token: '',
|
||||||
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 {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 () => {
|
const handleRequestEmailVerification = async () => {
|
||||||
setError('')
|
dispatch({
|
||||||
setSendingStatus('sending')
|
type: 'setMutationStatus',
|
||||||
|
status: 'pending',
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await wait(1000, requestEmailVerification())
|
await wait(1000, requestEmailVerification())
|
||||||
setStep('sent')
|
dispatch({
|
||||||
|
type: 'setMutationStatus',
|
||||||
|
status: 'success',
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('EmailDialog: sending verification email failed', {
|
logger.error('EmailDialog: sending verification email failed', {
|
||||||
safeMessage: e,
|
safeMessage: e,
|
||||||
})
|
})
|
||||||
setError(_(msg`Failed to send email, please try again.`))
|
dispatch({
|
||||||
} finally {
|
type: 'setError',
|
||||||
setSendingStatus(null)
|
error: _(msg`Failed to send email, please try again.`),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleConfirmEmail = async () => {
|
const handleConfirmEmail = async () => {
|
||||||
setError('')
|
dispatch({
|
||||||
setConfirmingStatus('sending')
|
type: 'setMutationStatus',
|
||||||
|
status: 'pending',
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await wait(1000, confirmEmail({token}))
|
await wait(1000, confirmEmail({token: state.token}))
|
||||||
setStep('tokenConfirmed')
|
dispatch({
|
||||||
|
type: 'setStep',
|
||||||
|
step: 'success',
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('EmailDialog: confirming email failed', {
|
logger.error('EmailDialog: confirming email failed', {
|
||||||
safeMessage: e,
|
safeMessage: e,
|
||||||
})
|
})
|
||||||
setError(_(msg`Failed to verify email, please try again.`))
|
dispatch({
|
||||||
} finally {
|
type: 'setError',
|
||||||
setConfirmingStatus(null)
|
error: _(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_heavy]}>
|
||||||
|
<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 (
|
return (
|
||||||
<View style={[a.gap_lg]}>
|
<View style={[a.gap_lg]}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<Text style={[a.text_xl, a.font_heavy]}>
|
<Text style={[a.text_xl, a.font_heavy]}>
|
||||||
{step === 'sent' ? (
|
{state.step === 'email' ? (
|
||||||
|
state.mutationStatus === 'success' ? (
|
||||||
<>
|
<>
|
||||||
<Span style={{top: 1}}>
|
<Span style={{top: 1}}>
|
||||||
<Check size="sm" fill={t.palette.positive_500} />
|
<Check size="sm" fill={t.palette.positive_500} />
|
||||||
</Span>{' '}
|
</Span>{' '}
|
||||||
<Trans>Email sent!</Trans>
|
<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>
|
<Trans>Verify email</Trans>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Trans>Confirm email code</Trans>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{step === 'default' && (
|
{state.step === 'email' && state.mutationStatus !== 'success' && (
|
||||||
<>
|
<>
|
||||||
{config.instructions?.map((int, i) => (
|
{config.instructions?.map((int, i) => (
|
||||||
<Text
|
<Text
|
||||||
key={i}
|
key={i}
|
||||||
style={[
|
style={[
|
||||||
|
a.italic,
|
||||||
a.text_sm,
|
a.text_sm,
|
||||||
a.leading_snug,
|
a.leading_snug,
|
||||||
t.atoms.text_contrast_medium,
|
t.atoms.text_contrast_medium,
|
||||||
@@ -120,27 +214,15 @@ export function Verify({config}: {config: Extract<Screen, {id: 'Verify'}>}) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
<Text style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
{step === 'sent' ? (
|
{state.step === 'email' ? (
|
||||||
|
state.mutationStatus === 'success' ? (
|
||||||
<Trans>
|
<Trans>
|
||||||
We sent an email to{' '}
|
We sent an email to{' '}
|
||||||
<Span style={[a.font_bold, t.atoms.text]}>
|
<Span style={[a.font_bold, t.atoms.text]}>
|
||||||
{currentAccount!.email}
|
{currentAccount!.email}
|
||||||
</Span>{' '}
|
</Span>{' '}
|
||||||
containing a link. Click on it to complete the email verification
|
containing a link. Click on it to complete the email
|
||||||
process.
|
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>
|
||||||
) : (
|
) : (
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -148,18 +230,47 @@ export function Verify({config}: {config: Extract<Screen, {id: 'Verify'}>}) {
|
|||||||
<Span style={[a.font_bold, t.atoms.text]}>
|
<Span style={[a.font_bold, t.atoms.text]}>
|
||||||
{currentAccount!.email}
|
{currentAccount!.email}
|
||||||
</Span>{' '}
|
</Span>{' '}
|
||||||
containing a link. Click on it to complete the email verification
|
containing a link. Click on it to complete the email
|
||||||
process.
|
verification process.
|
||||||
|
</Trans>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Trans>
|
||||||
|
Enter the code we sent to{' '}
|
||||||
|
<Span style={[a.font_bold, t.atoms.text]}>
|
||||||
|
{currentAccount!.email}
|
||||||
|
</Span>{' '}
|
||||||
|
below.
|
||||||
</Trans>
|
</Trans>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{step === 'sent' && (
|
{state.step === 'email' && state.mutationStatus === 'success' && (
|
||||||
<ResendEmailText onPress={requestEmailVerification} />
|
<ResendEmailText onPress={requestEmailVerification} />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{step === 'sent' && (
|
{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 />
|
<Divider />
|
||||||
|
|
||||||
@@ -170,7 +281,10 @@ export function Verify({config}: {config: Extract<Screen, {id: 'Verify'}>}) {
|
|||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
label={_(msg`Enter code`)}
|
label={_(msg`Enter code`)}
|
||||||
{...createStaticClick(() => {
|
{...createStaticClick(() => {
|
||||||
setStep('token')
|
dispatch({
|
||||||
|
type: 'setStep',
|
||||||
|
step: 'token',
|
||||||
|
})
|
||||||
})}>
|
})}>
|
||||||
Click here.
|
Click here.
|
||||||
</InlineLinkText>
|
</InlineLinkText>
|
||||||
@@ -179,58 +293,20 @@ export function Verify({config}: {config: Extract<Screen, {id: 'Verify'}>}) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === 'default' ? (
|
{state.step === 'token' ? (
|
||||||
<>
|
|
||||||
{error && <Admonition type="error"> {error} </Admonition>}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
label={_(msg`Send verification email`)}
|
|
||||||
size="large"
|
|
||||||
variant="solid"
|
|
||||||
color="primary"
|
|
||||||
onPress={handleRequestEmailVerification}
|
|
||||||
disabled={sendingStatus === 'sending'}>
|
|
||||||
<ButtonText>
|
|
||||||
<Trans>Send email</Trans>
|
|
||||||
</ButtonText>
|
|
||||||
<ButtonIcon
|
|
||||||
icon={sendingStatus === 'sending' ? Loader : Envelope}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{!config.hideInitialCodeButton && (
|
|
||||||
<>
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.text_sm,
|
|
||||||
a.leading_snug,
|
|
||||||
t.atoms.text_contrast_medium,
|
|
||||||
]}>
|
|
||||||
<Trans>
|
|
||||||
Have a code?{' '}
|
|
||||||
<InlineLinkText
|
|
||||||
label={_(msg`Enter code`)}
|
|
||||||
{...createStaticClick(() => {
|
|
||||||
setStep('token')
|
|
||||||
})}>
|
|
||||||
Click here.
|
|
||||||
</InlineLinkText>
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : step === 'token' ? (
|
|
||||||
<>
|
<>
|
||||||
<TokenField
|
<TokenField
|
||||||
value={token}
|
value={state.token}
|
||||||
onChangeText={setToken}
|
onChangeText={token => {
|
||||||
|
dispatch({
|
||||||
|
type: 'setToken',
|
||||||
|
value: token,
|
||||||
|
})
|
||||||
|
}}
|
||||||
onSubmitEditing={() => {}}
|
onSubmitEditing={() => {}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{error && <Admonition type="error"> {error} </Admonition>}
|
{state.error && <Admonition type="error">{state.error}</Admonition>}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Verify`)}
|
label={_(msg`Verify`)}
|
||||||
@@ -238,12 +314,31 @@ export function Verify({config}: {config: Extract<Screen, {id: 'Verify'}>}) {
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={handleConfirmEmail}
|
onPress={handleConfirmEmail}
|
||||||
disabled={!token || confirmingStatus === 'sending'}>
|
disabled={!state.token || state.mutationStatus === 'pending'}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Verify code</Trans>
|
<Trans>Verify code</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
{confirmingStatus === 'sending' && <ButtonIcon icon={Loader} />}
|
{state.mutationStatus === 'pending' && <ButtonIcon icon={Loader} />}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Text
|
||||||
|
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>
|
||||||
|
Don't have a code?{' '}
|
||||||
|
<InlineLinkText
|
||||||
|
label={_(msg`Enter code`)}
|
||||||
|
{...createStaticClick(() => {
|
||||||
|
dispatch({
|
||||||
|
type: 'setStep',
|
||||||
|
step: 'email',
|
||||||
|
})
|
||||||
|
})}>
|
||||||
|
Click here.
|
||||||
|
</InlineLinkText>
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user