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