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

Configure Feed

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

at 6982eb4fb4d44105dc8b44e898d452d4e5d32c82 209 lines 7.7 kB view raw
1import {View} from 'react-native' 2import {msg, Trans} from '@lingui/macro' 3import {useLingui} from '@lingui/react' 4 5import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo' 6import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf' 7import {Admonition} from '#/components/Admonition' 8import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog' 9import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' 10import {AgeAssuranceConfigUnavailableError} from '#/components/ageAssurance/AgeAssuranceErrors' 11import { 12 AgeAssuranceInitDialog, 13 useDialogControl, 14} from '#/components/ageAssurance/AgeAssuranceInitDialog' 15import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy' 16import {Button, ButtonText} from '#/components/Button' 17import * as Dialog from '#/components/Dialog' 18import {DeviceLocationRequestDialog} from '#/components/dialogs/DeviceLocationRequestDialog' 19import {Divider} from '#/components/Divider' 20import {createStaticClick, InlineLinkText} from '#/components/Link' 21import * as Toast from '#/components/Toast' 22import {Text} from '#/components/Typography' 23import {logger, useAgeAssurance} from '#/ageAssurance' 24import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess' 25import {IS_NATIVE} from '#/env' 26import {useDeviceGeolocationApi} from '#/geolocation' 27 28export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) { 29 const aa = useAgeAssurance() 30 if (aa.state.access === aa.Access.Full) return null 31 if (aa.state.error === 'config') { 32 return ( 33 <View style={style}> 34 <AgeAssuranceConfigUnavailableError /> 35 </View> 36 ) 37 } 38 return <Inner style={style} /> 39} 40 41function Inner({style}: ViewStyleProp & {}) { 42 const t = useTheme() 43 const {_, i18n} = useLingui() 44 const control = useDialogControl() 45 const appealControl = Dialog.useDialogControl() 46 const locationControl = Dialog.useDialogControl() 47 const getTimeAgo = useGetTimeAgo() 48 const {gtPhone} = useBreakpoints() 49 const {setDeviceGeolocation} = useDeviceGeolocationApi() 50 const computeAgeAssuranceRegionAccess = useComputeAgeAssuranceRegionAccess() 51 52 const copy = useAgeAssuranceCopy() 53 const aa = useAgeAssurance() 54 const {status, lastInitiatedAt} = aa.state 55 const isBlocked = status === aa.Status.Blocked 56 const hasInitiated = !!lastInitiatedAt 57 const timeAgo = lastInitiatedAt 58 ? getTimeAgo(lastInitiatedAt, new Date()) 59 : null 60 const diff = lastInitiatedAt 61 ? dateDiff(lastInitiatedAt, new Date(), 'down') 62 : null 63 64 return ( 65 <> 66 <AgeAssuranceInitDialog control={control} /> 67 <AgeAssuranceAppealDialog control={appealControl} /> 68 69 <View style={style}> 70 <View 71 style={[a.p_lg, a.rounded_md, a.border, t.atoms.border_contrast_low]}> 72 <View 73 style={[ 74 a.flex_row, 75 a.justify_between, 76 a.align_center, 77 a.gap_lg, 78 a.pb_md, 79 a.z_10, 80 ]}> 81 <View style={[a.align_start]}> 82 <AgeAssuranceBadge /> 83 </View> 84 </View> 85 86 <View style={[a.pb_md, a.gap_xs]}> 87 <Text style={[a.text_sm, a.leading_snug]}>{copy.notice}</Text> 88 89 {IS_NATIVE && ( 90 <> 91 <Text style={[a.text_sm, a.leading_snug]}> 92 <Trans> 93 Is your location not accurate?{' '} 94 <InlineLinkText 95 label={_(msg`Confirm your location`)} 96 {...createStaticClick(() => { 97 locationControl.open() 98 })}> 99 Tap here to confirm your location. 100 </InlineLinkText>{' '} 101 </Trans> 102 </Text> 103 104 <DeviceLocationRequestDialog 105 control={locationControl} 106 onLocationAcquired={props => { 107 const access = computeAgeAssuranceRegionAccess( 108 props.geolocation, 109 ) 110 if (access !== aa.Access.Full) { 111 props.disableDialogAction() 112 props.setDialogError( 113 _( 114 msg`We're sorry, but based on your device's location, you are currently located in a region that requires age assurance.`, 115 ), 116 ) 117 } else { 118 props.closeDialog(() => { 119 // set this after close! 120 setDeviceGeolocation(props.geolocation) 121 Toast.show(_(msg`Thanks! You're all set.`), { 122 type: 'success', 123 }) 124 }) 125 } 126 }} 127 /> 128 </> 129 )} 130 </View> 131 132 {isBlocked ? ( 133 <Admonition type="warning"> 134 <Trans> 135 You are currently unable to access Bluesky's Age Assurance flow. 136 Please{' '} 137 <InlineLinkText 138 label={_(msg`Contact our moderation team`)} 139 {...createStaticClick(() => { 140 appealControl.open() 141 logger.metric('ageAssurance:appealDialogOpen', {}) 142 })}> 143 contact our moderation team 144 </InlineLinkText>{' '} 145 if you believe this is an error. 146 </Trans> 147 </Admonition> 148 ) : ( 149 <> 150 <Divider /> 151 <View 152 style={[ 153 a.pt_md, 154 gtPhone 155 ? [ 156 a.flex_row_reverse, 157 a.gap_xl, 158 a.justify_between, 159 a.align_center, 160 ] 161 : [a.gap_md], 162 ]}> 163 <Button 164 label={_(msg`Verify now`)} 165 size="small" 166 variant="solid" 167 color={hasInitiated ? 'secondary' : 'primary'} 168 onPress={() => { 169 control.open() 170 logger.metric('ageAssurance:initDialogOpen', { 171 hasInitiatedPreviously: hasInitiated, 172 }) 173 }}> 174 <ButtonText> 175 {hasInitiated ? ( 176 <Trans>Verify again</Trans> 177 ) : ( 178 <Trans>Verify now</Trans> 179 )} 180 </ButtonText> 181 </Button> 182 183 {lastInitiatedAt && timeAgo && diff ? ( 184 <Text 185 style={[a.text_sm, a.italic, t.atoms.text_contrast_medium]} 186 title={i18n.date(lastInitiatedAt, { 187 dateStyle: 'medium', 188 timeStyle: 'medium', 189 })}> 190 {diff.value === 0 ? ( 191 <Trans>Last initiated just now</Trans> 192 ) : ( 193 <Trans>Last initiated {timeAgo} ago</Trans> 194 )} 195 </Text> 196 ) : ( 197 <Text 198 style={[a.text_sm, a.italic, t.atoms.text_contrast_medium]}> 199 <Trans>Age assurance only takes a few minutes</Trans> 200 </Text> 201 )} 202 </View> 203 </> 204 )} 205 </View> 206 </View> 207 </> 208 ) 209}