This commit is contained in:
Eric Bailey
2025-06-26 21:54:04 -05:00
parent 7d4923fff4
commit e28af3259c
8 changed files with 57 additions and 59 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isAndroid, isIOS} from '#/platform/detection' import {isAndroid, isIOS} from '#/platform/detection'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as AgeAssuranceProvider} from '#/state/ageAssurance' import {Provider as AgeAssuranceProvider} from '#/state/age-assurance'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as DialogStateProvider} from '#/state/dialogs'
import {listenSessionDropped} from '#/state/events' import {listenSessionDropped} from '#/state/events'
+1 -3
View File
@@ -15,9 +15,7 @@ import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider' import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import { import {Provider as AgeAssuranceProvider} from '#/state/age-assurance'
Provider as AgeAssuranceProvider,
} from '#/state/ageAssurance'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as DialogStateProvider} from '#/state/dialogs'
import {listenSessionDropped} from '#/state/events' import {listenSessionDropped} from '#/state/events'
@@ -2,7 +2,7 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useAgeAssuranceContext} from '#/state/ageAssurance' import {useAgeAssuranceContext} from '#/state/age-assurance'
import {atoms as a, select, useTheme, type ViewStyleProp} from '#/alf' import {atoms as a, select, useTheme, type ViewStyleProp} from '#/alf'
import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
import { import {
@@ -1,4 +1,4 @@
import {useAgeAssuranceContext} from '#/state/ageAssurance' import {useAgeAssuranceContext} from '#/state/age-assurance'
export function True({ export function True({
children, children,
+1 -1
View File
@@ -8,7 +8,7 @@ import debounce from 'lodash.debounce'
import {PUBLIC_APPVIEW_DID, PUBLIC_STAGING_APPVIEW_DID} from '#/lib/constants' import {PUBLIC_APPVIEW_DID, PUBLIC_STAGING_APPVIEW_DID} from '#/lib/constants'
import {logger as notyLogger} from '#/lib/notifications/util' import {logger as notyLogger} from '#/lib/notifications/util'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {useAgeAssuranceContext} from '#/state/ageAssurance' import {useAgeAssuranceContext} from '#/state/age-assurance'
import {type SessionAccount, useAgent, useSession} from '#/state/session' import {type SessionAccount, useAgent, useSession} from '#/state/session'
import BackgroundNotificationHandler from '#/../modules/expo-background-notification-handler' import BackgroundNotificationHandler from '#/../modules/expo-background-notification-handler'
+1 -1
View File
@@ -12,7 +12,7 @@ import {
} from '#/lib/routes/types' } from '#/lib/routes/types'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS} from '#/platform/detection' import {isIOS} from '#/platform/detection'
import {useAgeAssuranceContext} from '#/state/ageAssurance' import {useAgeAssuranceContext} from '#/state/age-assurance'
import { import {
useMyLabelersQuery, useMyLabelersQuery,
usePreferencesQuery, usePreferencesQuery,
@@ -1,54 +1,27 @@
import {createContext, useContext, useMemo} from 'react' import {createContext, useContext, useMemo} from 'react'
import { import {
// useQueryClient, // useQueryClient,
type QueryObserverBaseResult,
useQuery, useQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {wait} from '#/lib/async/wait' import {wait} from '#/lib/async/wait'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {Logger} from '#/logger' import {Logger} from '#/logger'
import {
type AgeAssuranceAPIContextType,
type AgeAssuranceContextType,
type AppBskyUnspeccedDefs,
} from '#/state/age-assurance/types'
import {useGeolocation} from '#/state/geolocation' import {useGeolocation} from '#/state/geolocation'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
const logger = Logger.create(Logger.Context.AgeAssurance) const logger = Logger.create(Logger.Context.AgeAssurance)
export const ageAssuranceQueryKeyRoot = 'ageAssurance' as const const createAgeAssuranceQueryKey = (did: string) =>
export const createAgeAssuranceQueryKey = (did: string) => ['ageAssurance', did] as const
[ageAssuranceQueryKeyRoot, did] as const const DEFAULT_AGE_ASSURANCE_STATE: AppBskyUnspeccedDefs.AgeAssuranceState = {
const DEFAULT_AGE_ASSURANCE_STATE: TempAgeAssuranceState = { lastInitiatedAt: undefined,
status: 'unknown', status: 'unknown',
} }
type TempAgeAssuranceState = {
lastInitiatedAt?: string
status: 'unknown' | 'pending' | 'assured'
}
export type AgeAssuranceContextType = {
isLoaded: boolean
/**
* Whether the current user is age-restricted based on their geolocation and
* age assurance state retrieved from the server.
*/
isAgeRestricted: boolean
isExempt: boolean
/**
* The last time the age assurance state was attempted by the user.
*/
lastInitiatedAt: string | undefined
/**
* Whether the user has initiated an age assurance check.
*/
hasInitiated: boolean
}
export type AgeAssuranceAPIContextType = {
/**
* Refreshes the age assurance state by fetching it from the server.
*/
refetch: QueryObserverBaseResult['refetch']
}
const AgeAssuranceContext = createContext<AgeAssuranceContextType>({ const AgeAssuranceContext = createContext<AgeAssuranceContextType>({
isLoaded: false, isLoaded: false,
isAgeRestricted: false, isAgeRestricted: false,
@@ -56,7 +29,6 @@ const AgeAssuranceContext = createContext<AgeAssuranceContextType>({
lastInitiatedAt: undefined, lastInitiatedAt: undefined,
hasInitiated: false, hasInitiated: false,
}) })
const AgeAssuranceAPIContext = createContext<AgeAssuranceAPIContextType>({ const AgeAssuranceAPIContext = createContext<AgeAssuranceAPIContextType>({
// @ts-ignore // @ts-ignore
refetch: () => Promise.resolve(), refetch: () => Promise.resolve(),
@@ -67,7 +39,7 @@ export function Provider({children}: {children: React.ReactNode}) {
const agent = useAgent() const agent = useAgent()
const {geolocation} = useGeolocation() const {geolocation} = useGeolocation()
const {data, refetch} = useQuery({ const {data, isFetched, refetch} = useQuery({
enabled: !!agent.session, enabled: !!agent.session,
queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'), queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'),
async queryFn() { async queryFn() {
@@ -76,9 +48,9 @@ export function Provider({children}: {children: React.ReactNode}) {
1e3, 1e3,
(() => ({ (() => ({
data: { data: {
lastInitiatedAt: new Date().toISOString(), lastInitiatedAt: undefined,
status: 'unknown', status: 'unknown',
} as TempAgeAssuranceState, } as AppBskyUnspeccedDefs.AgeAssuranceState,
}))(), }))(),
) )
@@ -92,18 +64,15 @@ export function Provider({children}: {children: React.ReactNode}) {
if (!isNetworkError(e)) { if (!isNetworkError(e)) {
logger.error(`ageAssurance: failed to fetch`, {safeMessage: e}) logger.error(`ageAssurance: failed to fetch`, {safeMessage: e})
} }
return DEFAULT_AGE_ASSURANCE_STATE
} }
}, },
}) })
const ageAssuranceContext = useMemo<AgeAssuranceContextType>(() => { const ageAssuranceContext = useMemo<AgeAssuranceContextType>(() => {
const isLoaded = Boolean(data)
const isAgeRestrictedGeo = !!geolocation?.isAgeRestrictedGeo const isAgeRestrictedGeo = !!geolocation?.isAgeRestrictedGeo
const {status, lastInitiatedAt} = data || DEFAULT_AGE_ASSURANCE_STATE const {status, lastInitiatedAt} = data || DEFAULT_AGE_ASSURANCE_STATE
const ctx: AgeAssuranceContextType = { const ctx: AgeAssuranceContextType = {
isLoaded, isLoaded: isFetched,
lastInitiatedAt, lastInitiatedAt,
hasInitiated: !!lastInitiatedAt, hasInitiated: !!lastInitiatedAt,
isExempt: !isAgeRestrictedGeo, isExempt: !isAgeRestrictedGeo,
@@ -113,7 +82,7 @@ export function Provider({children}: {children: React.ReactNode}) {
logger.debug(`context`, ctx) logger.debug(`context`, ctx)
return ctx return ctx
}, [geolocation, data]) }, [geolocation, isFetched, data])
const ageAssuranceAPIContext = useMemo<AgeAssuranceAPIContextType>( const ageAssuranceAPIContext = useMemo<AgeAssuranceAPIContextType>(
() => ({ () => ({
@@ -131,12 +100,6 @@ export function Provider({children}: {children: React.ReactNode}) {
) )
} }
/**
* Returns the current age assurance state from context. This data is awaited
* prior to rendering the app, and therefore the data here should be up to date
* and trustworth by the time we're rendering anything inside the `Splash`
* wrapper in the root component files.
*/
export function useAgeAssuranceContext() { export function useAgeAssuranceContext() {
return useContext(AgeAssuranceContext) return useContext(AgeAssuranceContext)
} }
+37
View File
@@ -0,0 +1,37 @@
import {type QueryObserverBaseResult} from '@tanstack/react-query'
export namespace AppBskyUnspeccedDefs {
export type AgeAssuranceState = {
lastInitiatedAt?: string
status: 'unknown' | 'pending' | 'assured'
}
}
export type AgeAssuranceContextType = {
isLoaded: boolean
/**
* Whether the current user is age-restricted based on their geolocation and
* age assurance state retrieved from the server.
*/
isAgeRestricted: boolean
/**
* Whether the user is exempt from age assurance checks, e.g., due to
* them not being in a region that requires age assurance.
*/
isExempt: boolean
/**
* The last time the age assurance state was attempted by the user.
*/
lastInitiatedAt: string | undefined
/**
* Whether the user has initiated an age assurance check.
*/
hasInitiated: boolean
}
export type AgeAssuranceAPIContextType = {
/**
* Refreshes the age assurance state by fetching it from the server.
*/
refetch: QueryObserverBaseResult['refetch']
}