Use listeners for easier to grok logic
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
import {useCallback, useState} from 'react'
|
import {useCallback, useEffect, useState} from 'react'
|
||||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
import {emitEmailVerified} from '#/components/dialogs/EmailDialog/events'
|
||||||
|
|
||||||
export type AccountEmailState = {
|
export type AccountEmailState = {
|
||||||
isEmailVerified: boolean
|
isEmailVerified: boolean
|
||||||
email2FAEnabled: boolean
|
email2FAEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const accountEmailStateQueryKey = ['isEmailVerified'] as const
|
export const accountEmailStateQueryKey = ['accountEmailState'] as const
|
||||||
|
|
||||||
export function useInvalidateAccountEmailState() {
|
export function useInvalidateAccountEmailState() {
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
@@ -36,21 +37,17 @@ export function useUpdateAccountEmailStateQueryCache() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAccountEmailState({
|
export function useAccountEmailState() {
|
||||||
onVerify,
|
|
||||||
}: {
|
|
||||||
onVerify?: () => void
|
|
||||||
} = {}) {
|
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const [prevIsEmailVerified, setPrevEmailIsVerified] = useState(
|
const [prevIsEmailVerified, setPrevEmailIsVerified] = useState(
|
||||||
!!agent.session?.emailConfirmed,
|
!!agent.session?.emailConfirmed,
|
||||||
)
|
)
|
||||||
|
const fallbackData: AccountEmailState = {
|
||||||
|
isEmailVerified: !!agent.session?.emailConfirmed,
|
||||||
|
email2FAEnabled: !!agent.session?.emailAuthFactor,
|
||||||
|
}
|
||||||
const query = useQuery<AccountEmailState>({
|
const query = useQuery<AccountEmailState>({
|
||||||
enabled: !!agent.session,
|
enabled: !!agent.session,
|
||||||
initialData: {
|
|
||||||
isEmailVerified: !!agent.session?.emailConfirmed,
|
|
||||||
email2FAEnabled: !!agent.session?.emailAuthFactor,
|
|
||||||
},
|
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
queryKey: accountEmailStateQueryKey,
|
queryKey: accountEmailStateQueryKey,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@@ -63,12 +60,20 @@ export function useAccountEmailState({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (query.data.isEmailVerified && !prevIsEmailVerified) {
|
const state = query.data ?? fallbackData
|
||||||
setPrevEmailIsVerified(true)
|
|
||||||
onVerify?.()
|
|
||||||
} else if (!query.data.isEmailVerified && prevIsEmailVerified) {
|
|
||||||
setPrevEmailIsVerified(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return query.data
|
/*
|
||||||
|
* This will emit `n` times for each instance of this hook. So the listeners
|
||||||
|
* all use `once` to prevent multiple handlers firing.
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (state.isEmailVerified && !prevIsEmailVerified) {
|
||||||
|
setPrevEmailIsVerified(true)
|
||||||
|
emitEmailVerified()
|
||||||
|
} else if (!state.isEmailVerified && prevIsEmailVerified) {
|
||||||
|
setPrevEmailIsVerified(false)
|
||||||
|
}
|
||||||
|
}, [state, prevIsEmailVerified])
|
||||||
|
|
||||||
|
return state
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import {useEffect} from 'react'
|
||||||
|
import EventEmitter from 'eventemitter3'
|
||||||
|
|
||||||
|
const events = new EventEmitter<{
|
||||||
|
emailVerified: void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
export function emitEmailVerified() {
|
||||||
|
events.emit('emailVerified')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOnEmailVerified(cb: () => void) {
|
||||||
|
useEffect(() => {
|
||||||
|
/*
|
||||||
|
* N.B. Use `once` here, since the event can fire multiple times for each
|
||||||
|
* instance of `useAccountEmailState`
|
||||||
|
*/
|
||||||
|
events.once('emailVerified', cb)
|
||||||
|
return () => {
|
||||||
|
events.off('emailVerified', cb)
|
||||||
|
}
|
||||||
|
}, [cb])
|
||||||
|
}
|
||||||
@@ -207,6 +207,7 @@ export function Update(_props: ScreenProps<ScreenID.Update>) {
|
|||||||
}
|
}
|
||||||
keyboardType="email-address"
|
keyboardType="email-address"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
|
autoCapitalize="none"
|
||||||
onSubmitEditing={handleUpdateEmail}
|
onSubmitEditing={handleUpdateEmail}
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {useCallback, useReducer} from 'react'
|
import {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'
|
||||||
@@ -11,9 +11,9 @@ import {Admonition} from '#/components/Admonition'
|
|||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'
|
import {ResendEmailText} from '#/components/dialogs/EmailDialog/components/ResendEmailText'
|
||||||
import {TokenField} from '#/components/dialogs/EmailDialog/components/TokenField'
|
import {TokenField} from '#/components/dialogs/EmailDialog/components/TokenField'
|
||||||
import {useAccountEmailState} from '#/components/dialogs/EmailDialog/data/useAccountEmailState'
|
|
||||||
import {useConfirmEmail} from '#/components/dialogs/EmailDialog/data/useConfirmEmail'
|
import {useConfirmEmail} from '#/components/dialogs/EmailDialog/data/useConfirmEmail'
|
||||||
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
|
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
|
||||||
|
import {useOnEmailVerified} from '#/components/dialogs/EmailDialog/events'
|
||||||
import {
|
import {
|
||||||
type ScreenID,
|
type ScreenID,
|
||||||
type ScreenProps,
|
type ScreenProps,
|
||||||
@@ -98,7 +98,7 @@ export function Verify({config}: ScreenProps<ScreenID.Verify>) {
|
|||||||
const {mutateAsync: requestEmailVerification} = useRequestEmailVerification()
|
const {mutateAsync: requestEmailVerification} = useRequestEmailVerification()
|
||||||
const {mutateAsync: confirmEmail} = useConfirmEmail()
|
const {mutateAsync: confirmEmail} = useConfirmEmail()
|
||||||
|
|
||||||
const onVerify = useCallback(() => {
|
useOnEmailVerified(() => {
|
||||||
if (config.onVerify) {
|
if (config.onVerify) {
|
||||||
config.onVerify()
|
config.onVerify()
|
||||||
} else {
|
} else {
|
||||||
@@ -107,8 +107,7 @@ export function Verify({config}: ScreenProps<ScreenID.Verify>) {
|
|||||||
step: 'success',
|
step: 'success',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [config, dispatch])
|
})
|
||||||
useAccountEmailState({onVerify})
|
|
||||||
|
|
||||||
const handleRequestEmailVerification = async () => {
|
const handleRequestEmailVerification = async () => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
Reference in New Issue
Block a user