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