From 13db34c590cff5fa177a69744a06ca0794124a96 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 15 Jul 2025 17:00:33 -0500 Subject: [PATCH] Checkpoint: almost there --- .../ageAssurance/AgeAssuranceAccountCard.tsx | 4 ++-- .../ageAssurance/AgeAssuranceAdmonition.tsx | 4 ++-- .../AgeAssuranceDismissableNotice.tsx | 5 ++-- .../ageAssurance/AgeRestrictedScreen.tsx | 4 ++-- src/lib/notifications/notifications.ts | 20 +++++++--------- src/screens/Moderation/index.tsx | 6 ++--- src/state/ageAssurance/index.tsx | 22 +++++++---------- src/state/ageAssurance/types.ts | 7 +----- src/state/ageAssurance/useAgeInfo.ts | 10 ++++---- src/state/ageAssurance/useIsAgeRestricted.ts | 24 +++++++++++++++++++ .../useMustCompleteAgeAssurance.ts | 17 ------------- src/state/queries/preferences/index.ts | 8 +++---- 12 files changed, 62 insertions(+), 69 deletions(-) create mode 100644 src/state/ageAssurance/useIsAgeRestricted.ts delete mode 100644 src/state/ageAssurance/useMustCompleteAgeAssurance.ts diff --git a/src/components/ageAssurance/AgeAssuranceAccountCard.tsx b/src/components/ageAssurance/AgeAssuranceAccountCard.tsx index cfa4ce2b0d..3c26a0cc2a 100644 --- a/src/components/ageAssurance/AgeAssuranceAccountCard.tsx +++ b/src/components/ageAssurance/AgeAssuranceAccountCard.tsx @@ -20,11 +20,11 @@ import {createStaticClick, InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) { - const {isLoaded, mustCompleteAgeAssurance, isUnderage} = useAgeInfo() + const {isLoaded, isAgeRestricted, isUnderage} = useAgeInfo() if (!isLoaded) return null if (isUnderage) return null - if (!mustCompleteAgeAssurance) return null + if (!isAgeRestricted) return null return } diff --git a/src/components/ageAssurance/AgeAssuranceAdmonition.tsx b/src/components/ageAssurance/AgeAssuranceAdmonition.tsx index e30dfa8cf4..8bd3b86635 100644 --- a/src/components/ageAssurance/AgeAssuranceAdmonition.tsx +++ b/src/components/ageAssurance/AgeAssuranceAdmonition.tsx @@ -15,11 +15,11 @@ export function AgeAssuranceAdmonition({ style, }: ViewStyleProp & {children: React.ReactNode}) { const control = useDialogControl() - const {isLoaded, isUnderage, mustCompleteAgeAssurance} = useAgeInfo() + const {isLoaded, isUnderage, isAgeRestricted} = useAgeInfo() if (!isLoaded) return null if (isUnderage) return null - if (!mustCompleteAgeAssurance) return null + if (!isAgeRestricted) return null return ( diff --git a/src/components/ageAssurance/AgeAssuranceDismissableNotice.tsx b/src/components/ageAssurance/AgeAssuranceDismissableNotice.tsx index 9b7c29b30c..9a0abc411d 100644 --- a/src/components/ageAssurance/AgeAssuranceDismissableNotice.tsx +++ b/src/components/ageAssurance/AgeAssuranceDismissableNotice.tsx @@ -12,8 +12,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' export function AgeAssuranceDismissableNotice({style}: ViewStyleProp & {}) { const {_} = useLingui() - const {isLoaded, isUnderage, mustCompleteAgeAssurance, assurance} = - useAgeInfo() + const {isLoaded, isUnderage, isAgeRestricted, assurance} = useAgeInfo() const {nux} = useNux(Nux.AgeAssuranceDismissableNotice) const copy = useAgeAssuranceCopy() const {mutate: save, variables} = useSaveNux() @@ -21,7 +20,7 @@ export function AgeAssuranceDismissableNotice({style}: ViewStyleProp & {}) { if (!isLoaded) return null if (isUnderage) return null - if (!mustCompleteAgeAssurance) return null + if (!isAgeRestricted) return null if (assurance.lastInitiatedAt) return null if (hidden) return null if (nux && nux.completed) return null diff --git a/src/components/ageAssurance/AgeRestrictedScreen.tsx b/src/components/ageAssurance/AgeRestrictedScreen.tsx index 35628b6698..408924ed17 100644 --- a/src/components/ageAssurance/AgeRestrictedScreen.tsx +++ b/src/components/ageAssurance/AgeRestrictedScreen.tsx @@ -24,7 +24,7 @@ export function AgeRestrictedScreen({ }) { const {_} = useLingui() const copy = useAgeAssuranceCopy() - const {isLoaded, mustCompleteAgeAssurance} = useAgeInfo() + const {isLoaded, isAgeRestricted} = useAgeInfo() if (!isLoaded) { return ( @@ -39,7 +39,7 @@ export function AgeRestrictedScreen({ ) } - if (!mustCompleteAgeAssurance) return children + if (!isAgeRestricted) return children return ( diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts index 522c33ceb5..78eef3b104 100644 --- a/src/lib/notifications/notifications.ts +++ b/src/lib/notifications/notifications.ts @@ -8,8 +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 {useMustCompleteAgeAssurance} from '#/state/ageAssurance/useMustCompleteAgeAssurance' +import {useIsAgeRestricted} from '#/state/ageAssurance/useIsAgeRestricted' import {type SessionAccount, useAgent, useSession} from '#/state/session' import BackgroundNotificationHandler from '#/../modules/expo-background-notification-handler' @@ -123,7 +122,7 @@ async function getPushToken() { * @see https://github.com/bluesky-social/social-app/pull/4467 */ export function useGetAndRegisterPushToken() { - const mustCompleteAgeAssurance = useMustCompleteAgeAssurance() + const {isAgeRestricted} = useIsAgeRestricted() const registerPushToken = useRegisterPushToken() return useCallback( async ({ @@ -150,13 +149,13 @@ export function useGetAndRegisterPushToken() { */ registerPushToken({ token, - isAgeRestricted: isAgeRestrictedOverride ?? mustCompleteAgeAssurance, + isAgeRestricted: isAgeRestrictedOverride ?? isAgeRestricted, }) } return token }, - [registerPushToken, mustCompleteAgeAssurance], + [registerPushToken, isAgeRestricted], ) } @@ -171,15 +170,14 @@ export function useNotificationsRegistration() { const {currentAccount} = useSession() const registerPushToken = useRegisterPushToken() const getAndRegisterPushToken = useGetAndRegisterPushToken() - const {isLoaded: isAgeAssuranceLoaded} = useAgeAssuranceContext() - const mustCompleteAgeAssurance = useMustCompleteAgeAssurance() + const {isReady: isAgeRestrictionReady, isAgeRestricted} = useIsAgeRestricted() useEffect(() => { /** * We want this to init right away _after_ we have a logged in user, and * _after_ we've loaded their age assurance state. */ - if (!currentAccount || !isAgeAssuranceLoaded) return + if (!currentAccount || !isAgeRestrictionReady) return notyLogger.debug(`useNotificationsRegistration`) @@ -201,7 +199,7 @@ export function useNotificationsRegistration() { * @see https://docs.expo.dev/versions/latest/sdk/notifications/#addpushtokenlistenerlistener */ const subscription = Notifications.addPushTokenListener(async token => { - registerPushToken({token, isAgeRestricted: mustCompleteAgeAssurance}) + registerPushToken({token, isAgeRestricted: isAgeRestricted}) notyLogger.debug(`addPushTokenListener callback`, {token}) }) @@ -212,8 +210,8 @@ export function useNotificationsRegistration() { currentAccount, getAndRegisterPushToken, registerPushToken, - isAgeAssuranceLoaded, - mustCompleteAgeAssurance, + isAgeRestrictionReady, + isAgeRestricted, ]) } diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index baa0265d8e..f3f557658e 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -160,7 +160,7 @@ export function ModerationScreenInner({ data: labelers, error: labelersError, } = useMyLabelersQuery() - const {declaredAge, isUnderage, mustCompleteAgeAssurance} = useAgeInfo() + const {declaredAge, isUnderage, isAgeRestricted} = useAgeInfo() useFocusEffect( useCallback(() => { @@ -345,7 +345,7 @@ export function ModerationScreenInner({ a.overflow_hidden, t.atoms.bg_contrast_25, ]}> - {!isUnderage && !mustCompleteAgeAssurance && ( + {!isUnderage && !isAgeRestricted && ( <> )} diff --git a/src/state/ageAssurance/index.tsx b/src/state/ageAssurance/index.tsx index 4d79184539..d58e624ab9 100644 --- a/src/state/ageAssurance/index.tsx +++ b/src/state/ageAssurance/index.tsx @@ -13,7 +13,6 @@ import { type AgeAssuranceContextType, } from '#/state/ageAssurance/types' import {useGeolocation} from '#/state/geolocation' -import {STALE} from '#/state/queries' import {useAgent} from '#/state/session' const logger = Logger.create(Logger.Context.AgeAssurance) @@ -26,7 +25,6 @@ const DEFAULT_AGE_ASSURANCE_STATE: AppBskyUnspeccedDefs.AgeAssuranceState = { const AgeAssuranceContext = createContext({ status: 'unknown', isLoaded: false, - isAgeRestricted: false, lastInitiatedAt: undefined, }) const AgeAssuranceAPIContext = createContext({ @@ -54,8 +52,6 @@ export function Provider({children}: {children: React.ReactNode}) { */ enabled: true, queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'), - staleTime: STALE.MINUTES.ONE, - refetchOnWindowFocus: geolocation?.isAgeRestrictedGeo === true, async queryFn() { if (!agent.session) return null @@ -78,11 +74,13 @@ export function Provider({children}: {children: React.ReactNode}) { account: agent.session?.did, }) - await getAndRegisterPushToken({ - isAgeRestricted: Boolean( - isAgeRestrictedGeo && data.status !== 'assured', - ), - }) + if (gate('age_assurance')) { + await getAndRegisterPushToken({ + isAgeRestricted: Boolean( + isAgeRestrictedGeo && data.status !== 'assured', + ), + }) + } return data } catch (e) { @@ -99,19 +97,15 @@ export function Provider({children}: {children: React.ReactNode}) { * Derive state, or fall back to defaults */ const ageAssuranceContext = useMemo(() => { - const enabled = __DEV__ || gate('age_assurance') const {status, lastInitiatedAt} = data || DEFAULT_AGE_ASSURANCE_STATE const ctx: AgeAssuranceContextType = { isLoaded: isFetched, status, lastInitiatedAt, - isAgeRestricted: isAgeRestrictedGeo && status !== 'assured' && enabled, } - logger.debug(`context`, ctx) - return ctx - }, [gate, isAgeRestrictedGeo, isFetched, data]) + }, [isFetched, data]) const ageAssuranceAPIContext = useMemo( () => ({ diff --git a/src/state/ageAssurance/types.ts b/src/state/ageAssurance/types.ts index 182411a29d..bb70f87c14 100644 --- a/src/state/ageAssurance/types.ts +++ b/src/state/ageAssurance/types.ts @@ -10,12 +10,7 @@ export type AgeAssuranceContextType = { /** * The last time the age assurance state was attempted by the user. */ - lastInitiatedAt: string | undefined - /** - * Whether the current user is age-restricted based on their geolocation and - * age assurance state retrieved from the server. - */ - isAgeRestricted: boolean + lastInitiatedAt: AppBskyUnspeccedDefs.AgeAssuranceState['lastInitiatedAt'] } export type AgeAssuranceAPIContextType = { diff --git a/src/state/ageAssurance/useAgeInfo.ts b/src/state/ageAssurance/useAgeInfo.ts index 80df8f96e5..c02da96384 100644 --- a/src/state/ageAssurance/useAgeInfo.ts +++ b/src/state/ageAssurance/useAgeInfo.ts @@ -2,7 +2,7 @@ import {useMemo} from 'react' import {Logger} from '#/logger' import {useAgeAssuranceContext} from '#/state/ageAssurance' -import {useMustCompleteAgeAssurance} from '#/state/ageAssurance/useMustCompleteAgeAssurance' +import {useIsAgeRestricted} from '#/state/ageAssurance/useIsAgeRestricted' import {usePreferencesQuery} from '#/state/queries/preferences' const logger = Logger.create(Logger.Context.AgeAssurance) @@ -11,7 +11,7 @@ type AgeInfo = { isLoaded: boolean declaredAge: number | undefined isUnderage: boolean - mustCompleteAgeAssurance: boolean + isAgeRestricted: boolean assurance: ReturnType } @@ -21,8 +21,8 @@ type AgeInfo = { * more user-friendly interface. */ export function useAgeInfo(): AgeInfo { - const mustCompleteAgeAssurance = useMustCompleteAgeAssurance() const ctx = useAgeAssuranceContext() + const {isAgeRestricted} = useIsAgeRestricted() const {isFetched: preferencesLoaded, data: preferences} = usePreferencesQuery() const declaredAge = preferences?.userAge @@ -34,11 +34,11 @@ export function useAgeInfo(): AgeInfo { isLoaded, declaredAge, isUnderage, - mustCompleteAgeAssurance, + isAgeRestricted, assurance: ctx, } logger.debug(`useAgeInfo`, info) return info - }, [ctx, preferencesLoaded, declaredAge, mustCompleteAgeAssurance]) + }, [ctx, preferencesLoaded, declaredAge, isAgeRestricted]) } diff --git a/src/state/ageAssurance/useIsAgeRestricted.ts b/src/state/ageAssurance/useIsAgeRestricted.ts new file mode 100644 index 0000000000..848f30bdd9 --- /dev/null +++ b/src/state/ageAssurance/useIsAgeRestricted.ts @@ -0,0 +1,24 @@ +import {useMemo} from 'react' + +import {useGate} from '#/lib/statsig/statsig' +import {useAgeAssuranceContext} from '#/state/ageAssurance' +import {useGeolocation} from '#/state/geolocation' + +export function useIsAgeRestricted() { + const {isLoaded, status} = useAgeAssuranceContext() + const {geolocation} = useGeolocation() + const gate = useGate() + + return useMemo(() => { + if (!gate('age_assurance') || !geolocation?.isAgeRestrictedGeo) { + return { + isReady: true, + isAgeRestricted: false, + } + } + return { + isReady: isLoaded, + isAgeRestricted: status !== 'assured', + } + }, [isLoaded, status, geolocation, gate]) +} diff --git a/src/state/ageAssurance/useMustCompleteAgeAssurance.ts b/src/state/ageAssurance/useMustCompleteAgeAssurance.ts deleted file mode 100644 index 552306bf96..0000000000 --- a/src/state/ageAssurance/useMustCompleteAgeAssurance.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {useMemo} from 'react' - -import {useGate} from '#/lib/statsig/statsig' -import {useAgeAssuranceContext} from '#/state/ageAssurance' -import {useGeolocation} from '#/state/geolocation' - -export function useMustCompleteAgeAssurance() { - const gate = useGate() - const {geolocation} = useGeolocation() - const {isLoaded, status: ageAssuranceStatus} = useAgeAssuranceContext() - return useMemo(() => { - if (!gate('age_assurance')) return false - if (!geolocation?.isAgeRestrictedGeo) return false - if (!isLoaded) return false - return ageAssuranceStatus !== 'assured' - }, [isLoaded, gate, geolocation, ageAssuranceStatus]) -} diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 5100f721e5..ae6013624b 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -11,7 +11,7 @@ import {replaceEqualDeep} from '#/lib/functions' import {getAge} from '#/lib/strings/time' import {logger} from '#/logger' import {AGE_RESTRICTED_MODERATION_PREFS} from '#/state/ageAssurance/const' -import {useMustCompleteAgeAssurance} from '#/state/ageAssurance/useMustCompleteAgeAssurance' +import {useIsAgeRestricted} from '#/state/ageAssurance/useIsAgeRestricted' import {STALE} from '#/state/queries' import { DEFAULT_HOME_FEED_PREFS, @@ -34,7 +34,7 @@ export const preferencesQueryKey = [preferencesQueryKeyRoot] export function usePreferencesQuery() { const agent = useAgent() - const mustCompleteAgeAssurance = useMustCompleteAgeAssurance() + const {isReady: isAgeRestrictionReady, isAgeRestricted} = useIsAgeRestricted() return useQuery({ staleTime: STALE.SECONDS.FIFTEEN, @@ -76,12 +76,12 @@ export function usePreferencesQuery() { select: useCallback( (data: UsePreferencesQueryResponse) => { const isUnderage = (data.userAge || 0) < 18 - if (isUnderage || mustCompleteAgeAssurance) { + if (isUnderage || (isAgeRestrictionReady && isAgeRestricted)) { data.moderationPrefs = AGE_RESTRICTED_MODERATION_PREFS } return data }, - [mustCompleteAgeAssurance], + [isAgeRestrictionReady, isAgeRestricted], ), }) }