Add location to NoAccessScreen, move around GPS request
This commit is contained in:
@@ -43,19 +43,20 @@ import {logger} from '#/ageAssurance/logger'
|
|||||||
import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state'
|
import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state'
|
||||||
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
||||||
import {
|
import {
|
||||||
|
createGeolocationString,
|
||||||
getAgeAssuranceDataFromDeviceSignals,
|
getAgeAssuranceDataFromDeviceSignals,
|
||||||
isLegacyBirthdateBug,
|
isLegacyBirthdateBug,
|
||||||
useAgeAssuranceRegionConfig,
|
useAgeAssuranceRegionConfig,
|
||||||
} from '#/ageAssurance/util'
|
} from '#/ageAssurance/util'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||||
import {useDeviceGeolocationApi} from '#/geolocation'
|
import {useDeviceGeolocationApi, useGeolocation} from '#/geolocation'
|
||||||
|
|
||||||
const textStyles = [a.text_md, a.leading_snug]
|
const textStyles = [a.text_md, a.leading_snug]
|
||||||
|
|
||||||
export function NoAccessScreen() {
|
export function NoAccessScreen() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const {gtPhone} = useBreakpoints()
|
const {gtPhone} = useBreakpoints()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
@@ -67,6 +68,10 @@ export function NoAccessScreen() {
|
|||||||
const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed()
|
const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed()
|
||||||
const {logoutCurrentAccount} = useSessionApi()
|
const {logoutCurrentAccount} = useSessionApi()
|
||||||
const createSupportLink = useCreateSupportLink()
|
const createSupportLink = useCreateSupportLink()
|
||||||
|
const geolocation = useGeolocation()
|
||||||
|
const {setDeviceGeolocation} = useDeviceGeolocationApi()
|
||||||
|
const locationControl = Dialog.useDialogControl()
|
||||||
|
const computeAgeAssuranceRegionAccess = useComputeAgeAssuranceRegionAccess()
|
||||||
|
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
const isBlocked = aa.state.status === aa.Status.Blocked
|
const isBlocked = aa.state.status === aa.Status.Blocked
|
||||||
@@ -74,6 +79,8 @@ export function NoAccessScreen() {
|
|||||||
const hasDeclaredAge = aa.flags.hasDeclaredAge
|
const hasDeclaredAge = aa.flags.hasDeclaredAge
|
||||||
const canUpdateBirthday =
|
const canUpdateBirthday =
|
||||||
isBirthdateUpdateAllowed || isLegacyBirthdateBug(metadata?.birthdate || '')
|
isBirthdateUpdateAllowed || isLegacyBirthdateBug(metadata?.birthdate || '')
|
||||||
|
const geolocationString = createGeolocationString(geolocation, i18n.locale)
|
||||||
|
const isUsingGPS = !!geolocation.deviceGeolocation?.countryCode && IS_NATIVE
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// just counting overall hits here
|
// just counting overall hits here
|
||||||
@@ -189,6 +196,78 @@ export function NoAccessScreen() {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{region && (
|
||||||
|
<Text style={[textStyles]}>
|
||||||
|
{isUsingGPS ? (
|
||||||
|
<Trans>
|
||||||
|
Based on your device's location, we think you're
|
||||||
|
in{' '}
|
||||||
|
<Text style={[textStyles, a.font_bold]}>
|
||||||
|
{geolocationString}
|
||||||
|
</Text>
|
||||||
|
.
|
||||||
|
</Trans>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Trans>
|
||||||
|
Based on your network, we think you're in{' '}
|
||||||
|
<Text style={[textStyles, a.font_bold]}>
|
||||||
|
{geolocationString}
|
||||||
|
</Text>
|
||||||
|
. This estimate may be inaccurate if you're
|
||||||
|
using a VPN.
|
||||||
|
</Trans>{' '}
|
||||||
|
{IS_NATIVE && (
|
||||||
|
<>
|
||||||
|
<Trans>
|
||||||
|
You can also{' '}
|
||||||
|
<SimpleInlineLinkText
|
||||||
|
label={_(msg`Update your location`)}
|
||||||
|
{...createStaticClick(() => {
|
||||||
|
locationControl.open()
|
||||||
|
})}
|
||||||
|
style={[textStyles]}>
|
||||||
|
tap here to update your location with GPS.
|
||||||
|
</SimpleInlineLinkText>
|
||||||
|
</Trans>
|
||||||
|
|
||||||
|
<DeviceLocationRequestDialog
|
||||||
|
control={locationControl}
|
||||||
|
onLocationAcquired={props => {
|
||||||
|
const access =
|
||||||
|
computeAgeAssuranceRegionAccess(
|
||||||
|
props.geolocation,
|
||||||
|
)
|
||||||
|
if (access !== aa.Access.Full) {
|
||||||
|
props.disableDialogAction()
|
||||||
|
props.setDialogError(
|
||||||
|
_(
|
||||||
|
msg`We're sorry, but based on your device's location, you are currently located in a region that requires age assurance.`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
props.closeDialog(() => {
|
||||||
|
// set this after close!
|
||||||
|
setDeviceGeolocation(
|
||||||
|
props.geolocation,
|
||||||
|
)
|
||||||
|
Toast.show(
|
||||||
|
_(msg`Thanks! You're all set.`),
|
||||||
|
{
|
||||||
|
type: 'success',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
{!aa.flags.isOverRegionMinAccessAge && (
|
{!aa.flags.isOverRegionMinAccessAge && (
|
||||||
<Text style={[textStyles]}>
|
<Text style={[textStyles]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -312,10 +391,7 @@ function AccessSection() {
|
|||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const control = useDialogControl()
|
const control = useDialogControl()
|
||||||
const appealControl = Dialog.useDialogControl()
|
const appealControl = Dialog.useDialogControl()
|
||||||
const locationControl = Dialog.useDialogControl()
|
|
||||||
const getTimeAgo = useGetTimeAgo()
|
const getTimeAgo = useGetTimeAgo()
|
||||||
const {setDeviceGeolocation} = useDeviceGeolocationApi()
|
|
||||||
const computeAgeAssuranceRegionAccess = useComputeAgeAssuranceRegionAccess()
|
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const region = useAgeAssuranceRegionConfig()
|
const region = useAgeAssuranceRegionConfig()
|
||||||
|
|
||||||
@@ -505,50 +581,6 @@ function AccessSection() {
|
|||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View style={[a.gap_xs]}>
|
|
||||||
{IS_NATIVE && (
|
|
||||||
<>
|
|
||||||
<Admonition>
|
|
||||||
<Trans>
|
|
||||||
Is your location not accurate?{' '}
|
|
||||||
<SimpleInlineLinkText
|
|
||||||
label={_(msg`Update your location`)}
|
|
||||||
{...createStaticClick(() => {
|
|
||||||
locationControl.open()
|
|
||||||
})}>
|
|
||||||
Tap here to update your location with GPS.
|
|
||||||
</SimpleInlineLinkText>
|
|
||||||
</Trans>
|
|
||||||
</Admonition>
|
|
||||||
|
|
||||||
<DeviceLocationRequestDialog
|
|
||||||
control={locationControl}
|
|
||||||
onLocationAcquired={props => {
|
|
||||||
const access = computeAgeAssuranceRegionAccess(
|
|
||||||
props.geolocation,
|
|
||||||
)
|
|
||||||
if (access !== aa.Access.Full) {
|
|
||||||
props.disableDialogAction()
|
|
||||||
props.setDialogError(
|
|
||||||
_(
|
|
||||||
msg`We're sorry, but based on your device's location, you are currently located in a region that requires age assurance.`,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
props.closeDialog(() => {
|
|
||||||
// set this after close!
|
|
||||||
setDeviceGeolocation(props.geolocation)
|
|
||||||
Toast.show(_(msg`Thanks! You're all set.`), {
|
|
||||||
type: 'success',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
+10
-12
@@ -9,24 +9,22 @@ import {type OtherRequiredData} from '#/ageAssurance/data'
|
|||||||
import {IS_DEV, IS_E2E} from '#/env'
|
import {IS_DEV, IS_E2E} from '#/env'
|
||||||
import {type Geolocation} from '#/geolocation'
|
import {type Geolocation} from '#/geolocation'
|
||||||
|
|
||||||
export const enabled = (IS_DEV && false) || IS_E2E
|
export const enabled = (IS_DEV && true) || IS_E2E
|
||||||
|
|
||||||
export const geolocation: Geolocation | undefined = enabled
|
export const geolocation: Geolocation | undefined = enabled
|
||||||
? {
|
? {
|
||||||
countryCode: 'AA',
|
countryCode: 'US',
|
||||||
regionCode: undefined,
|
regionCode: 'TX',
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const deviceGeolocationEnabled = false || IS_E2E
|
export const deviceGeolocation: Geolocation | undefined = enabled
|
||||||
export const deviceGeolocation: Geolocation | undefined =
|
? {
|
||||||
enabled && deviceGeolocationEnabled
|
countryCode: 'AA',
|
||||||
? {
|
regionCode: undefined,
|
||||||
countryCode: 'AA',
|
...geolocation,
|
||||||
regionCode: undefined,
|
}
|
||||||
...geolocation,
|
: undefined
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
export const otherRequiredData: OtherRequiredData = {
|
export const otherRequiredData: OtherRequiredData = {
|
||||||
birthdate: new Date(2000, 12, 1).toISOString(),
|
birthdate: new Date(2000, 12, 1).toISOString(),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
|
||||||
import {getAge} from '#/lib/strings/time'
|
import {getAge} from '#/lib/strings/time'
|
||||||
|
import {regionName} from '#/locale/helpers'
|
||||||
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/const'
|
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/const'
|
||||||
import {
|
import {
|
||||||
DEVICE_SIGNALS_SUPPORTED,
|
DEVICE_SIGNALS_SUPPORTED,
|
||||||
@@ -21,6 +22,7 @@ import {
|
|||||||
type AgeAssuranceState,
|
type AgeAssuranceState,
|
||||||
} from '#/ageAssurance/types'
|
} from '#/ageAssurance/types'
|
||||||
import {type Geolocation, useGeolocation} from '#/geolocation'
|
import {type Geolocation, useGeolocation} from '#/geolocation'
|
||||||
|
import {USRegionNameToRegionCode} from '#/geolocation/util'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get age assurance region config based on geolocation, with fallback to
|
* Get age assurance region config based on geolocation, with fallback to
|
||||||
@@ -206,3 +208,27 @@ export function computeAgeAssuranceFlags({
|
|||||||
allowsDeviceVerification,
|
allowsDeviceVerification,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const USRegionCodeToRegionName: {[regionCode: string]: string} =
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(USRegionNameToRegionCode).map(([name, code]) => [
|
||||||
|
code,
|
||||||
|
name,
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
export function createGeolocationString(
|
||||||
|
geolocation: Geolocation,
|
||||||
|
appLang: string,
|
||||||
|
): string | undefined {
|
||||||
|
const {countryCode, regionCode} = geolocation
|
||||||
|
if (!countryCode) return undefined
|
||||||
|
const country = regionName(countryCode, appLang)
|
||||||
|
// If `regionName` couldn't resolve a real name and fell through to the raw
|
||||||
|
// code, we'd rather show nothing than a bare ISO code in the prose.
|
||||||
|
if (country === countryCode) return undefined
|
||||||
|
if (regionCode && countryCode === 'US') {
|
||||||
|
const state = USRegionCodeToRegionName[regionCode]
|
||||||
|
if (state) return `${state}, ${country}`
|
||||||
|
}
|
||||||
|
return country
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {View} from 'react-native'
|
|||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
||||||
import {regionName} from '#/locale/helpers'
|
|
||||||
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
|
||||||
import {Admonition} from '#/components/Admonition'
|
import {Admonition} from '#/components/Admonition'
|
||||||
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
|
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
|
||||||
@@ -22,40 +21,10 @@ import * as Toast from '#/components/Toast'
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess'
|
||||||
|
import {createGeolocationString} from '#/ageAssurance/util'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {IS_NATIVE} from '#/env'
|
import {IS_NATIVE} from '#/env'
|
||||||
import {
|
import {useDeviceGeolocationApi, useGeolocation} from '#/geolocation'
|
||||||
type Geolocation,
|
|
||||||
useDeviceGeolocationApi,
|
|
||||||
useGeolocation,
|
|
||||||
} from '#/geolocation'
|
|
||||||
import {USRegionNameToRegionCode} from '#/geolocation/util'
|
|
||||||
import {device, useStorage} from '#/storage'
|
|
||||||
|
|
||||||
const USRegionCodeToRegionName: {[regionCode: string]: string} =
|
|
||||||
Object.fromEntries(
|
|
||||||
Object.entries(USRegionNameToRegionCode).map(([name, code]) => [
|
|
||||||
code,
|
|
||||||
name,
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
function formatRegion(
|
|
||||||
geolocation: Geolocation,
|
|
||||||
appLang: string,
|
|
||||||
): string | undefined {
|
|
||||||
const {countryCode, regionCode} = geolocation
|
|
||||||
if (!countryCode) return undefined
|
|
||||||
const country = regionName(countryCode, appLang)
|
|
||||||
// If `regionName` couldn't resolve a real name and fell through to the raw
|
|
||||||
// code, we'd rather show nothing than a bare ISO code in the prose.
|
|
||||||
if (country === countryCode) return undefined
|
|
||||||
if (regionCode && countryCode === 'US') {
|
|
||||||
const state = USRegionCodeToRegionName[regionCode]
|
|
||||||
if (state) return `${state}, ${country}`
|
|
||||||
}
|
|
||||||
return country
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) {
|
export function AgeAssuranceAccountCard({style}: ViewStyleProp & {}) {
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
@@ -220,13 +189,12 @@ function RegionNotice() {
|
|||||||
const {t: l, i18n} = useLingui()
|
const {t: l, i18n} = useLingui()
|
||||||
const aa = useAgeAssurance()
|
const aa = useAgeAssurance()
|
||||||
const geolocation = useGeolocation()
|
const geolocation = useGeolocation()
|
||||||
const [deviceGeolocation] = useStorage(device, ['deviceGeolocation'])
|
|
||||||
const {setDeviceGeolocation} = useDeviceGeolocationApi()
|
const {setDeviceGeolocation} = useDeviceGeolocationApi()
|
||||||
const computeAgeAssuranceRegionAccess = useComputeAgeAssuranceRegionAccess()
|
const computeAgeAssuranceRegionAccess = useComputeAgeAssuranceRegionAccess()
|
||||||
const locationControl = Dialog.useDialogControl()
|
const locationControl = Dialog.useDialogControl()
|
||||||
|
|
||||||
const region = formatRegion(geolocation, i18n.locale)
|
const region = createGeolocationString(geolocation, i18n.locale)
|
||||||
const isGPS = !!deviceGeolocation?.countryCode && IS_NATIVE
|
const isGPS = !!geolocation.deviceGeolocation?.countryCode && IS_NATIVE
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -258,8 +226,7 @@ function RegionNotice() {
|
|||||||
{isGPS ? (
|
{isGPS ? (
|
||||||
<Trans>
|
<Trans>
|
||||||
Based on your device's location, we think you're in{' '}
|
Based on your device's location, we think you're in{' '}
|
||||||
<Text style={[a.text_sm, a.font_bold]}>{region}</Text>. This
|
<Text style={[a.text_sm, a.font_bold]}>{region}</Text>.
|
||||||
estimate may be inaccurate if you're using a VPN.
|
|
||||||
</Trans>
|
</Trans>
|
||||||
) : (
|
) : (
|
||||||
<Trans>
|
<Trans>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export * from '#/geolocation/types'
|
|||||||
const GeolocationContext = createContext<Geolocation>({
|
const GeolocationContext = createContext<Geolocation>({
|
||||||
countryCode: undefined,
|
countryCode: undefined,
|
||||||
regionCode: undefined,
|
regionCode: undefined,
|
||||||
|
serviceGeolocation: undefined,
|
||||||
|
deviceGeolocation: undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
const DeviceGeolocationAPIContext = createContext<{
|
const DeviceGeolocationAPIContext = createContext<{
|
||||||
@@ -44,7 +46,17 @@ export function Provider({children}: {children: ReactNode}) {
|
|||||||
'deviceGeolocation',
|
'deviceGeolocation',
|
||||||
])
|
])
|
||||||
const geolocation = useMemo(() => {
|
const geolocation = useMemo(() => {
|
||||||
return mergeGeolocations(deviceGeolocation, geolocationService)
|
const geo = mergeGeolocations(deviceGeolocation, geolocationService)
|
||||||
|
// create new objects to avoid cyclical references
|
||||||
|
geo.serviceGeolocation = {
|
||||||
|
countryCode: geolocationService?.countryCode,
|
||||||
|
regionCode: geolocationService?.regionCode,
|
||||||
|
}
|
||||||
|
geo.deviceGeolocation = {
|
||||||
|
countryCode: deviceGeolocation?.countryCode,
|
||||||
|
regionCode: deviceGeolocation?.regionCode,
|
||||||
|
}
|
||||||
|
return geo
|
||||||
}, [deviceGeolocation, geolocationService])
|
}, [deviceGeolocation, geolocationService])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export type Geolocation = {
|
export type Geolocation = {
|
||||||
countryCode: string | undefined
|
countryCode: string | undefined
|
||||||
regionCode: string | undefined
|
regionCode: string | undefined
|
||||||
|
serviceGeolocation?: Geolocation
|
||||||
|
deviceGeolocation?: Geolocation
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user