Clarify naming conventions and relationships

This commit is contained in:
Eric Bailey
2026-06-08 15:00:43 -05:00
parent bef23cc9f6
commit 03605de62f
10 changed files with 102 additions and 81 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
export const prefetchAgeAssuranceData = () => {}
export const prefetchAgeAssuranceServerData = () => {}
export const setBirthdateForDid = () => {}
export const setCreatedAtForDid = () => {}
@@ -32,7 +32,7 @@ import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {BottomSheetOutlet} from '#/../modules/bottom-sheet'
import {useAgeAssurance} from '#/ageAssurance'
import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
import {useAgeAssuranceServerDataContext} from '#/ageAssurance/data'
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
import {
isLegacyBirthdateBug,
@@ -53,7 +53,7 @@ export function NoAccessScreen() {
const birthdateControl = useDialogControl()
const deactivateAccountControl = useDialogControl()
const deleteAccountControl = useDialogControl()
const {data} = useAgeAssuranceDataContext()
const {metadata} = useAgeAssuranceServerDataContext()
const region = useAgeAssuranceRegionConfig()
const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed()
const {logoutCurrentAccount} = useSessionApi()
@@ -62,15 +62,15 @@ export function NoAccessScreen() {
const aa = useAgeAssurance()
const isBlocked = aa.state.status === aa.Status.Blocked
const isAARegion = !!region
const hasDeclaredAge = data?.declaredAge !== undefined
const hasDeclaredAge = metadata?.declaredAge !== undefined
const canUpdateBirthday =
isBirthdateUpdateAllowed || isLegacyBirthdateBug(data?.birthdate || '')
isBirthdateUpdateAllowed || isLegacyBirthdateBug(metadata?.birthdate || '')
useEffect(() => {
// just counting overall hits here
ax.metric(`blockedGeoOverlay:shown`, {})
ax.metric(`ageAssurance:noAccessScreen:shown`, {
accountCreatedAt: data?.accountCreatedAt || 'unknown',
accountCreatedAt: metadata?.accountCreatedAt || 'unknown',
isAARegion,
hasDeclaredAge,
canUpdateBirthday,
+24 -22
View File
@@ -24,6 +24,7 @@ import {fetchActorDeclarationRecord} from '#/state/queries/messages/actor-declar
import {useAgent, useSession} from '#/state/session'
import * as debug from '#/ageAssurance/debug'
import {logger} from '#/ageAssurance/logger'
import {type AgeAssuranceMetadata} from '#/ageAssurance/types'
import {
getBirthdateStringFromAge,
isLegacyBirthdateBug,
@@ -485,9 +486,9 @@ export function useOtherRequiredDataQuery() {
}
/**
* Helper to prefetch all age assurance data.
* Helper to prefetch all age assurance data from the server.
*/
export function prefetchAgeAssuranceData({agent}: {agent: AtpAgent}) {
export function prefetchAgeAssuranceServerData({agent}: {agent: AtpAgent}) {
return Promise.allSettled([
// config fetch initiated at the top of the App.platform.tsx files, awaited here
configPrefetchPromise,
@@ -496,8 +497,8 @@ export function prefetchAgeAssuranceData({agent}: {agent: AtpAgent}) {
])
}
export function clearAgeAssuranceDataForDid({did}: {did: string}) {
logger.debug(`clearAgeAssuranceDataForDid: ${did}`)
export function clearAgeAssuranceServerDataForDid({did}: {did: string}) {
logger.debug(`clearAgeAssuranceServerDataForDid: ${did}`)
qc.removeQueries({queryKey: createServerStateQueryKey({did}), exact: true})
qc.removeQueries({
queryKey: createOtherRequiredDataQueryKey({did}),
@@ -505,8 +506,8 @@ export function clearAgeAssuranceDataForDid({did}: {did: string}) {
})
}
export function clearAgeAssuranceData() {
logger.debug(`clearAgeAssuranceData`)
export function clearAgeAssuranceServerDataForAll() {
logger.debug(`clearAgeAssuranceServerDataForAll`)
qc.clear()
}
@@ -514,30 +515,30 @@ export function clearAgeAssuranceData() {
* Context
*/
export type AgeAssuranceData = {
export type AgeAssuranceServerData = {
/**
* The raw config from the appview.
*/
config: AppBskyAgeassuranceDefs.Config | undefined
/**
* The raw state from the appview. Must be further processed before being useful.
*/
state: AppBskyAgeassuranceDefs.State | undefined
data:
| {
accountCreatedAt: AppBskyAgeassuranceDefs.StateMetadata['accountCreatedAt']
declaredAge: number | undefined
birthdate: string | undefined
}
| undefined
metadata: AgeAssuranceMetadata | undefined
}
export const AgeAssuranceDataContext = createContext<AgeAssuranceData>({
const AgeAssuranceServerDataContext = createContext<AgeAssuranceServerData>({
config: undefined,
state: undefined,
data: {
metadata: {
accountCreatedAt: undefined,
declaredAge: undefined,
birthdate: undefined,
},
})
export function useAgeAssuranceDataContext() {
return useContext(AgeAssuranceDataContext)
export function useAgeAssuranceServerDataContext() {
return useContext(AgeAssuranceServerDataContext)
}
export function AgeAssuranceDataProvider({
export function AgeAssuranceServerDataProvider({
children,
}: {
children: React.ReactNode
@@ -550,7 +551,8 @@ export function AgeAssuranceDataProvider({
() => ({
config,
state,
data: {
metadata: {
// yes, it's weird, but accountCreatedAt comes back on the `getState` endpoint
accountCreatedAt: metadata?.accountCreatedAt,
declaredAge: data?.birthdate
? getAge(new Date(data.birthdate))
@@ -561,8 +563,8 @@ export function AgeAssuranceDataProvider({
[config, state, data, metadata],
)
return (
<AgeAssuranceDataContext.Provider value={ctx}>
<AgeAssuranceServerDataContext.Provider value={ctx}>
{children}
</AgeAssuranceDataContext.Provider>
</AgeAssuranceServerDataContext.Provider>
)
}
+10 -10
View File
@@ -4,8 +4,8 @@ import {useGetAndRegisterPushToken} from '#/lib/notifications/notifications'
import {useAgent} from '#/state/session'
import {Provider as RedirectOverlayProvider} from '#/ageAssurance/components/RedirectOverlay'
import {
AgeAssuranceDataProvider,
useAgeAssuranceDataContext,
AgeAssuranceServerDataProvider,
useAgeAssuranceServerDataContext,
} from '#/ageAssurance/data'
import {logger} from '#/ageAssurance/logger'
import {
@@ -26,7 +26,7 @@ import {
export {
prefetchConfig as prefetchAgeAssuranceConfig,
prefetchAgeAssuranceData,
prefetchAgeAssuranceServerData,
refetchServerState as refetchAgeAssuranceServerState,
usePatchOtherRequiredData as usePatchAgeAssuranceOtherRequiredData,
usePatchServerState as usePatchAgeAssuranceServerState,
@@ -67,19 +67,19 @@ export function useAgeAssurance() {
export function Provider({children}: {children: React.ReactNode}) {
return (
<AgeAssuranceDataProvider>
<AgeAssuranceServerDataProvider>
<InnerProvider>
<RedirectOverlayProvider>{children}</RedirectOverlayProvider>
</InnerProvider>
</AgeAssuranceDataProvider>
</AgeAssuranceServerDataProvider>
)
}
function InnerProvider({children}: {children: React.ReactNode}) {
const agent = useAgent()
const state = useAgeAssuranceState()
const {data} = useAgeAssuranceDataContext()
const config = useAgeAssuranceRegionConfigWithFallback()
const {metadata} = useAgeAssuranceServerDataContext()
const regionConfig = useAgeAssuranceRegionConfigWithFallback()
const getAndRegisterPushToken = useGetAndRegisterPushToken()
const handleAccessUpdate = useCallback(
@@ -107,11 +107,11 @@ function InnerProvider({children}: {children: React.ReactNode}) {
state,
flags: computeAgeAssuranceFlags({
state,
config,
data,
regionConfig,
metadata,
}),
}
}, [state, data, config])}>
}, [state, metadata, regionConfig])}>
{children}
</AgeAssuranceStateContext.Provider>
)
+23 -17
View File
@@ -1,18 +1,21 @@
import {useEffect, useMemo, useState} from 'react'
import {computeAgeAssuranceRegionAccess} from '@atproto/api'
import {
type AppBskyAgeassuranceDefs,
computeAgeAssuranceRegionAccess,
} from '@atproto/api'
import {getAge} from '#/lib/strings/time'
import {useSession} from '#/state/session'
import {
type AgeAssuranceData,
getConfigFromCache,
getOtherRequiredDataFromCache,
getServerStateFromCache,
useAgeAssuranceDataContext,
useAgeAssuranceServerDataContext,
} from '#/ageAssurance/data'
import {logger} from '#/ageAssurance/logger'
import {
AgeAssuranceAccess,
type AgeAssuranceMetadata,
type AgeAssuranceState,
AgeAssuranceStatus,
parseAccessFromString,
@@ -32,16 +35,16 @@ import {device} from '#/storage'
*/
function computeAgeAssuranceState({
hasSession,
config,
geolocation,
config,
state,
data,
metadata,
}: {
hasSession: boolean
config: AgeAssuranceData['config']
geolocation: Geolocation
state: AgeAssuranceData['state']
data: AgeAssuranceData['data']
config?: AppBskyAgeassuranceDefs.Config
state?: AppBskyAgeassuranceDefs.State
metadata?: AgeAssuranceMetadata
}) {
/**
* This is where we control logged-out moderation prefs. It's all
@@ -91,7 +94,10 @@ function computeAgeAssuranceState({
* accounts with an accurate birthdate, our default fallback rules should
* ensure correct access.
*/
const result = computeAgeAssuranceRegionAccess(region, data)
const result = computeAgeAssuranceRegionAccess(region, {
accountCreatedAt: metadata?.accountCreatedAt,
declaredAge: metadata?.declaredAge,
})
const computed = {
lastInitiatedAt: state?.lastInitiatedAt,
// prefer server state
@@ -106,7 +112,7 @@ function computeAgeAssuranceState({
logger.debug('debug useAgeAssuranceState', {
region,
state,
data,
metadata,
computed,
})
return computed
@@ -132,7 +138,7 @@ export function unsafeGetAndComputeAgeAssurance({did}: {did: string}) {
}
const region = getAgeAssuranceRegionConfigWithFallback(config, geolocation)
const data = {
const metadata: AgeAssuranceMetadata = {
accountCreatedAt: state.metadata?.accountCreatedAt,
declaredAge: requiredData?.birthdate
? getAge(new Date(requiredData.birthdate))
@@ -144,15 +150,15 @@ export function unsafeGetAndComputeAgeAssurance({did}: {did: string}) {
config,
geolocation,
state: state.state,
data,
metadata,
})
return {
state: computed,
flags: computeAgeAssuranceFlags({
state: computed,
config: region,
data,
regionConfig: region,
metadata,
}),
}
}
@@ -160,7 +166,7 @@ export function unsafeGetAndComputeAgeAssurance({did}: {did: string}) {
export function useAgeAssuranceState(): AgeAssuranceState {
const {hasSession} = useSession()
const geolocation = useGeolocation()
const {config, state, data} = useAgeAssuranceDataContext()
const {config, state, metadata} = useAgeAssuranceServerDataContext()
return useMemo(
() =>
@@ -169,9 +175,9 @@ export function useAgeAssuranceState(): AgeAssuranceState {
config,
geolocation,
state,
data,
metadata,
}),
[hasSession, geolocation, config, state, data],
[hasSession, geolocation, config, state, metadata],
)
}
+8
View File
@@ -1,3 +1,5 @@
import {type computeAgeAssuranceRegionAccess} from '@atproto/api'
import {logger} from '#/ageAssurance/logger'
export enum AgeAssuranceAccess {
@@ -14,6 +16,12 @@ export enum AgeAssuranceStatus {
Blocked = 'blocked',
}
export type AgeAssuranceMetadata = Parameters<
typeof computeAgeAssuranceRegionAccess
>[1] & {
birthdate: string | undefined
}
export type AgeAssuranceState = {
lastInitiatedAt?: string
status: AgeAssuranceStatus
@@ -1,14 +1,14 @@
import {useCallback} from 'react'
import {computeAgeAssuranceRegionAccess} from '@atproto/api'
import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
import {useAgeAssuranceServerDataContext} from '#/ageAssurance/data'
import {logger} from '#/ageAssurance/logger'
import {AgeAssuranceAccess, parseAccessFromString} from '#/ageAssurance/types'
import {getAgeAssuranceRegionConfigWithFallback} from '#/ageAssurance/util'
import {type Geolocation} from '#/geolocation'
export function useComputeAgeAssuranceRegionAccess() {
const {config, data} = useAgeAssuranceDataContext()
const {config, metadata} = useAgeAssuranceServerDataContext()
return useCallback(
(geolocation: Geolocation) => {
if (!config) {
@@ -19,11 +19,14 @@ export function useComputeAgeAssuranceRegionAccess() {
config,
geolocation,
)
const result = computeAgeAssuranceRegionAccess(region, data)
const result = computeAgeAssuranceRegionAccess(region, {
accountCreatedAt: metadata?.accountCreatedAt,
declaredAge: metadata?.declaredAge,
})
return result
? parseAccessFromString(result.access)
: AgeAssuranceAccess.Full
},
[config, data],
[config, metadata],
)
}
+13 -13
View File
@@ -11,14 +11,14 @@ import {getAge} from '#/lib/strings/time'
import {restrictChatSettings} from '#/state/queries/messages/restrictChatSettings'
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation'
import {
type AgeAssuranceData,
getDidFromAgentSession,
getOtherRequiredDataFromCache,
useAgeAssuranceDataContext,
useAgeAssuranceServerDataContext,
} from '#/ageAssurance/data'
import {
AgeAssuranceAccess,
type AgeAssuranceFlags,
type AgeAssuranceMetadata,
type AgeAssuranceState,
} from '#/ageAssurance/types'
import {type Geolocation, useGeolocation} from '#/geolocation'
@@ -67,7 +67,7 @@ export function getAgeAssuranceRegionConfigWithFallback(
*/
export function useAgeAssuranceRegionConfig() {
const geolocation = useGeolocation()
const {config} = useAgeAssuranceDataContext()
const {config} = useAgeAssuranceServerDataContext()
return useMemo(() => {
if (!config) return
// use generic helper, we want to potentially return undefined
@@ -136,22 +136,22 @@ export function maybeRestrictChatSettings({agent}: {agent: AtpAgent}) {
export function computeAgeAssuranceFlags({
state,
config,
data,
regionConfig,
metadata,
}: {
state: AgeAssuranceState
config: AppBskyAgeassuranceDefs.ConfigRegion
data: AgeAssuranceData['data']
regionConfig: AppBskyAgeassuranceDefs.ConfigRegion
metadata?: AgeAssuranceMetadata
}): AgeAssuranceFlags {
const chatDisabled = state.access !== AgeAssuranceAccess.Full
const isDeclaredUnderAdultAge = data?.birthdate
? isUnderAge(data.birthdate, 18)
const isDeclaredUnderAdultAge = metadata?.declaredAge
? metadata.declaredAge < 18
: true
const isOverRegionMinAccessAge = data?.birthdate
? !isUnderAge(data.birthdate, config.minAccessAge)
const isOverRegionMinAccessAge = metadata?.declaredAge
? metadata.declaredAge >= regionConfig.minAccessAge
: false
const isOverAppMinAccessAge = data?.birthdate
? !isUnderAge(data.birthdate, MIN_ACCESS_AGE)
const isOverAppMinAccessAge = metadata?.declaredAge
? metadata.declaredAge >= MIN_ACCESS_AGE
: false
const adultContentDisabled =
state.access !== AgeAssuranceAccess.Full || isDeclaredUnderAdultAge
+4 -4
View File
@@ -24,7 +24,7 @@ import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate'
import {restrictChatSettings} from '#/state/queries/messages/restrictChatSettings'
import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders'
import {
prefetchAgeAssuranceData,
prefetchAgeAssuranceServerData,
setBirthdateForDid,
setCreatedAtForDid,
} from '#/ageAssurance/data'
@@ -74,7 +74,7 @@ export async function createAgentAndResume(
}
// after session is attached
const aa = prefetchAgeAssuranceData({agent})
const aa = prefetchAgeAssuranceServerData({agent})
agent.configureProxy(BLUESKY_PROXY_HEADER.get())
@@ -113,7 +113,7 @@ export async function createAgentAndLogin(
const account = agentToSessionAccountOrThrow(agent)
const gates = features.refresh({strategy: 'prefer-fresh-gates'})
const moderation = configureModerationForAccount(agent, account)
const aa = prefetchAgeAssuranceData({agent})
const aa = prefetchAgeAssuranceServerData({agent})
agent.configureProxy(BLUESKY_PROXY_HEADER.get())
@@ -175,7 +175,7 @@ export async function createAgentAndCreateAccount(
setBirthdateForDid({did: account.did, birthdate})
snoozeBirthdateUpdateAllowedForDid(account.did)
// do this last
const aa = prefetchAgeAssuranceData({agent})
const aa = prefetchAgeAssuranceServerData({agent})
// Not awaited so that we can still get into onboarding.
// This is OK because we won't let you toggle adult stuff until you set the date.
+7 -5
View File
@@ -36,8 +36,8 @@ import {
} from '#/state/session/types'
import {useOnboardingDispatch} from '#/state/shell/onboarding'
import {
clearAgeAssuranceData,
clearAgeAssuranceDataForDid,
clearAgeAssuranceServerDataForAll,
clearAgeAssuranceServerDataForDid,
} from '#/ageAssurance/data'
const StateContext = createContext<SessionStateContext>({
@@ -203,7 +203,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
addSessionDebugLog({type: 'method:end', method: 'logout'})
if (prevState.currentAgentState.did) {
clearAgeAssuranceDataForDid({did: prevState.currentAgentState.did})
clearAgeAssuranceServerDataForDid({
did: prevState.currentAgentState.did,
})
void clearPersistedQueryStorage(prevState.currentAgentState.did)
}
// reset onboarding flow on logout
@@ -234,7 +236,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
},
)
addSessionDebugLog({type: 'method:end', method: 'logout'})
clearAgeAssuranceData()
clearAgeAssuranceServerDataForAll()
for (const account of prevState.accounts) {
void clearPersistedQueryStorage(account.did)
}
@@ -304,7 +306,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
accountDid: account.did,
})
addSessionDebugLog({type: 'method:end', method: 'removeAccount', account})
clearAgeAssuranceDataForDid({did: account.did})
clearAgeAssuranceServerDataForDid({did: account.did})
},
[store, cancelPendingTask],
)