Share verify flow logic
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
import {useCallback, useEffect, useState} from 'react'
|
import {useCallback, useEffect} from 'react'
|
||||||
import {ScrollView, View} from 'react-native'
|
import {ScrollView, View} from 'react-native'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import type * as AgeRange from 'expo-age-range'
|
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
||||||
import {useIsBirthdateUpdateAllowed} from '#/state/birthdate'
|
import {useIsBirthdateUpdateAllowed} from '#/state/birthdate'
|
||||||
import {useSession, useSessionApi} from '#/state/session'
|
import {useSessionApi} from '#/state/session'
|
||||||
import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
|
import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
|
||||||
import {DeleteAccountDialog} from '#/screens/Settings/components/DeleteAccountDialog'
|
import {DeleteAccountDialog} from '#/screens/Settings/components/DeleteAccountDialog'
|
||||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
||||||
@@ -30,18 +29,12 @@ import * as Toast from '#/components/Toast'
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {BottomSheetOutlet} from '#/../modules/bottom-sheet'
|
import {BottomSheetOutlet} from '#/../modules/bottom-sheet'
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
import {
|
import {useAgeAssuranceServerDataContext} from '#/ageAssurance/data'
|
||||||
getDeviceSignals,
|
|
||||||
setDeviceSignalsForRegion,
|
|
||||||
useAgeAssuranceServerDataContext,
|
|
||||||
} from '#/ageAssurance/data'
|
|
||||||
import {logger} from '#/ageAssurance/logger'
|
|
||||||
import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state'
|
|
||||||
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
||||||
|
import {useAgeAssuranceVerificationFlow} from '#/ageAssurance/useVerificationFlow'
|
||||||
import {
|
import {
|
||||||
canBirthdateUpdateIncreaseAccess,
|
canBirthdateUpdateIncreaseAccess,
|
||||||
createGeolocationString,
|
createGeolocationString,
|
||||||
getAgeAssuranceDataFromDeviceSignals,
|
|
||||||
isLegacyBirthdateBug,
|
isLegacyBirthdateBug,
|
||||||
useAgeAssuranceRegionConfig,
|
useAgeAssuranceRegionConfig,
|
||||||
} from '#/ageAssurance/util'
|
} from '#/ageAssurance/util'
|
||||||
@@ -380,8 +373,6 @@ function AccessSection() {
|
|||||||
const control = useDialogControl()
|
const control = useDialogControl()
|
||||||
const appealControl = Dialog.useDialogControl()
|
const appealControl = Dialog.useDialogControl()
|
||||||
const getTimeAgo = useGetTimeAgo()
|
const getTimeAgo = useGetTimeAgo()
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const region = useAgeAssuranceRegionConfig()
|
|
||||||
|
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
const {status, lastInitiatedAt} = aa.state
|
const {status, lastInitiatedAt} = aa.state
|
||||||
@@ -393,108 +384,13 @@ function AccessSection() {
|
|||||||
const diff = lastInitiatedAt
|
const diff = lastInitiatedAt
|
||||||
? dateDiff(lastInitiatedAt, new Date(), 'down')
|
? dateDiff(lastInitiatedAt, new Date(), 'down')
|
||||||
: null
|
: null
|
||||||
const allowsDeviceVerification = region && aa.flags.allowsDeviceVerification
|
const {
|
||||||
const verifyCta = allowsDeviceVerification
|
onPressVerify,
|
||||||
? _(msg`Share age data`)
|
|
||||||
: hasInitiated
|
|
||||||
? _(msg`Verify again`)
|
|
||||||
: _(msg`Verify now`)
|
|
||||||
|
|
||||||
const [isVerifyingDevice, setIsVerifyingDevice] = useState(false)
|
|
||||||
|
|
||||||
const openKwsDialog = useCallback(() => {
|
|
||||||
control.open()
|
|
||||||
ax.metric('ageAssurance:initDialogOpen', {
|
|
||||||
hasInitiatedPreviously: hasInitiated,
|
|
||||||
})
|
|
||||||
}, [control, ax, hasInitiated])
|
|
||||||
|
|
||||||
const onPressVerify = useCallback(async () => {
|
|
||||||
const did = currentAccount?.did
|
|
||||||
|
|
||||||
// Just for typescript, this screen won't be shown without a logged in user
|
|
||||||
if (!did) return
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In regions that permit on-device verification, try the native age API
|
|
||||||
* first. We tag the result with the current region (device assurance is
|
|
||||||
* region-bound — a TX grant only counts in TX) and, if it's sufficient,
|
|
||||||
* persist it client-side so the AA state recompute lifts the gate.
|
|
||||||
*
|
|
||||||
* Once the OS returns a response we stay on the device path and report the
|
|
||||||
* outcome via a toast (sufficient, under-age, or no usable data) rather than
|
|
||||||
* silently falling back — users can still opt into KWS via the inline link.
|
|
||||||
* We only fall through to the KWS dialog below when the device can't give us
|
|
||||||
* a response at all: `getDeviceSignals` handles its own errors and returns
|
|
||||||
* undefined (e.g. on web or failure), and there's nothing to act on without
|
|
||||||
* a session DID.
|
|
||||||
*/
|
|
||||||
if (allowsDeviceVerification) {
|
|
||||||
// Show a loading state while the OS age prompt is up.
|
|
||||||
setIsVerifyingDevice(true)
|
|
||||||
let signals: AgeRange.AgeRangeResponse | undefined
|
|
||||||
try {
|
|
||||||
signals = await getDeviceSignals()
|
|
||||||
} finally {
|
|
||||||
setIsVerifyingDevice(false)
|
|
||||||
}
|
|
||||||
if (signals) {
|
|
||||||
const {assuredAge} = getAgeAssuranceDataFromDeviceSignals(
|
|
||||||
region,
|
|
||||||
signals,
|
|
||||||
)
|
|
||||||
if (assuredAge !== undefined) {
|
|
||||||
// Persist (keyed by this region) so the AA state recomputes from the
|
|
||||||
// cache write. Recompute here too so we can react to the outcome: a
|
|
||||||
// sufficient age lifts the gate (nothing more to do), but the device
|
|
||||||
// may report an age below the region's threshold, in which case
|
|
||||||
// access stays `none` and we tell the user.
|
|
||||||
setDeviceSignalsForRegion({did, region, signals})
|
|
||||||
const {state} = unsafeGetAndComputeAgeAssurance({did})
|
|
||||||
if (state.access === aa.Access.None) {
|
|
||||||
Toast.show(
|
|
||||||
_(
|
|
||||||
msg`We're sorry, but based on the data shared by your device, you are not old enough to access Bluesky.`,
|
|
||||||
),
|
|
||||||
{type: 'info'},
|
|
||||||
)
|
|
||||||
} else if (state.access === aa.Access.Unknown) {
|
|
||||||
Toast.show(
|
|
||||||
_(
|
|
||||||
msg`Hmm, it seems we weren't able to compute your level of access. Please try again.`,
|
|
||||||
),
|
|
||||||
{type: 'warning'},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Toast.show(_(msg`Thanks! You're all set.`), {
|
|
||||||
type: 'success',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// We got a device response but it carried no usable age information.
|
|
||||||
Toast.show(
|
|
||||||
_(
|
|
||||||
msg`Hmm, it seems your device was unable to share age information with us.`,
|
|
||||||
),
|
|
||||||
{type: 'warning'},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.debug(
|
|
||||||
`onPressVerify: no device signals available (web/error/no DID), falling back to KWS`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
openKwsDialog()
|
|
||||||
}, [
|
|
||||||
region,
|
|
||||||
currentAccount?.did,
|
|
||||||
openKwsDialog,
|
openKwsDialog,
|
||||||
|
isVerifyingDevice,
|
||||||
allowsDeviceVerification,
|
allowsDeviceVerification,
|
||||||
aa,
|
verifyCta,
|
||||||
_,
|
} = useAgeAssuranceVerificationFlow({initDialogControl: control})
|
||||||
])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import {useCallback, useState} from 'react'
|
||||||
|
import type * as AgeRange from 'expo-age-range'
|
||||||
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
import {type DialogControlProps} from '#/components/Dialog'
|
||||||
|
import * as Toast from '#/components/Toast'
|
||||||
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
|
import {getDeviceSignals, setDeviceSignalsForRegion} from '#/ageAssurance/data'
|
||||||
|
import {logger} from '#/ageAssurance/logger'
|
||||||
|
import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state'
|
||||||
|
import {
|
||||||
|
getAgeAssuranceDataFromDeviceSignals,
|
||||||
|
useAgeAssuranceRegionConfig,
|
||||||
|
} from '#/ageAssurance/util'
|
||||||
|
import {useAnalytics} from '#/analytics'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared "Verify now" flow for the age assurance surfaces (the no-access screen
|
||||||
|
* and the account settings card). Handles the device-first verification path
|
||||||
|
* with a KWS fallback, keeping the two entry points in sync.
|
||||||
|
*
|
||||||
|
* The caller owns the KWS dialog (`AgeAssuranceInitDialog`) and passes its
|
||||||
|
* `initDialogControl` here so we can open it for the fallback / opt-in cases.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - `onPressVerify`: the button handler. In device-verification regions it
|
||||||
|
* prompts the OS age API, persists a sufficient result (region-bound), and
|
||||||
|
* toasts the outcome; otherwise (or on any missing/failed device response) it
|
||||||
|
* opens the KWS dialog.
|
||||||
|
* - `openKwsDialog`: opens the KWS dialog directly (for the inline "use KWS"
|
||||||
|
* link), emitting the same metric as the fallback path.
|
||||||
|
* - `isVerifyingDevice`: true while the OS age prompt is up, for a button
|
||||||
|
* loading state.
|
||||||
|
* - `allowsDeviceVerification`: whether this region + device supports the
|
||||||
|
* on-device flow, for CTA/copy branching.
|
||||||
|
* - `verifyCta`: the localized button label for the current mode.
|
||||||
|
*/
|
||||||
|
export function useAgeAssuranceVerificationFlow({
|
||||||
|
initDialogControl,
|
||||||
|
}: {
|
||||||
|
initDialogControl: DialogControlProps
|
||||||
|
}) {
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
const ax = useAnalytics()
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
const region = useAgeAssuranceRegionConfig()
|
||||||
|
const aa = useAgeAssurance()
|
||||||
|
const hasInitiated = !!aa.state.lastInitiatedAt
|
||||||
|
const allowsDeviceVerification = region && aa.flags.allowsDeviceVerification
|
||||||
|
const verifyCta = allowsDeviceVerification
|
||||||
|
? l`Share age data`
|
||||||
|
: hasInitiated
|
||||||
|
? l`Verify again`
|
||||||
|
: l`Verify now`
|
||||||
|
|
||||||
|
const [isVerifyingDevice, setIsVerifyingDevice] = useState(false)
|
||||||
|
|
||||||
|
const openKwsDialog = useCallback(() => {
|
||||||
|
initDialogControl.open()
|
||||||
|
ax.metric('ageAssurance:initDialogOpen', {
|
||||||
|
hasInitiatedPreviously: hasInitiated,
|
||||||
|
})
|
||||||
|
}, [initDialogControl, ax, hasInitiated])
|
||||||
|
|
||||||
|
const onPressVerify = useCallback(async () => {
|
||||||
|
const did = currentAccount?.did
|
||||||
|
|
||||||
|
// Just for typescript, these surfaces aren't shown without a logged in user
|
||||||
|
if (!did) return
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In regions that permit on-device verification, try the native age API
|
||||||
|
* first. We tag the result with the current region (device assurance is
|
||||||
|
* region-bound — a TX grant only counts in TX) and, if it's sufficient,
|
||||||
|
* persist it client-side so the AA state recompute lifts the gate.
|
||||||
|
*
|
||||||
|
* Once the OS returns a response we stay on the device path and report the
|
||||||
|
* outcome via a toast (sufficient, under-age, or no usable data) rather than
|
||||||
|
* silently falling back — users can still opt into KWS via the inline link.
|
||||||
|
* We only fall through to the KWS dialog below when the device can't give us
|
||||||
|
* a response at all: `getDeviceSignals` handles its own errors and returns
|
||||||
|
* undefined (e.g. on web or failure).
|
||||||
|
*/
|
||||||
|
if (allowsDeviceVerification) {
|
||||||
|
// Show a loading state while the OS age prompt is up.
|
||||||
|
setIsVerifyingDevice(true)
|
||||||
|
let signals: AgeRange.AgeRangeResponse | undefined
|
||||||
|
try {
|
||||||
|
signals = await getDeviceSignals()
|
||||||
|
} finally {
|
||||||
|
setIsVerifyingDevice(false)
|
||||||
|
}
|
||||||
|
if (signals) {
|
||||||
|
const {assuredAge} = getAgeAssuranceDataFromDeviceSignals(
|
||||||
|
region,
|
||||||
|
signals,
|
||||||
|
)
|
||||||
|
if (assuredAge !== undefined) {
|
||||||
|
// Persist (keyed by this region) so the AA state recomputes from the
|
||||||
|
// cache write. Recompute here too so we can react to the outcome: a
|
||||||
|
// sufficient age lifts the gate (nothing more to do), but the device
|
||||||
|
// may report an age below the region's threshold, in which case
|
||||||
|
// access stays `none` and we tell the user.
|
||||||
|
setDeviceSignalsForRegion({did, region, signals})
|
||||||
|
const {state} = unsafeGetAndComputeAgeAssurance({did})
|
||||||
|
if (state.access === aa.Access.None) {
|
||||||
|
Toast.show(
|
||||||
|
l`We're sorry, but based on the data shared by your device, you are not old enough to access Bluesky.`,
|
||||||
|
{type: 'info'},
|
||||||
|
)
|
||||||
|
} else if (state.access === aa.Access.Unknown) {
|
||||||
|
Toast.show(
|
||||||
|
l`Hmm, it seems we weren't able to compute your level of access. Please try again.`,
|
||||||
|
{type: 'warning'},
|
||||||
|
)
|
||||||
|
} else if (state.access === aa.Access.Safe) {
|
||||||
|
Toast.show(
|
||||||
|
l`You're all set! However, certain features may still be restricted until you're able to verify you're an adult.`,
|
||||||
|
{
|
||||||
|
type: 'success',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Toast.show(l`You're all set!`, {
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// We got a device response but it carried no usable age information.
|
||||||
|
Toast.show(
|
||||||
|
l`Hmm, it seems your device was unable to share age information with us.`,
|
||||||
|
{type: 'warning'},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.debug(
|
||||||
|
`onPressVerify: no device signals available (web/error), falling back to KWS`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
openKwsDialog()
|
||||||
|
}, [
|
||||||
|
region,
|
||||||
|
currentAccount?.did,
|
||||||
|
openKwsDialog,
|
||||||
|
allowsDeviceVerification,
|
||||||
|
aa,
|
||||||
|
l,
|
||||||
|
])
|
||||||
|
|
||||||
|
return {
|
||||||
|
onPressVerify,
|
||||||
|
openKwsDialog,
|
||||||
|
isVerifyingDevice,
|
||||||
|
allowsDeviceVerification,
|
||||||
|
verifyCta,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
import {useCallback, useState} from 'react'
|
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import type * as AgeRange from 'expo-age-range'
|
|
||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
|
||||||
import {Admonition} from '#/components/Admonition'
|
import {Admonition} from '#/components/Admonition'
|
||||||
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
|
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
|
||||||
@@ -25,15 +22,9 @@ import {Loader} from '#/components/Loader'
|
|||||||
import * as Toast from '#/components/Toast'
|
import * as Toast from '#/components/Toast'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
import {getDeviceSignals, setDeviceSignalsForRegion} from '#/ageAssurance/data'
|
|
||||||
import {logger} from '#/ageAssurance/logger'
|
|
||||||
import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state'
|
|
||||||
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
||||||
import {
|
import {useAgeAssuranceVerificationFlow} from '#/ageAssurance/useVerificationFlow'
|
||||||
createGeolocationString,
|
import {createGeolocationString} from '#/ageAssurance/util'
|
||||||
getAgeAssuranceDataFromDeviceSignals,
|
|
||||||
useAgeAssuranceRegionConfig,
|
|
||||||
} from '#/ageAssurance/util'
|
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {IS_NATIVE} from '#/env'
|
import {IS_NATIVE} from '#/env'
|
||||||
import {useDeviceGeolocationApi, useGeolocation} from '#/geolocation'
|
import {useDeviceGeolocationApi, useGeolocation} from '#/geolocation'
|
||||||
@@ -59,8 +50,6 @@ function Inner({style}: ViewStyleProp & {}) {
|
|||||||
const appealControl = Dialog.useDialogControl()
|
const appealControl = Dialog.useDialogControl()
|
||||||
const getTimeAgo = useGetTimeAgo()
|
const getTimeAgo = useGetTimeAgo()
|
||||||
const {gtPhone} = useBreakpoints()
|
const {gtPhone} = useBreakpoints()
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const region = useAgeAssuranceRegionConfig()
|
|
||||||
|
|
||||||
const copy = useAgeAssuranceCopy()
|
const copy = useAgeAssuranceCopy()
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
@@ -74,101 +63,13 @@ function Inner({style}: ViewStyleProp & {}) {
|
|||||||
const diff = lastInitiatedAt
|
const diff = lastInitiatedAt
|
||||||
? dateDiff(lastInitiatedAt, new Date(), 'down')
|
? dateDiff(lastInitiatedAt, new Date(), 'down')
|
||||||
: null
|
: null
|
||||||
const allowsDeviceVerification = region && aa.flags.allowsDeviceVerification
|
const {
|
||||||
const verifyCta = allowsDeviceVerification
|
onPressVerify,
|
||||||
? l`Share age data`
|
|
||||||
: hasInitiated
|
|
||||||
? l`Verify again`
|
|
||||||
: l`Verify now`
|
|
||||||
|
|
||||||
const [isVerifyingDevice, setIsVerifyingDevice] = useState(false)
|
|
||||||
|
|
||||||
const openKwsDialog = useCallback(() => {
|
|
||||||
control.open()
|
|
||||||
ax.metric('ageAssurance:initDialogOpen', {
|
|
||||||
hasInitiatedPreviously: hasInitiated,
|
|
||||||
})
|
|
||||||
}, [control, ax, hasInitiated])
|
|
||||||
|
|
||||||
const onPressVerify = useCallback(async () => {
|
|
||||||
const did = currentAccount?.did
|
|
||||||
|
|
||||||
// Just for typescript, this card won't be shown without a logged in user
|
|
||||||
if (!did) return
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In regions that permit on-device verification, try the native age API
|
|
||||||
* first. We tag the result with the current region (device assurance is
|
|
||||||
* region-bound — a TX grant only counts in TX) and, if it's sufficient,
|
|
||||||
* persist it client-side so the AA state recompute lifts the gate.
|
|
||||||
*
|
|
||||||
* Once the OS returns a response we stay on the device path and report the
|
|
||||||
* outcome via a toast (sufficient, under-age, or no usable data) rather than
|
|
||||||
* silently falling back — users can still opt into KWS via the inline link.
|
|
||||||
* We only fall through to the KWS dialog below when the device can't give us
|
|
||||||
* a response at all: `getDeviceSignals` handles its own errors and returns
|
|
||||||
* undefined (e.g. on web or failure).
|
|
||||||
*/
|
|
||||||
if (allowsDeviceVerification) {
|
|
||||||
// Show a loading state while the OS age prompt is up.
|
|
||||||
setIsVerifyingDevice(true)
|
|
||||||
let signals: AgeRange.AgeRangeResponse | undefined
|
|
||||||
try {
|
|
||||||
signals = await getDeviceSignals()
|
|
||||||
} finally {
|
|
||||||
setIsVerifyingDevice(false)
|
|
||||||
}
|
|
||||||
if (signals) {
|
|
||||||
const {assuredAge} = getAgeAssuranceDataFromDeviceSignals(
|
|
||||||
region,
|
|
||||||
signals,
|
|
||||||
)
|
|
||||||
if (assuredAge !== undefined) {
|
|
||||||
// Persist (keyed by this region) so the AA state recomputes from the
|
|
||||||
// cache write. Recompute here too so we can react to the outcome: a
|
|
||||||
// sufficient age lifts the gate (nothing more to do), but the device
|
|
||||||
// may report an age below the region's threshold, in which case
|
|
||||||
// access stays `none` and we tell the user.
|
|
||||||
setDeviceSignalsForRegion({did, region, signals})
|
|
||||||
const {state} = unsafeGetAndComputeAgeAssurance({did})
|
|
||||||
if (state.access === aa.Access.None) {
|
|
||||||
Toast.show(
|
|
||||||
l`We're sorry, but based on the data shared by your device, you are not old enough to access Bluesky.`,
|
|
||||||
{type: 'info'},
|
|
||||||
)
|
|
||||||
} else if (state.access === aa.Access.Unknown) {
|
|
||||||
Toast.show(
|
|
||||||
l`Hmm, it seems we weren't able to compute your level of access. Please try again.`,
|
|
||||||
{type: 'warning'},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Toast.show(l`Thanks! You're all set.`, {
|
|
||||||
type: 'success',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// We got a device response but it carried no usable age information.
|
|
||||||
Toast.show(
|
|
||||||
l`Hmm, it seems your device was unable to share age information with us.`,
|
|
||||||
{type: 'warning'},
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.debug(
|
|
||||||
`onPressVerify: no device signals available (web/error), falling back to KWS`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
openKwsDialog()
|
|
||||||
}, [
|
|
||||||
region,
|
|
||||||
currentAccount?.did,
|
|
||||||
openKwsDialog,
|
openKwsDialog,
|
||||||
|
isVerifyingDevice,
|
||||||
allowsDeviceVerification,
|
allowsDeviceVerification,
|
||||||
aa,
|
verifyCta,
|
||||||
l,
|
} = useAgeAssuranceVerificationFlow({initDialogControl: control})
|
||||||
])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user