Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at f8975036440051185486f6b2c00a201ef2e18a8c 248 lines 6.7 kB view raw
1import {useEffect, useRef, useState} from 'react' 2import {View} from 'react-native' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {retry} from '#/lib/async/retry' 7import {wait} from '#/lib/async/wait' 8import {useAgent} from '#/state/session' 9import {atoms as a, useTheme, web} from '#/alf' 10import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' 11import {Button, ButtonText} from '#/components/Button' 12import * as Dialog from '#/components/Dialog' 13import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' 14import {CheckThick_Stroke2_Corner0_Rounded as SuccessIcon} from '#/components/icons/Check' 15import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo' 16import {Loader} from '#/components/Loader' 17import {Text} from '#/components/Typography' 18import {refetchAgeAssuranceServerState} from '#/ageAssurance' 19import {useAnalytics} from '#/analytics' 20import {IS_NATIVE} from '#/env' 21 22export type AgeAssuranceRedirectDialogState = { 23 result: 'success' | 'unknown' 24 actorDid: string 25} 26 27/** 28 * Validate and parse the query parameters returned from the age assurance 29 * redirect. If not valid, returns `undefined` and the dialog will not open. 30 */ 31export function parseAgeAssuranceRedirectDialogState( 32 state: { 33 result?: string 34 actorDid?: string 35 } = {}, 36): AgeAssuranceRedirectDialogState | undefined { 37 let result: AgeAssuranceRedirectDialogState['result'] = 'unknown' 38 const actorDid = state.actorDid 39 40 switch (state.result) { 41 case 'success': 42 result = 'success' 43 break 44 case 'unknown': 45 default: 46 result = 'unknown' 47 break 48 } 49 50 if (result && actorDid) { 51 return { 52 result, 53 actorDid, 54 } 55 } 56} 57 58export function useAgeAssuranceRedirectDialogControl() { 59 return useGlobalDialogsControlContext().ageAssuranceRedirectDialogControl 60} 61 62export function AgeAssuranceRedirectDialog() { 63 const {_} = useLingui() 64 const control = useAgeAssuranceRedirectDialogControl() 65 66 // for testing 67 // Dialog.useAutoOpen(control.control, 3e3) 68 69 return ( 70 <Dialog.Outer control={control.control} onClose={() => control.clear()}> 71 <Dialog.Handle /> 72 73 <Dialog.ScrollableInner 74 label={_(msg`Verifying your age assurance status`)} 75 style={[web({maxWidth: 400})]}> 76 <Inner optimisticState={control.value} /> 77 </Dialog.ScrollableInner> 78 </Dialog.Outer> 79 ) 80} 81 82export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) { 83 const t = useTheme() 84 const ax = useAnalytics() 85 const {_} = useLingui() 86 const agent = useAgent() 87 const polling = useRef(false) 88 const unmounted = useRef(false) 89 const control = useAgeAssuranceRedirectDialogControl() 90 const [error, setError] = useState(false) 91 const [success, setSuccess] = useState(false) 92 93 useEffect(() => { 94 if (polling.current) return 95 96 polling.current = true 97 98 ax.metric('ageAssurance:redirectDialogOpen', {}) 99 100 wait( 101 3e3, 102 retry( 103 5, 104 () => true, 105 async () => { 106 if (!agent.session) return 107 if (unmounted.current) return 108 109 const data = await refetchAgeAssuranceServerState({agent}) 110 111 if (data?.state.status !== 'assured') { 112 throw new Error( 113 `Polling for age assurance state did not receive assured status`, 114 ) 115 } 116 117 return data 118 }, 119 1e3, 120 ), 121 ) 122 .then(async data => { 123 if (!data) return 124 if (!agent.session) return 125 if (unmounted.current) return 126 127 setSuccess(true) 128 129 ax.metric('ageAssurance:redirectDialogSuccess', {}) 130 }) 131 .catch(() => { 132 if (unmounted.current) return 133 setError(true) 134 ax.metric('ageAssurance:redirectDialogFail', {}) 135 }) 136 137 return () => { 138 unmounted.current = true 139 } 140 }, [ax, agent, control]) 141 142 if (success) { 143 return ( 144 <> 145 <View style={[a.align_start, a.w_full]}> 146 <AgeAssuranceBadge /> 147 148 <View 149 style={[ 150 a.flex_row, 151 a.justify_between, 152 a.align_center, 153 a.gap_sm, 154 a.pt_lg, 155 a.pb_md, 156 ]}> 157 <SuccessIcon size="sm" fill={t.palette.positive_500} /> 158 <Text style={[a.text_xl, a.font_bold]}> 159 <Trans>Success</Trans> 160 </Text> 161 </View> 162 163 <Text style={[a.text_md, a.leading_snug]}> 164 <Trans> 165 We've confirmed your age assurance status. You can now close this 166 dialog. 167 </Trans> 168 </Text> 169 170 {IS_NATIVE && ( 171 <View style={[a.w_full, a.pt_lg]}> 172 <Button 173 label={_(msg`Close`)} 174 size="large" 175 variant="solid" 176 color="secondary" 177 onPress={() => control.control.close()}> 178 <ButtonText> 179 <Trans>Close</Trans> 180 </ButtonText> 181 </Button> 182 </View> 183 )} 184 </View> 185 186 <Dialog.Close /> 187 </> 188 ) 189 } 190 191 return ( 192 <> 193 <View style={[a.align_start, a.w_full]}> 194 <AgeAssuranceBadge /> 195 196 <View 197 style={[ 198 a.flex_row, 199 a.justify_between, 200 a.align_center, 201 a.gap_sm, 202 a.pt_lg, 203 a.pb_md, 204 ]}> 205 {error && <ErrorIcon size="md" fill={t.palette.negative_500} />} 206 207 <Text style={[a.text_xl, a.font_bold]}> 208 {error ? <Trans>Connection issue</Trans> : <Trans>Verifying</Trans>} 209 </Text> 210 211 {!error && <Loader size="md" />} 212 </View> 213 214 <Text style={[a.text_md, a.leading_snug]}> 215 {error ? ( 216 <Trans> 217 We were unable to receive the verification due to a connection 218 issue. It may arrive later. If it does, your account will update 219 automatically. 220 </Trans> 221 ) : ( 222 <Trans> 223 We're confirming your age assurance status with our servers. This 224 should only take a few seconds. 225 </Trans> 226 )} 227 </Text> 228 229 {error && IS_NATIVE && ( 230 <View style={[a.w_full, a.pt_lg]}> 231 <Button 232 label={_(msg`Close`)} 233 size="large" 234 variant="solid" 235 color="secondary" 236 onPress={() => control.control.close()}> 237 <ButtonText> 238 <Trans>Close</Trans> 239 </ButtonText> 240 </Button> 241 </View> 242 )} 243 </View> 244 245 {error && <Dialog.Close />} 246 </> 247 ) 248}