import {useEffect, useRef, useState} from 'react' import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {retry} from '#/lib/async/retry' import {wait} from '#/lib/async/wait' import {isNative} from '#/platform/detection' import {useAgeAssuranceAPIContext} from '#/state/ageAssurance' import {logger} from '#/state/ageAssurance/util' import {useAgent} from '#/state/session' import {atoms as a, useTheme, web} from '#/alf' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' import {CheckThick_Stroke2_Corner0_Rounded as SuccessIcon} from '#/components/icons/Check' import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' export type AgeAssuranceRedirectDialogState = { result: 'success' | 'unknown' actorDid: string } /** * Validate and parse the query parameters returned from the age assurance * redirect. If not valid, returns `undefined` and the dialog will not open. */ export function parseAgeAssuranceRedirectDialogState( state: { result?: string actorDid?: string } = {}, ): AgeAssuranceRedirectDialogState | undefined { let result: AgeAssuranceRedirectDialogState['result'] = 'unknown' const actorDid = state.actorDid switch (state.result) { case 'success': result = 'success' break case 'unknown': default: result = 'unknown' break } if (result && actorDid) { return { result, actorDid, } } } export function useAgeAssuranceRedirectDialogControl() { return useGlobalDialogsControlContext().ageAssuranceRedirectDialogControl } export function AgeAssuranceRedirectDialog() { const {_} = useLingui() const control = useAgeAssuranceRedirectDialogControl() // TODO for testing // Dialog.useAutoOpen(control.control, 3e3) return ( control.clear()}> ) } export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) { const t = useTheme() const {_} = useLingui() const agent = useAgent() const polling = useRef(false) const unmounted = useRef(false) const control = useAgeAssuranceRedirectDialogControl() const [error, setError] = useState(false) const [success, setSuccess] = useState(false) const {refetch: refreshAgeAssuranceState} = useAgeAssuranceAPIContext() useEffect(() => { if (polling.current) return polling.current = true logger.metric('ageAssurance:redirectDialogOpen', {}) wait( 3e3, retry( 5, () => true, async () => { if (!agent.session) return if (unmounted.current) return const {data} = await agent.app.bsky.unspecced.getAgeAssuranceState() if (data.status !== 'assured') { throw new Error( `Polling for age assurance state did not receive assured status`, ) } return data }, 1e3, ), ) .then(async data => { if (!data) return if (!agent.session) return if (unmounted.current) return // success! update state await refreshAgeAssuranceState() setSuccess(true) logger.metric('ageAssurance:redirectDialogSuccess', {}) }) .catch(() => { if (unmounted.current) return setError(true) // try a refetch anyway refreshAgeAssuranceState() logger.metric('ageAssurance:redirectDialogFail', {}) }) return () => { unmounted.current = true } }, [agent, control, refreshAgeAssuranceState]) if (success) { return ( <> Success We've confirmed your age assurance status. You can now close this dialog. {isNative && ( )} ) } return ( <> {error && } {error ? Connection issue : Verifying} {!error && } {error ? ( We were unable to receive the verification due to a connection issue. It may arrive later. If it does, your account will update automatically. ) : ( We're confirming your age assurance status with our servers. This should only take a few seconds. )} {error && isNative && ( )} {error && } ) }