Do better
This commit is contained in:
+32
-7
@@ -3,19 +3,40 @@ import {useQuery, useQueryClient} from '@tanstack/react-query'
|
|||||||
|
|
||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
|
|
||||||
export const isEmailVerifiedQueryKey = ['isEmailVerified'] as const
|
export type AccountEmailState = {
|
||||||
|
isEmailVerified: boolean
|
||||||
|
email2FAEnabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export function useInvalidateIsEmailVerified() {
|
export const accountEmailStateQueryKey = ['isEmailVerified'] as const
|
||||||
|
|
||||||
|
export function useInvalidateAccountEmailState() {
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
|
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
return qc.invalidateQueries({
|
return qc.invalidateQueries({
|
||||||
queryKey: isEmailVerifiedQueryKey,
|
queryKey: accountEmailStateQueryKey,
|
||||||
})
|
})
|
||||||
}, [qc])
|
}, [qc])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useIsEmailVerified({
|
export function useUpdateAccountEmailStateQueryCache() {
|
||||||
|
const qc = useQueryClient()
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
(data: AccountEmailState) => {
|
||||||
|
return qc.setQueriesData(
|
||||||
|
{
|
||||||
|
queryKey: accountEmailStateQueryKey,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[qc],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAccountEmailState({
|
||||||
onVerify,
|
onVerify,
|
||||||
}: {
|
}: {
|
||||||
onVerify?: () => void
|
onVerify?: () => void
|
||||||
@@ -24,16 +45,20 @@ export function useIsEmailVerified({
|
|||||||
const [prevIsEmailVerified, setPrevEmailIsVerified] = useState(
|
const [prevIsEmailVerified, setPrevEmailIsVerified] = useState(
|
||||||
!!agent.session?.emailConfirmed,
|
!!agent.session?.emailConfirmed,
|
||||||
)
|
)
|
||||||
const query = useQuery({
|
const query = useQuery<AccountEmailState>({
|
||||||
enabled: !!agent.session,
|
enabled: !!agent.session,
|
||||||
initialData: {isEmailVerified: !!agent.session?.emailConfirmed},
|
initialData: {
|
||||||
|
isEmailVerified: !!agent.session?.emailConfirmed,
|
||||||
|
email2FAEnabled: !!agent.session?.emailAuthFactor,
|
||||||
|
},
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
queryKey: isEmailVerifiedQueryKey,
|
queryKey: accountEmailStateQueryKey,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
// will also trigger updates to `#/state/session` data
|
// will also trigger updates to `#/state/session` data
|
||||||
const {data} = await agent.resumeSession(agent.session!)
|
const {data} = await agent.resumeSession(agent.session!)
|
||||||
return {
|
return {
|
||||||
isEmailVerified: !!data.emailConfirmed,
|
isEmailVerified: !!data.emailConfirmed,
|
||||||
|
email2FAEnabled: !!data.emailAuthFactor,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import {useMutation} from '@tanstack/react-query'
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
import {useInvalidateIsEmailVerified} from '#/components/dialogs/EmailDialog/data/useIsEmailVerified'
|
import {useUpdateAccountEmailStateQueryCache} from '#/components/dialogs/EmailDialog/data/useAccountEmailState'
|
||||||
|
|
||||||
export function useConfirmEmail() {
|
export function useConfirmEmail() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const invalidateIsEmailVerified = useInvalidateIsEmailVerified()
|
const updateAccountEmailStateQueryCache =
|
||||||
|
useUpdateAccountEmailStateQueryCache()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({token}: {token: string}) => {
|
mutationFn: async ({token}: {token: string}) => {
|
||||||
@@ -18,8 +19,11 @@ export function useConfirmEmail() {
|
|||||||
email: currentAccount.email,
|
email: currentAccount.email,
|
||||||
token: token.trim(),
|
token: token.trim(),
|
||||||
})
|
})
|
||||||
await agent.resumeSession(agent.session!)
|
const {data} = await agent.resumeSession(agent.session!)
|
||||||
await invalidateIsEmailVerified()
|
updateAccountEmailStateQueryCache({
|
||||||
|
isEmailVerified: !!data.emailConfirmed,
|
||||||
|
email2FAEnabled: !!data.emailAuthFactor,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import {useMutation} from '@tanstack/react-query'
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
|
import {useUpdateAccountEmailStateQueryCache} from '#/components/dialogs/EmailDialog/data/useAccountEmailState'
|
||||||
|
|
||||||
export function useManageEmail2FA() {
|
export function useManageEmail2FA() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
const updateAccountEmailStateQueryCache =
|
||||||
|
useUpdateAccountEmailStateQueryCache()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
@@ -22,7 +25,11 @@ export function useManageEmail2FA() {
|
|||||||
emailAuthFactor: enabled,
|
emailAuthFactor: enabled,
|
||||||
token,
|
token,
|
||||||
})
|
})
|
||||||
await agent.resumeSession(agent.session!)
|
const {data} = await agent.resumeSession(agent.session!)
|
||||||
|
updateAccountEmailStateQueryCache({
|
||||||
|
isEmailVerified: !!data.emailConfirmed,
|
||||||
|
email2FAEnabled: !!data.emailAuthFactor,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
type StatefulControl,
|
type StatefulControl,
|
||||||
useStatefulDialogControl,
|
useStatefulDialogControl,
|
||||||
} from '#/components/dialogs/Context'
|
} from '#/components/dialogs/Context'
|
||||||
import {useIsEmailVerified} from '#/components/dialogs/EmailDialog/data/useIsEmailVerified'
|
import {useAccountEmailState} from '#/components/dialogs/EmailDialog/data/useAccountEmailState'
|
||||||
import {Manage2FA} from '#/components/dialogs/EmailDialog/screens/Manage2FA'
|
import {Manage2FA} from '#/components/dialogs/EmailDialog/screens/Manage2FA'
|
||||||
import {Update} from '#/components/dialogs/EmailDialog/screens/Update'
|
import {Update} from '#/components/dialogs/EmailDialog/screens/Update'
|
||||||
import {VerificationReminder} from '#/components/dialogs/EmailDialog/screens/VerificationReminder'
|
import {VerificationReminder} from '#/components/dialogs/EmailDialog/screens/VerificationReminder'
|
||||||
@@ -24,7 +24,7 @@ export function useEmailDialogControl() {
|
|||||||
|
|
||||||
export function EmailDialog({control}: {control: StatefulControl<Screen>}) {
|
export function EmailDialog({control}: {control: StatefulControl<Screen>}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isEmailVerified} = useIsEmailVerified()
|
const {isEmailVerified} = useAccountEmailState()
|
||||||
const onClose = useCallback(() => {
|
const onClose = useCallback(() => {
|
||||||
if (!isEmailVerified) {
|
if (!isEmailVerified) {
|
||||||
if (control.value?.id === ScreenID.Verify) {
|
if (control.value?.id === ScreenID.Verify) {
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ export function Disable() {
|
|||||||
{state.tokenStatus === 'pending' ? (
|
{state.tokenStatus === 'pending' ? (
|
||||||
<ButtonIcon icon={Loader} />
|
<ButtonIcon icon={Loader} />
|
||||||
) : state.tokenStatus === 'success' ? (
|
) : state.tokenStatus === 'success' ? (
|
||||||
<ButtonIcon icon={Loader} />
|
<ButtonIcon icon={Check} />
|
||||||
) : null}
|
) : null}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/macro'
|
||||||
|
|
||||||
import {useSession} from '#/state/session'
|
import {useAccountEmailState} from '#/components/dialogs/EmailDialog/data/useAccountEmailState'
|
||||||
import {useIsEmailVerified} from '#/components/dialogs/EmailDialog/data/useIsEmailVerified'
|
|
||||||
import {Disable} from '#/components/dialogs/EmailDialog/screens/Manage2FA/Disable'
|
import {Disable} from '#/components/dialogs/EmailDialog/screens/Manage2FA/Disable'
|
||||||
import {Enable} from '#/components/dialogs/EmailDialog/screens/Manage2FA/Enable'
|
import {Enable} from '#/components/dialogs/EmailDialog/screens/Manage2FA/Enable'
|
||||||
import {
|
import {
|
||||||
@@ -11,12 +10,10 @@ import {
|
|||||||
} from '#/components/dialogs/EmailDialog/types'
|
} from '#/components/dialogs/EmailDialog/types'
|
||||||
|
|
||||||
export function Manage2FA({showScreen}: ScreenProps<ScreenID.Manage2FA>) {
|
export function Manage2FA({showScreen}: ScreenProps<ScreenID.Manage2FA>) {
|
||||||
|
const {isEmailVerified, email2FAEnabled} = useAccountEmailState()
|
||||||
const [requestedAction, setRequestedAction] = useState<
|
const [requestedAction, setRequestedAction] = useState<
|
||||||
'enable' | 'disable' | null
|
'enable' | 'disable' | null
|
||||||
>(null)
|
>(null)
|
||||||
// TODO confirm this is in sync
|
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const {isEmailVerified} = useIsEmailVerified()
|
|
||||||
|
|
||||||
if (!isEmailVerified) {
|
if (!isEmailVerified) {
|
||||||
showScreen({
|
showScreen({
|
||||||
@@ -32,24 +29,41 @@ export function Manage2FA({showScreen}: ScreenProps<ScreenID.Manage2FA>) {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentAccount?.emailAuthFactor && requestedAction !== 'enable') {
|
/*
|
||||||
|
* Wacky state handling so that once 2FA settings change, we don't show the
|
||||||
|
* wrong step of this form - esb
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (email2FAEnabled) {
|
||||||
if (!requestedAction) {
|
if (!requestedAction) {
|
||||||
setRequestedAction('disable')
|
setRequestedAction('disable')
|
||||||
|
return <Disable />
|
||||||
}
|
}
|
||||||
return <Disable />
|
|
||||||
} else if (
|
if (requestedAction === 'disable') {
|
||||||
!currentAccount?.emailAuthFactor &&
|
return <Disable />
|
||||||
requestedAction !== 'disable'
|
}
|
||||||
) {
|
if (requestedAction === 'enable') {
|
||||||
|
return <Enable />
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (!requestedAction) {
|
if (!requestedAction) {
|
||||||
setRequestedAction('enable')
|
setRequestedAction('enable')
|
||||||
|
return <Enable />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Enable />
|
if (requestedAction === 'disable') {
|
||||||
|
return <Disable />
|
||||||
|
}
|
||||||
|
if (requestedAction === 'enable') {
|
||||||
|
return <Enable />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should never happen
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ 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 {useIsEmailVerified} from '#/components/dialogs/EmailDialog/data/useIsEmailVerified'
|
|
||||||
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
|
import {useRequestEmailVerification} from '#/components/dialogs/EmailDialog/data/useRequestEmailVerification'
|
||||||
import {
|
import {
|
||||||
type ScreenID,
|
type ScreenID,
|
||||||
@@ -108,7 +108,7 @@ export function Verify({config}: ScreenProps<ScreenID.Verify>) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [config, dispatch])
|
}, [config, dispatch])
|
||||||
useIsEmailVerified({onVerify})
|
useAccountEmailState({onVerify})
|
||||||
|
|
||||||
const handleRequestEmailVerification = async () => {
|
const handleRequestEmailVerification = async () => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
Reference in New Issue
Block a user