Gate age signals by platform and version
This commit is contained in:
@@ -45,7 +45,6 @@ import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAs
|
|||||||
import {
|
import {
|
||||||
getAgeAssuranceDataFromDeviceSignals,
|
getAgeAssuranceDataFromDeviceSignals,
|
||||||
isLegacyBirthdateBug,
|
isLegacyBirthdateBug,
|
||||||
regionAllowsDeviceVerification,
|
|
||||||
useAgeAssuranceRegionConfig,
|
useAgeAssuranceRegionConfig,
|
||||||
} from '#/ageAssurance/util'
|
} from '#/ageAssurance/util'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
@@ -330,8 +329,7 @@ function AccessSection() {
|
|||||||
const diff = lastInitiatedAt
|
const diff = lastInitiatedAt
|
||||||
? dateDiff(lastInitiatedAt, new Date(), 'down')
|
? dateDiff(lastInitiatedAt, new Date(), 'down')
|
||||||
: null
|
: null
|
||||||
const allowsDeviceVerification =
|
const allowsDeviceVerification = region && aa.flags.allowsDeviceVerification
|
||||||
region && regionAllowsDeviceVerification(region)
|
|
||||||
const verifyCta = hasInitiated
|
const verifyCta = hasInitiated
|
||||||
? _(msg`Verify again`)
|
? _(msg`Verify again`)
|
||||||
: allowsDeviceVerification
|
: allowsDeviceVerification
|
||||||
|
|||||||
@@ -4,12 +4,29 @@ import {
|
|||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
|
||||||
import {AgeAssuranceAccess} from '#/ageAssurance/types'
|
import {AgeAssuranceAccess} from '#/ageAssurance/types'
|
||||||
|
import {ANDROID_API_LEVEL, IOS_MAJOR_VERSION, IS_ANDROID, IS_IOS} from '#/env'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum age required to access the app at all.
|
* Minimum age required to access the app at all.
|
||||||
*/
|
*/
|
||||||
export const MIN_ACCESS_AGE = 13
|
export const MIN_ACCESS_AGE = 13
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current device can provide the native on-device age signals we
|
||||||
|
* use for age assurance (via `expo-age-range`). We gate on OS version because
|
||||||
|
* the underlying platform APIs are only available on:
|
||||||
|
*
|
||||||
|
* - iOS 26.0+ (Declared Age Range API)
|
||||||
|
* https://developer.apple.com/documentation/declaredagerange/
|
||||||
|
* - Android 6.0 / API level 23+ (Play Age Signals API)
|
||||||
|
* https://developer.android.com/google/play/age-signals/use-age-signals-api
|
||||||
|
*
|
||||||
|
* On unsupported platforms/versions (including web) this is `false`, so we skip
|
||||||
|
* the device flow and fall back to KWS.
|
||||||
|
*/
|
||||||
|
export const DEVICE_SIGNALS_SUPPORTED: boolean =
|
||||||
|
(IS_IOS && IOS_MAJOR_VERSION >= 26) || (IS_ANDROID && ANDROID_API_LEVEL >= 23)
|
||||||
|
|
||||||
export const FALLBACK_REGION_CONFIG: AppBskyAgeassuranceDefs.ConfigRegion = {
|
export const FALLBACK_REGION_CONFIG: AppBskyAgeassuranceDefs.ConfigRegion = {
|
||||||
countryCode: '*',
|
countryCode: '*',
|
||||||
regionCode: undefined,
|
regionCode: undefined,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
} from '#/state/birthdate'
|
} from '#/state/birthdate'
|
||||||
import {fetchActorDeclarationRecord} from '#/state/queries/messages/actor-declaration'
|
import {fetchActorDeclarationRecord} from '#/state/queries/messages/actor-declaration'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
|
import {DEVICE_SIGNALS_SUPPORTED} from '#/ageAssurance/const'
|
||||||
import * as debug from '#/ageAssurance/debug'
|
import * as debug from '#/ageAssurance/debug'
|
||||||
import {logger} from '#/ageAssurance/logger'
|
import {logger} from '#/ageAssurance/logger'
|
||||||
import {
|
import {
|
||||||
@@ -34,7 +35,7 @@ import {
|
|||||||
getBirthdateStringFromAge,
|
getBirthdateStringFromAge,
|
||||||
isLegacyBirthdateBug,
|
isLegacyBirthdateBug,
|
||||||
} from '#/ageAssurance/util'
|
} from '#/ageAssurance/util'
|
||||||
import {IS_DEV, IS_NATIVE} from '#/env'
|
import {IS_DEV} from '#/env'
|
||||||
import {useGeolocation} from '#/geolocation'
|
import {useGeolocation} from '#/geolocation'
|
||||||
import {device} from '#/storage'
|
import {device} from '#/storage'
|
||||||
|
|
||||||
@@ -506,7 +507,7 @@ export async function getDeviceSignals(): Promise<
|
|||||||
> {
|
> {
|
||||||
if (debug.enabled && debug.useMockDeviceSignalsAPIResponse)
|
if (debug.enabled && debug.useMockDeviceSignalsAPIResponse)
|
||||||
return debug.resolve(debug.deviceSignals)
|
return debug.resolve(debug.deviceSignals)
|
||||||
if (!IS_NATIVE) return undefined
|
if (!DEVICE_SIGNALS_SUPPORTED) return undefined
|
||||||
try {
|
try {
|
||||||
return await AgeRange.requestAgeRangeAsync({
|
return await AgeRange.requestAgeRangeAsync({
|
||||||
threshold1: 13,
|
threshold1: 13,
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import {
|
|||||||
|
|
||||||
import {getAge} from '#/lib/strings/time'
|
import {getAge} from '#/lib/strings/time'
|
||||||
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/const'
|
import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/const'
|
||||||
import {FALLBACK_REGION_CONFIG, MIN_ACCESS_AGE} from '#/ageAssurance/const'
|
import {
|
||||||
|
DEVICE_SIGNALS_SUPPORTED,
|
||||||
|
FALLBACK_REGION_CONFIG,
|
||||||
|
MIN_ACCESS_AGE,
|
||||||
|
} from '#/ageAssurance/const'
|
||||||
import {useAgeAssuranceServerDataContext} from '#/ageAssurance/data'
|
import {useAgeAssuranceServerDataContext} from '#/ageAssurance/data'
|
||||||
import {
|
import {
|
||||||
AgeAssuranceAccess,
|
AgeAssuranceAccess,
|
||||||
@@ -16,7 +20,6 @@ import {
|
|||||||
type AgeAssuranceMetadata,
|
type AgeAssuranceMetadata,
|
||||||
type AgeAssuranceState,
|
type AgeAssuranceState,
|
||||||
} from '#/ageAssurance/types'
|
} from '#/ageAssurance/types'
|
||||||
import {IS_WEB} from '#/env'
|
|
||||||
import {type Geolocation, useGeolocation} from '#/geolocation'
|
import {type Geolocation, useGeolocation} from '#/geolocation'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,7 +192,7 @@ export function computeAgeAssuranceFlags({
|
|||||||
const adultContentDisabled =
|
const adultContentDisabled =
|
||||||
state.access !== AgeAssuranceAccess.Full || isDeclaredUnderAdultAge
|
state.access !== AgeAssuranceAccess.Full || isDeclaredUnderAdultAge
|
||||||
const allowsDeviceVerification =
|
const allowsDeviceVerification =
|
||||||
regionAllowsDeviceVerification(regionConfig) && !IS_WEB
|
DEVICE_SIGNALS_SUPPORTED && regionAllowsDeviceVerification(regionConfig)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAgeRestricted,
|
isAgeRestricted,
|
||||||
|
|||||||
Vendored
+16
-6
@@ -5,12 +5,23 @@ import {BUNDLE_IDENTIFIER, IS_TESTFLIGHT, RELEASE_VERSION} from '#/env/common'
|
|||||||
|
|
||||||
export * from '#/env/common'
|
export * from '#/env/common'
|
||||||
|
|
||||||
// for some reason Platform.OS === 'ios' AND Platform.Version is undefined in our CI unit tests -sfn
|
/**
|
||||||
const iOSMajorVersion =
|
* The major version number of the current iOS device (e.g. `26` for iOS 26.x),
|
||||||
|
* or `0` on non-iOS platforms.
|
||||||
|
*
|
||||||
|
* Note: for some reason Platform.OS === 'ios' AND Platform.Version is undefined
|
||||||
|
* in our CI unit tests -sfn
|
||||||
|
*/
|
||||||
|
export const IOS_MAJOR_VERSION: number =
|
||||||
Platform.OS === 'ios' && typeof Platform.Version === 'string'
|
Platform.OS === 'ios' && typeof Platform.Version === 'string'
|
||||||
? parseInt(Platform.Version.split('.')[0], 10)
|
? parseInt(Platform.Version.split('.')[0], 10)
|
||||||
: 0
|
: 0
|
||||||
const androidPlatformVersion =
|
/**
|
||||||
|
* The Android API level of the current device (e.g. `23` for Android 6.0), or
|
||||||
|
* `0` on non-Android platforms. Note this is the API level, not the marketing
|
||||||
|
* version.
|
||||||
|
*/
|
||||||
|
export const ANDROID_API_LEVEL: number =
|
||||||
Platform.OS === 'android' && typeof Platform.Version === 'number'
|
Platform.OS === 'android' && typeof Platform.Version === 'number'
|
||||||
? Platform.Version
|
? Platform.Version
|
||||||
: 0
|
: 0
|
||||||
@@ -52,8 +63,7 @@ export const IS_WEB_FIREFOX: boolean = false
|
|||||||
*/
|
*/
|
||||||
export const IS_HIGH_DPI: boolean = true
|
export const IS_HIGH_DPI: boolean = true
|
||||||
// ideally we'd use isLiquidGlassAvailable() from expo-glass-effect but checking iOS version is good enough for now
|
// ideally we'd use isLiquidGlassAvailable() from expo-glass-effect but checking iOS version is good enough for now
|
||||||
export const IS_LIQUID_GLASS: boolean = iOSMajorVersion >= 26
|
export const IS_LIQUID_GLASS: boolean = IOS_MAJOR_VERSION >= 26
|
||||||
// So we can avoid attempting on-device translation when we know it's unsupported.
|
// So we can avoid attempting on-device translation when we know it's unsupported.
|
||||||
export const IS_TRANSLATION_SUPPORTED: boolean =
|
export const IS_TRANSLATION_SUPPORTED: boolean =
|
||||||
(IS_IOS && iOSMajorVersion >= 18) ||
|
(IS_IOS && IOS_MAJOR_VERSION >= 18) || (IS_ANDROID && ANDROID_API_LEVEL > 22)
|
||||||
(IS_ANDROID && androidPlatformVersion > 22)
|
|
||||||
|
|||||||
Vendored
+11
@@ -14,6 +14,17 @@ export const APP_VERSION = RELEASE_VERSION
|
|||||||
*/
|
*/
|
||||||
export const APP_METADATA = `${BUNDLE_IDENTIFIER.slice(0, 7)} (${__DEV__ ? 'dev' : 'prod'})`
|
export const APP_METADATA = `${BUNDLE_IDENTIFIER.slice(0, 7)} (${__DEV__ ? 'dev' : 'prod'})`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The major version number of the current iOS device, or `0` on non-iOS
|
||||||
|
* platforms (always `0` on web).
|
||||||
|
*/
|
||||||
|
export const IOS_MAJOR_VERSION: number = 0
|
||||||
|
/**
|
||||||
|
* The Android API level of the current device, or `0` on non-Android platforms
|
||||||
|
* (always `0` on web).
|
||||||
|
*/
|
||||||
|
export const ANDROID_API_LEVEL: number = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform detection
|
* Platform detection
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user