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

Configure Feed

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

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