forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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 // should never happen, but need to guard
34 if (!config) {
35 logger.warn('useAgeAssuranceState: missing config')
36 return {
37 status: AgeAssuranceStatus.Unknown,
38 access: AgeAssuranceAccess.Unknown,
39 }
40 }
41
42 const region = getAgeAssuranceRegionConfigWithFallback(config, geolocation)
43 const isAARequired = region.countryCode !== '*'
44 const isTerminalState =
45 state?.status === 'assured' || state?.status === 'blocked'
46
47 /*
48 * If we are in a terminal state and AA is required for this region,
49 * we can trust the server state completely and avoid recomputing.
50 */
51 if (isTerminalState && isAARequired) {
52 return {
53 lastInitiatedAt: state.lastInitiatedAt,
54 status: parseStatusFromString(state.status),
55 access: parseAccessFromString(state.access),
56 }
57 }
58
59 /*
60 * Otherwise, we need to compute the access based on the latest data. For
61 * accounts with an accurate birthdate, our default fallback rules should
62 * ensure correct access.
63 */
64 const result = computeAgeAssuranceRegionAccess(region, data)
65 const computed = {
66 lastInitiatedAt: state?.lastInitiatedAt,
67 // prefer server state
68 status: state?.status
69 ? parseStatusFromString(state?.status)
70 : AgeAssuranceStatus.Unknown,
71 // prefer server state
72 access: result
73 ? parseAccessFromString(result.access)
74 : AgeAssuranceAccess.Full,
75 }
76 logger.debug('debug useAgeAssuranceState', {
77 region,
78 state,
79 data,
80 computed,
81 })
82 return computed
83 }, [hasSession, geolocation, config, state, data])
84}
85
86export function useOnAgeAssuranceAccessUpdate(
87 cb: (state: AgeAssuranceState) => void,
88) {
89 const state = useAgeAssuranceState()
90 // start with null to ensure callback is called on first render
91 const [prevAccess, setPrevAccess] = useState<AgeAssuranceAccess | null>(null)
92
93 useEffect(() => {
94 if (prevAccess !== state.access) {
95 setPrevAccess(state.access)
96 cb(state)
97 logger.debug(`useOnAgeAssuranceAccessUpdate`, {state})
98 }
99 }, [cb, state, prevAccess])
100}