diff --git a/src/App.native.tsx b/src/App.native.tsx index 29e359c48c..3818d29860 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -26,7 +26,7 @@ import I18nProvider from '#/locale/i18nProvider' import {logger} from '#/logger' import {isAndroid, isIOS} from '#/platform/detection' 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 DialogStateProvider} from '#/state/dialogs' import {listenSessionDropped} from '#/state/events' diff --git a/src/App.web.tsx b/src/App.web.tsx index ef60cb8a08..c591c1d5b8 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -15,9 +15,7 @@ import {ThemeProvider} from '#/lib/ThemeContext' import I18nProvider from '#/locale/i18nProvider' import {logger} from '#/logger' 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 DialogStateProvider} from '#/state/dialogs' import {listenSessionDropped} from '#/state/events' diff --git a/src/components/ageAssurance/AgeAssurancePrompt.tsx b/src/components/ageAssurance/AgeAssurancePrompt.tsx index 828c7cfc63..ac9db9b80f 100644 --- a/src/components/ageAssurance/AgeAssurancePrompt.tsx +++ b/src/components/ageAssurance/AgeAssurancePrompt.tsx @@ -2,7 +2,7 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' 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 {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import { diff --git a/src/components/ageAssurance/IsAgeRestricted.tsx b/src/components/ageAssurance/IsAgeRestricted.tsx index 6108ca838e..4bf8c9b105 100644 --- a/src/components/ageAssurance/IsAgeRestricted.tsx +++ b/src/components/ageAssurance/IsAgeRestricted.tsx @@ -1,4 +1,4 @@ -import {useAgeAssuranceContext} from '#/state/ageAssurance' +import {useAgeAssuranceContext} from '#/state/age-assurance' export function True({ children, diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts index 48124e6975..d49e08d736 100644 --- a/src/lib/notifications/notifications.ts +++ b/src/lib/notifications/notifications.ts @@ -8,7 +8,7 @@ import debounce from 'lodash.debounce' import {PUBLIC_APPVIEW_DID, PUBLIC_STAGING_APPVIEW_DID} from '#/lib/constants' import {logger as notyLogger} from '#/lib/notifications/util' 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 BackgroundNotificationHandler from '#/../modules/expo-background-notification-handler' diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index 9b28e637a9..93ea00ad7e 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -12,7 +12,7 @@ import { } from '#/lib/routes/types' import {logger} from '#/logger' import {isIOS} from '#/platform/detection' -import {useAgeAssuranceContext} from '#/state/ageAssurance' +import {useAgeAssuranceContext} from '#/state/age-assurance' import { useMyLabelersQuery, usePreferencesQuery, diff --git a/src/state/ageAssurance.tsx b/src/state/age-assurance/index.tsx similarity index 61% rename from src/state/ageAssurance.tsx rename to src/state/age-assurance/index.tsx index 1c16544d08..ac6b46fcf0 100644 --- a/src/state/ageAssurance.tsx +++ b/src/state/age-assurance/index.tsx @@ -1,54 +1,27 @@ import {createContext, useContext, useMemo} from 'react' import { // useQueryClient, - type QueryObserverBaseResult, useQuery, } from '@tanstack/react-query' import {wait} from '#/lib/async/wait' import {isNetworkError} from '#/lib/strings/errors' import {Logger} from '#/logger' +import { + type AgeAssuranceAPIContextType, + type AgeAssuranceContextType, + type AppBskyUnspeccedDefs, +} from '#/state/age-assurance/types' import {useGeolocation} from '#/state/geolocation' import {useAgent} from '#/state/session' const logger = Logger.create(Logger.Context.AgeAssurance) -export const ageAssuranceQueryKeyRoot = 'ageAssurance' as const -export const createAgeAssuranceQueryKey = (did: string) => - [ageAssuranceQueryKeyRoot, did] as const -const DEFAULT_AGE_ASSURANCE_STATE: TempAgeAssuranceState = { +const createAgeAssuranceQueryKey = (did: string) => + ['ageAssurance', did] as const +const DEFAULT_AGE_ASSURANCE_STATE: AppBskyUnspeccedDefs.AgeAssuranceState = { + lastInitiatedAt: undefined, 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({ isLoaded: false, isAgeRestricted: false, @@ -56,7 +29,6 @@ const AgeAssuranceContext = createContext({ lastInitiatedAt: undefined, hasInitiated: false, }) - const AgeAssuranceAPIContext = createContext({ // @ts-ignore refetch: () => Promise.resolve(), @@ -67,7 +39,7 @@ export function Provider({children}: {children: React.ReactNode}) { const agent = useAgent() const {geolocation} = useGeolocation() - const {data, refetch} = useQuery({ + const {data, isFetched, refetch} = useQuery({ enabled: !!agent.session, queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'), async queryFn() { @@ -76,9 +48,9 @@ export function Provider({children}: {children: React.ReactNode}) { 1e3, (() => ({ data: { - lastInitiatedAt: new Date().toISOString(), + lastInitiatedAt: undefined, status: 'unknown', - } as TempAgeAssuranceState, + } as AppBskyUnspeccedDefs.AgeAssuranceState, }))(), ) @@ -92,18 +64,15 @@ export function Provider({children}: {children: React.ReactNode}) { if (!isNetworkError(e)) { logger.error(`ageAssurance: failed to fetch`, {safeMessage: e}) } - - return DEFAULT_AGE_ASSURANCE_STATE } }, }) const ageAssuranceContext = useMemo(() => { - const isLoaded = Boolean(data) const isAgeRestrictedGeo = !!geolocation?.isAgeRestrictedGeo const {status, lastInitiatedAt} = data || DEFAULT_AGE_ASSURANCE_STATE const ctx: AgeAssuranceContextType = { - isLoaded, + isLoaded: isFetched, lastInitiatedAt, hasInitiated: !!lastInitiatedAt, isExempt: !isAgeRestrictedGeo, @@ -113,7 +82,7 @@ export function Provider({children}: {children: React.ReactNode}) { logger.debug(`context`, ctx) return ctx - }, [geolocation, data]) + }, [geolocation, isFetched, data]) const ageAssuranceAPIContext = useMemo( () => ({ @@ -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() { return useContext(AgeAssuranceContext) } diff --git a/src/state/age-assurance/types.ts b/src/state/age-assurance/types.ts new file mode 100644 index 0000000000..de468f434d --- /dev/null +++ b/src/state/age-assurance/types.ts @@ -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'] +}