Pipe through deviceSignals for logical handling

This commit is contained in:
Eric Bailey
2026-07-02 11:07:18 -05:00
parent c3182f9ef7
commit 0456562abf
7 changed files with 44 additions and 40 deletions
+11 -12
View File
@@ -384,13 +384,8 @@ function AccessSection() {
const diff = lastInitiatedAt const diff = lastInitiatedAt
? dateDiff(lastInitiatedAt, new Date(), 'down') ? dateDiff(lastInitiatedAt, new Date(), 'down')
: null : null
const { const {onPressVerify, openInitDialog, isVerifying, verifyCta} =
onPressVerify, useAgeAssuranceVerificationFlow({initDialogControl: control})
openKwsDialog,
isVerifyingDevice,
allowsDeviceVerification,
verifyCta,
} = useAgeAssuranceVerificationFlow({initDialogControl: control})
return ( return (
<> <>
@@ -420,14 +415,18 @@ function AccessSection() {
<Button <Button
label={verifyCta} label={verifyCta}
size="large" size="large"
color={hasInitiated ? 'secondary' : 'primary'} color={
disabled={isVerifyingDevice} hasInitiated || aa.flags.hasSharedDeviceSignals
? 'secondary'
: 'primary'
}
disabled={isVerifying}
onPress={() => void onPressVerify()}> onPress={() => void onPressVerify()}>
<ButtonIcon icon={isVerifyingDevice ? Loader : ShieldIcon} /> <ButtonIcon icon={isVerifying ? Loader : ShieldIcon} />
<ButtonText>{verifyCta}</ButtonText> <ButtonText>{verifyCta}</ButtonText>
</Button> </Button>
{allowsDeviceVerification ? ( {aa.flags.allowsDeviceVerification ? (
<Text <Text
style={[a.text_sm, a.italic, t.atoms.text_contrast_medium]}> style={[a.text_sm, a.italic, t.atoms.text_contrast_medium]}>
<Trans> <Trans>
@@ -437,7 +436,7 @@ function AccessSection() {
<SimpleInlineLinkText <SimpleInlineLinkText
label={_(msg`Verify now using KWS`)} label={_(msg`Verify now using KWS`)}
{...createStaticClick(() => { {...createStaticClick(() => {
openKwsDialog() openInitDialog()
})}> })}>
you can use our trusted partner, KWS you can use our trusted partner, KWS
</SimpleInlineLinkText> </SimpleInlineLinkText>
+6 -3
View File
@@ -56,6 +56,7 @@ const AgeAssuranceStateContext = createContext<{
isOverRegionMinAccessAge: false, isOverRegionMinAccessAge: false,
isOverAppMinAccessAge: false, isOverAppMinAccessAge: false,
allowsDeviceVerification: false, allowsDeviceVerification: false,
hasSharedDeviceSignals: false,
}, },
}) })
@@ -81,7 +82,7 @@ export function Provider({children}: {children: React.ReactNode}) {
function InnerProvider({children}: {children: React.ReactNode}) { function InnerProvider({children}: {children: React.ReactNode}) {
const agent = useAgent() const agent = useAgent()
const state = useAgeAssuranceState() const state = useAgeAssuranceState()
const {metadata} = useAgeAssuranceServerDataContext() const {metadata, deviceSignals} = useAgeAssuranceServerDataContext()
const regionConfig = useAgeAssuranceRegionConfigWithFallback() const regionConfig = useAgeAssuranceRegionConfigWithFallback()
const getAndRegisterPushToken = useGetAndRegisterPushToken() const getAndRegisterPushToken = useGetAndRegisterPushToken()
@@ -91,6 +92,7 @@ function InnerProvider({children}: {children: React.ReactNode}) {
state: s, state: s,
regionConfig, regionConfig,
metadata, metadata,
deviceSignals,
}) })
if (flags.isAgeRestricted) { if (flags.isAgeRestricted) {
void getAndRegisterPushToken({ void getAndRegisterPushToken({
@@ -105,7 +107,7 @@ function InnerProvider({children}: {children: React.ReactNode}) {
}) })
} }
}, },
[agent, getAndRegisterPushToken, regionConfig, metadata], [agent, getAndRegisterPushToken, regionConfig, metadata, deviceSignals],
) )
useOnAgeAssuranceAccessUpdate(handleAccessUpdate) useOnAgeAssuranceAccessUpdate(handleAccessUpdate)
@@ -120,11 +122,12 @@ function InnerProvider({children}: {children: React.ReactNode}) {
state, state,
regionConfig, regionConfig,
metadata, metadata,
deviceSignals,
}), }),
} }
logger.debug(`useAgeAssurance`, res) logger.debug(`useAgeAssurance`, res)
return res return res
}, [state, metadata, regionConfig])}> }, [state, metadata, regionConfig, deviceSignals])}>
{children} {children}
</AgeAssuranceStateContext.Provider> </AgeAssuranceStateContext.Provider>
) )
+1
View File
@@ -188,6 +188,7 @@ export function unsafeGetAndComputeAgeAssurance({did}: {did: string}) {
state: computed, state: computed,
regionConfig: region, regionConfig: region,
metadata, metadata,
deviceSignals,
}), }),
} }
} }
+1
View File
@@ -56,6 +56,7 @@ export type AgeAssuranceFlags = {
isOverRegionMinAccessAge: boolean isOverRegionMinAccessAge: boolean
isOverAppMinAccessAge: boolean isOverAppMinAccessAge: boolean
allowsDeviceVerification: boolean allowsDeviceVerification: boolean
hasSharedDeviceSignals: boolean
} }
export function parseStatusFromString(raw: string) { export function parseStatusFromString(raw: string) {
+10 -13
View File
@@ -28,12 +28,10 @@ import {useAnalytics} from '#/analytics'
* prompts the OS age API, persists a sufficient result (region-bound), and * prompts the OS age API, persists a sufficient result (region-bound), and
* toasts the outcome; otherwise (or on any missing/failed device response) it * toasts the outcome; otherwise (or on any missing/failed device response) it
* opens the KWS dialog. * opens the KWS dialog.
* - `openKwsDialog`: opens the KWS dialog directly (for the inline "use KWS" * - `openInitDialog`: opens the KWS dialog directly (for the inline "use KWS"
* link), emitting the same metric as the fallback path. * link), emitting the same metric as the fallback path.
* - `isVerifyingDevice`: true while the OS age prompt is up, for a button * - `isVerifying`: true while the OS age prompt is up, for a button
* loading state. * 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. * - `verifyCta`: the localized button label for the current mode.
*/ */
export function useAgeAssuranceVerificationFlow({ export function useAgeAssuranceVerificationFlow({
@@ -54,9 +52,9 @@ export function useAgeAssuranceVerificationFlow({
? l`Verify again` ? l`Verify again`
: l`Verify now` : l`Verify now`
const [isVerifyingDevice, setIsVerifyingDevice] = useState(false) const [isVerifying, setIsVerifying] = useState(false)
const openKwsDialog = useCallback(() => { const openInitDialog = useCallback(() => {
initDialogControl.open() initDialogControl.open()
ax.metric('ageAssurance:initDialogOpen', { ax.metric('ageAssurance:initDialogOpen', {
hasInitiatedPreviously: hasInitiated, hasInitiatedPreviously: hasInitiated,
@@ -84,12 +82,12 @@ export function useAgeAssuranceVerificationFlow({
*/ */
if (allowsDeviceVerification) { if (allowsDeviceVerification) {
// Show a loading state while the OS age prompt is up. // Show a loading state while the OS age prompt is up.
setIsVerifyingDevice(true) setIsVerifying(true)
let signals: AgeRange.AgeRangeResponse | undefined let signals: AgeRange.AgeRangeResponse | undefined
try { try {
signals = await getDeviceSignals() signals = await getDeviceSignals()
} finally { } finally {
setIsVerifyingDevice(false) setIsVerifying(false)
} }
if (signals) { if (signals) {
const {assuredAge} = getAgeAssuranceDataFromDeviceSignals( const {assuredAge} = getAgeAssuranceDataFromDeviceSignals(
@@ -140,11 +138,11 @@ export function useAgeAssuranceVerificationFlow({
) )
} }
openKwsDialog() openInitDialog()
}, [ }, [
region, region,
currentAccount?.did, currentAccount?.did,
openKwsDialog, openInitDialog,
allowsDeviceVerification, allowsDeviceVerification,
aa, aa,
l, l,
@@ -152,9 +150,8 @@ export function useAgeAssuranceVerificationFlow({
return { return {
onPressVerify, onPressVerify,
openKwsDialog, openInitDialog,
isVerifyingDevice, isVerifying,
allowsDeviceVerification,
verifyCta, verifyCta,
} }
} }
+4
View File
@@ -266,10 +266,12 @@ export function computeAgeAssuranceFlags({
state, state,
regionConfig, regionConfig,
metadata, metadata,
deviceSignals,
}: { }: {
state: AgeAssuranceState state: AgeAssuranceState
regionConfig: AppBskyAgeassuranceDefs.ConfigRegion regionConfig: AppBskyAgeassuranceDefs.ConfigRegion
metadata?: AgeAssuranceMetadata metadata?: AgeAssuranceMetadata
deviceSignals?: AgeRange.AgeRangeResponse
}): AgeAssuranceFlags { }): AgeAssuranceFlags {
const isAgeRestricted = state.access !== AgeAssuranceAccess.Full const isAgeRestricted = state.access !== AgeAssuranceAccess.Full
const chatDisabled = isAgeRestricted const chatDisabled = isAgeRestricted
@@ -287,6 +289,7 @@ export function computeAgeAssuranceFlags({
state.access !== AgeAssuranceAccess.Full || isDeclaredUnderAdultAge state.access !== AgeAssuranceAccess.Full || isDeclaredUnderAdultAge
const allowsDeviceVerification = const allowsDeviceVerification =
DEVICE_SIGNALS_SUPPORTED && regionAllowsDeviceVerification(regionConfig) DEVICE_SIGNALS_SUPPORTED && regionAllowsDeviceVerification(regionConfig)
const hasSharedDeviceSignals = deviceSignals?.lowerBound !== undefined
return { return {
isAgeRestricted, isAgeRestricted,
@@ -298,6 +301,7 @@ export function computeAgeAssuranceFlags({
isOverRegionMinAccessAge, isOverRegionMinAccessAge,
isOverAppMinAccessAge, isOverAppMinAccessAge,
allowsDeviceVerification, allowsDeviceVerification,
hasSharedDeviceSignals,
} }
} }
@@ -63,13 +63,8 @@ function Inner({style}: ViewStyleProp & {}) {
const diff = lastInitiatedAt const diff = lastInitiatedAt
? dateDiff(lastInitiatedAt, new Date(), 'down') ? dateDiff(lastInitiatedAt, new Date(), 'down')
: null : null
const { const {onPressVerify, openInitDialog, isVerifying, verifyCta} =
onPressVerify, useAgeAssuranceVerificationFlow({initDialogControl: control})
openKwsDialog,
isVerifyingDevice,
allowsDeviceVerification,
verifyCta,
} = useAgeAssuranceVerificationFlow({initDialogControl: control})
return ( return (
<> <>
@@ -150,14 +145,18 @@ function Inner({style}: ViewStyleProp & {}) {
<Button <Button
label={verifyCta} label={verifyCta}
size="small" size="small"
color={hasInitiated ? 'secondary' : 'primary'} color={
disabled={isVerifyingDevice} hasInitiated || aa.flags.hasSharedDeviceSignals
? 'secondary'
: 'primary'
}
disabled={isVerifying}
onPress={() => void onPressVerify()}> onPress={() => void onPressVerify()}>
<ButtonIcon icon={isVerifyingDevice ? Loader : ShieldIcon} /> <ButtonIcon icon={isVerifying ? Loader : ShieldIcon} />
<ButtonText>{verifyCta}</ButtonText> <ButtonText>{verifyCta}</ButtonText>
</Button> </Button>
{allowsDeviceVerification ? ( {aa.flags.allowsDeviceVerification ? (
<Text <Text
style={[a.text_sm, a.italic, t.atoms.text_contrast_medium]}> style={[a.text_sm, a.italic, t.atoms.text_contrast_medium]}>
<Trans> <Trans>
@@ -167,7 +166,7 @@ function Inner({style}: ViewStyleProp & {}) {
<InlineLinkText <InlineLinkText
label={l`Verify now using KWS`} label={l`Verify now using KWS`}
{...createStaticClick(() => { {...createStaticClick(() => {
openKwsDialog() openInitDialog()
})}> })}>
you can use our trusted partner, KWS you can use our trusted partner, KWS
</InlineLinkText> </InlineLinkText>