Bluesky app fork with some witchin' additions 馃挮
at main 4.9 kB view raw
1import {useState} from 'react' 2import {View} from 'react-native' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {wait} from '#/lib/async/wait' 7import {isNetworkError, useCleanError} from '#/lib/hooks/useCleanError' 8import {logger} from '#/logger' 9import {isWeb} from '#/platform/detection' 10import {atoms as a, useTheme, web} from '#/alf' 11import {Admonition} from '#/components/Admonition' 12import {Button, ButtonIcon, ButtonText} from '#/components/Button' 13import * as Dialog from '#/components/Dialog' 14import {PinLocation_Stroke2_Corner0_Rounded as LocationIcon} from '#/components/icons/PinLocation' 15import {Loader} from '#/components/Loader' 16import {Text} from '#/components/Typography' 17import {type Geolocation, useRequestDeviceGeolocation} from '#/geolocation' 18 19export type Props = { 20 onLocationAcquired?: (props: { 21 geolocation: Geolocation 22 setDialogError: (error: string) => void 23 disableDialogAction: () => void 24 closeDialog: (callback?: () => void) => void 25 }) => void 26} 27 28export function DeviceLocationRequestDialog({ 29 control, 30 onLocationAcquired, 31}: Props & { 32 control: Dialog.DialogOuterProps['control'] 33}) { 34 const {_} = useLingui() 35 return ( 36 <Dialog.Outer control={control}> 37 <Dialog.Handle /> 38 39 <Dialog.ScrollableInner 40 label={_(msg`Confirm your location`)} 41 style={[web({maxWidth: 380})]}> 42 <DeviceLocationRequestDialogInner 43 onLocationAcquired={onLocationAcquired} 44 /> 45 <Dialog.Close /> 46 </Dialog.ScrollableInner> 47 </Dialog.Outer> 48 ) 49} 50 51function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) { 52 const t = useTheme() 53 const {_} = useLingui() 54 const {close} = Dialog.useDialogContext() 55 const requestDeviceLocation = useRequestDeviceGeolocation() 56 const cleanError = useCleanError() 57 58 const [isRequesting, setIsRequesting] = useState(false) 59 const [error, setError] = useState<string>('') 60 const [dialogDisabled, setDialogDisabled] = useState(false) 61 62 const onPressConfirm = async () => { 63 setError('') 64 setIsRequesting(true) 65 66 try { 67 const req = await wait(1e3, requestDeviceLocation()) 68 69 if (req.granted) { 70 const location = req.location 71 72 if (location && location.countryCode) { 73 onLocationAcquired?.({ 74 geolocation: location, 75 setDialogError: setError, 76 disableDialogAction: () => setDialogDisabled(true), 77 closeDialog: close, 78 }) 79 } else { 80 setError(_(msg`Failed to resolve location. Please try again.`)) 81 } 82 } else { 83 setError( 84 _( 85 msg`Unable to access location. You'll need to visit your system settings to enable location services for Bluesky.`, 86 ), 87 ) 88 } 89 } catch (e: any) { 90 const {clean, raw} = cleanError(e) 91 setError(clean || raw || e.message) 92 if (!isNetworkError(e)) { 93 logger.error(`blockedGeoOverlay: unexpected error`, { 94 safeMessage: e.message, 95 }) 96 } 97 } finally { 98 setIsRequesting(false) 99 } 100 } 101 102 return ( 103 <View style={[a.gap_md]}> 104 <Text style={[a.text_xl, a.font_bold]}> 105 <Trans>Confirm your location</Trans> 106 </Text> 107 <View style={[a.gap_sm, a.pb_xs]}> 108 <Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}> 109 <Trans> 110 Tap below to allow Bluesky to access your GPS location. We will then 111 use that data to more accurately determine the content and features 112 available in your region. 113 </Trans> 114 </Text> 115 116 <Text 117 style={[ 118 a.text_md, 119 a.leading_snug, 120 t.atoms.text_contrast_medium, 121 a.pb_xs, 122 ]}> 123 <Trans> 124 Your location data is not tracked and does not leave your device. 125 </Trans> 126 </Text> 127 </View> 128 129 {error && ( 130 <View style={[a.pb_xs]}> 131 <Admonition type="error">{error}</Admonition> 132 </View> 133 )} 134 135 <View style={[a.gap_sm]}> 136 {!dialogDisabled && ( 137 <Button 138 disabled={isRequesting} 139 label={_(msg`Allow location access`)} 140 onPress={onPressConfirm} 141 size={isWeb ? 'small' : 'large'} 142 color="primary"> 143 <ButtonIcon icon={isRequesting ? Loader : LocationIcon} /> 144 <ButtonText> 145 <Trans>Allow location access</Trans> 146 </ButtonText> 147 </Button> 148 )} 149 150 {!isWeb && ( 151 <Button 152 label={_(msg`Cancel`)} 153 onPress={() => close()} 154 size={isWeb ? 'small' : 'large'} 155 color="secondary"> 156 <ButtonText> 157 <Trans>Cancel</Trans> 158 </ButtonText> 159 </Button> 160 )} 161 </View> 162 </View> 163 ) 164}