Bluesky app fork with some witchin' additions 馃挮
at feat/tealfm 107 lines 3.4 kB view raw
1import {useEffect, useMemo, useState} from 'react' 2import {computeAgeAssuranceRegionAccess} from '@atproto/api' 3 4import {useSession} from '#/state/session' 5import {useAgeAssuranceDataContext} from '#/ageAssurance/data' 6import {logger} from '#/ageAssurance/logger' 7import { 8 AgeAssuranceAccess, 9 type AgeAssuranceState, 10 AgeAssuranceStatus, 11 parseAccessFromString, 12 parseStatusFromString, 13} from '#/ageAssurance/types' 14import {getAgeAssuranceRegionConfigWithFallback} from '#/ageAssurance/util' 15import {useGeolocation} from '#/geolocation' 16 17export function useAgeAssuranceState(): AgeAssuranceState { 18 const {hasSession} = useSession() 19 const geolocation = useGeolocation() 20 const {config, state, data} = useAgeAssuranceDataContext() 21 22 return useMemo(() => { 23 /** 24 * This is where we control logged-out moderation prefs. It's all 25 * downstream of AA now. 26 */ 27 if (!hasSession) 28 return { 29 status: AgeAssuranceStatus.Unknown, 30 access: AgeAssuranceAccess.Safe, 31 } 32 33 /** 34 * This can happen if the prefetch fails (such as due to network issues). 35 * The query handler will try it again, but if it continues to fail, of 36 * course we won't have config. 37 * 38 * In this case, fail open to avoid blocking users. 39 */ 40 if (!config) { 41 logger.warn('useAgeAssuranceState: missing config') 42 return { 43 status: AgeAssuranceStatus.Unknown, 44 access: AgeAssuranceAccess.Safe, 45 error: 'config', 46 } 47 } 48 49 const region = getAgeAssuranceRegionConfigWithFallback(config, geolocation) 50 const isAARequired = region.countryCode !== '*' 51 const isTerminalState = 52 state?.status === 'assured' || state?.status === 'blocked' 53 54 /* 55 * If we are in a terminal state and AA is required for this region, 56 * we can trust the server state completely and avoid recomputing. 57 */ 58 if (isTerminalState && isAARequired) { 59 return { 60 lastInitiatedAt: state.lastInitiatedAt, 61 status: parseStatusFromString(state.status), 62 access: parseAccessFromString(state.access), 63 } 64 } 65 66 /* 67 * Otherwise, we need to compute the access based on the latest data. For 68 * accounts with an accurate birthdate, our default fallback rules should 69 * ensure correct access. 70 */ 71 const result = computeAgeAssuranceRegionAccess(region, data) 72 const computed = { 73 lastInitiatedAt: state?.lastInitiatedAt, 74 // prefer server state 75 status: state?.status 76 ? parseStatusFromString(state?.status) 77 : AgeAssuranceStatus.Unknown, 78 // prefer server state 79 access: result 80 ? parseAccessFromString(result.access) 81 : AgeAssuranceAccess.Full, 82 } 83 logger.debug('debug useAgeAssuranceState', { 84 region, 85 state, 86 data, 87 computed, 88 }) 89 return computed 90 }, [hasSession, geolocation, config, state, data]) 91} 92 93export function useOnAgeAssuranceAccessUpdate( 94 cb: (state: AgeAssuranceState) => void, 95) { 96 const state = useAgeAssuranceState() 97 // start with null to ensure callback is called on first render 98 const [prevAccess, setPrevAccess] = useState<AgeAssuranceAccess | null>(null) 99 100 useEffect(() => { 101 if (prevAccess !== state.access) { 102 setPrevAccess(state.access) 103 cb(state) 104 logger.debug(`useOnAgeAssuranceAccessUpdate`, {state}) 105 } 106 }, [cb, state, prevAccess]) 107}