c4aef9f668
* Age Assurance V2 * Tighten up test * Add todos for sdk migration * Align RQ versions * Use useEffect for side effect * Improve effects, memoize * Standarize on birthdate * Copy feedback * Copilot * Add support link * Reove double .. * Cleanup * Remove redirect dialog * Cleanup todos, add comments * Update splash in main template too * Mock some stuff * Exhaustive checks Co-authored-by: Samuel Newman <mozzius@protonmail.com> * Exhaustive checks Co-authored-by: Samuel Newman <mozzius@protonmail.com> * Small fix to bday handling * Add comment * onboarding style tweak sneaking this in sorry! * rm unreachable breaks * Put useIntentHandler back on web * Remove misleading success set * Align on birthdate --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
165 lines
4.8 KiB
TypeScript
165 lines
4.8 KiB
TypeScript
import {useState} from 'react'
|
|
import {View} from 'react-native'
|
|
import {msg, Trans} from '@lingui/macro'
|
|
import {useLingui} from '@lingui/react'
|
|
|
|
import {wait} from '#/lib/async/wait'
|
|
import {isNetworkError, useCleanError} from '#/lib/hooks/useCleanError'
|
|
import {logger} from '#/logger'
|
|
import {isWeb} from '#/platform/detection'
|
|
import {atoms as a, useTheme, web} from '#/alf'
|
|
import {Admonition} from '#/components/Admonition'
|
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
|
import * as Dialog from '#/components/Dialog'
|
|
import {PinLocation_Stroke2_Corner0_Rounded as LocationIcon} from '#/components/icons/PinLocation'
|
|
import {Loader} from '#/components/Loader'
|
|
import {Text} from '#/components/Typography'
|
|
import {type Geolocation, useRequestDeviceGeolocation} from '#/geolocation'
|
|
|
|
export type Props = {
|
|
onLocationAcquired?: (props: {
|
|
geolocation: Geolocation
|
|
setDialogError: (error: string) => void
|
|
disableDialogAction: () => void
|
|
closeDialog: (callback?: () => void) => void
|
|
}) => void
|
|
}
|
|
|
|
export function DeviceLocationRequestDialog({
|
|
control,
|
|
onLocationAcquired,
|
|
}: Props & {
|
|
control: Dialog.DialogOuterProps['control']
|
|
}) {
|
|
const {_} = useLingui()
|
|
return (
|
|
<Dialog.Outer control={control}>
|
|
<Dialog.Handle />
|
|
|
|
<Dialog.ScrollableInner
|
|
label={_(msg`Confirm your location`)}
|
|
style={[web({maxWidth: 380})]}>
|
|
<DeviceLocationRequestDialogInner
|
|
onLocationAcquired={onLocationAcquired}
|
|
/>
|
|
<Dialog.Close />
|
|
</Dialog.ScrollableInner>
|
|
</Dialog.Outer>
|
|
)
|
|
}
|
|
|
|
function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
|
|
const t = useTheme()
|
|
const {_} = useLingui()
|
|
const {close} = Dialog.useDialogContext()
|
|
const requestDeviceLocation = useRequestDeviceGeolocation()
|
|
const cleanError = useCleanError()
|
|
|
|
const [isRequesting, setIsRequesting] = useState(false)
|
|
const [error, setError] = useState<string>('')
|
|
const [dialogDisabled, setDialogDisabled] = useState(false)
|
|
|
|
const onPressConfirm = async () => {
|
|
setError('')
|
|
setIsRequesting(true)
|
|
|
|
try {
|
|
const req = await wait(1e3, requestDeviceLocation())
|
|
|
|
if (req.granted) {
|
|
const location = req.location
|
|
|
|
if (location && location.countryCode) {
|
|
onLocationAcquired?.({
|
|
geolocation: location,
|
|
setDialogError: setError,
|
|
disableDialogAction: () => setDialogDisabled(true),
|
|
closeDialog: close,
|
|
})
|
|
} else {
|
|
setError(_(msg`Failed to resolve location. Please try again.`))
|
|
}
|
|
} else {
|
|
setError(
|
|
_(
|
|
msg`Unable to access location. You'll need to visit your system settings to enable location services for Bluesky.`,
|
|
),
|
|
)
|
|
}
|
|
} catch (e: any) {
|
|
const {clean, raw} = cleanError(e)
|
|
setError(clean || raw || e.message)
|
|
if (!isNetworkError(e)) {
|
|
logger.error(`blockedGeoOverlay: unexpected error`, {
|
|
safeMessage: e.message,
|
|
})
|
|
}
|
|
} finally {
|
|
setIsRequesting(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<View style={[a.gap_md]}>
|
|
<Text style={[a.text_xl, a.font_bold]}>
|
|
<Trans>Confirm your location</Trans>
|
|
</Text>
|
|
<View style={[a.gap_sm, a.pb_xs]}>
|
|
<Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
|
|
<Trans>
|
|
Tap below to allow Bluesky to access your GPS location. We will then
|
|
use that data to more accurately determine the content and features
|
|
available in your region.
|
|
</Trans>
|
|
</Text>
|
|
|
|
<Text
|
|
style={[
|
|
a.text_md,
|
|
a.leading_snug,
|
|
t.atoms.text_contrast_medium,
|
|
a.pb_xs,
|
|
]}>
|
|
<Trans>
|
|
Your location data is not tracked and does not leave your device.
|
|
</Trans>
|
|
</Text>
|
|
</View>
|
|
|
|
{error && (
|
|
<View style={[a.pb_xs]}>
|
|
<Admonition type="error">{error}</Admonition>
|
|
</View>
|
|
)}
|
|
|
|
<View style={[a.gap_sm]}>
|
|
{!dialogDisabled && (
|
|
<Button
|
|
disabled={isRequesting}
|
|
label={_(msg`Allow location access`)}
|
|
onPress={onPressConfirm}
|
|
size={isWeb ? 'small' : 'large'}
|
|
color="primary">
|
|
<ButtonIcon icon={isRequesting ? Loader : LocationIcon} />
|
|
<ButtonText>
|
|
<Trans>Allow location access</Trans>
|
|
</ButtonText>
|
|
</Button>
|
|
)}
|
|
|
|
{!isWeb && (
|
|
<Button
|
|
label={_(msg`Cancel`)}
|
|
onPress={() => close()}
|
|
size={isWeb ? 'small' : 'large'}
|
|
color="secondary">
|
|
<ButtonText>
|
|
<Trans>Cancel</Trans>
|
|
</ButtonText>
|
|
</Button>
|
|
)}
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|