import {useCallback, useEffect} from 'react' import {ScrollView, View} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import { SupportCode, useCreateSupportLink, } from '#/lib/hooks/useCreateSupportLink' import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {isAppPassword} from '#/lib/jwt' import {logger} from '#/logger' import {isWeb} from '#/platform/detection' import {isNative} from '#/platform/detection' import {useIsBirthdateUpdateAllowed} from '#/state/birthdate' import {useSession, useSessionApi} from '#/state/session' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {Admonition} from '#/components/Admonition' import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {AgeAssuranceInitDialog} from '#/components/ageAssurance/AgeAssuranceInitDialog' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' import * as Dialog from '#/components/Dialog' import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' import {DeviceLocationRequestDialog} from '#/components/dialogs/DeviceLocationRequestDialog' import {Full as Logo} from '#/components/icons/Logo' import {ShieldCheck_Stroke2_Corner0_Rounded as ShieldIcon} from '#/components/icons/Shield' import {createStaticClick, SimpleInlineLinkText} from '#/components/Link' import {Outlet as PortalOutlet} from '#/components/Portal' import * as Toast from '#/components/Toast' import {Span, Text} from '#/components/Typography' import {BottomSheetOutlet} from '#/../modules/bottom-sheet' import {useAgeAssurance} from '#/ageAssurance' import {useAgeAssuranceDataContext} from '#/ageAssurance/data' import {useComputeAgeAssuranceRegionAccess} from '#/ageAssurance/useComputeAgeAssuranceRegionAccess' import { isLegacyBirthdateBug, useAgeAssuranceRegionConfig, } from '#/ageAssurance/util' import {useDeviceGeolocationApi} from '#/geolocation' const textStyles = [a.text_md, a.leading_snug] export function NoAccessScreen() { const t = useTheme() const {_} = useLingui() const {gtPhone} = useBreakpoints() const insets = useSafeAreaInsets() const birthdateControl = useDialogControl() const {data} = useAgeAssuranceDataContext() const region = useAgeAssuranceRegionConfig() const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed() const {logoutCurrentAccount} = useSessionApi() const createSupportLink = useCreateSupportLink() const {currentAccount} = useSession() const isUsingAppPassword = isAppPassword(currentAccount?.accessJwt || '') const aa = useAgeAssurance() const isBlocked = aa.state.status === aa.Status.Blocked const isAARegion = !!region const hasDeclaredAge = data?.declaredAge !== undefined const canUpdateBirthday = isBirthdateUpdateAllowed || isLegacyBirthdateBug(data?.birthdate || '') useEffect(() => { // just counting overall hits here logger.metric(`blockedGeoOverlay:shown`, {}) }, []) const onPressLogout = useCallback(() => { if (isWeb) { // We're switching accounts, which remounts the entire app. // On mobile, this gets us Home, but on the web we also need reset the URL. // We can't change the URL via a navigate() call because the navigator // itself is about to unmount, and it calls pushState() too late. // So we change the URL ourselves. The navigator will pick it up on remount. history.pushState(null, '', '/') } logoutCurrentAccount('AgeAssuranceNoAccessScreen') }, [logoutCurrentAccount]) const birthdateUpdateText = canUpdateBirthday ? ( If you believe your birthdate is incorrect, you can update it by{' '} { birthdateControl.open() })}> clicking here . ) : ( If you believe your birthdate is incorrect, please{' '} contact our support team . ) return ( <> {hasDeclaredAge ? ( <> {isAARegion ? ( <> Hey there! You are accessing Bluesky from a region that legally requires us to verify your age before allowing you to access the app. {!isBlocked && birthdateUpdateText} ) : ( Unfortunately, the birthdate you have saved to your profile makes you too young to access Bluesky. {birthdateUpdateText} )} ) : ( Hi there! In order to provide an age-appropriate experience, we need to know your birthdate. This is a one-time thing, and your data will be kept private. Set your birthdate below and we'll get you back to posting and exploring in no time! {isUsingAppPassword && ( Hmm, it looks like you're logged in with an{' '} App Password. To set your birthdate, you'll need to log in with your main account password, or ask whomever controls this account to do so. )} )} To log out,{' '} { onPressLogout() })}> click here . {/* * While this blocking overlay is up, other dialogs in the shell * are not mounted, so it _should_ be safe to use these here * without fear of other modals showing up. */} ) } function AccessSection() { const t = useTheme() const {_, i18n} = useLingui() const control = useDialogControl() const appealControl = Dialog.useDialogControl() const locationControl = Dialog.useDialogControl() const getTimeAgo = useGetTimeAgo() const {setDeviceGeolocation} = useDeviceGeolocationApi() const computeAgeAssuranceRegionAccess = useComputeAgeAssuranceRegionAccess() const aa = useAgeAssurance() const {status, lastInitiatedAt} = aa.state const isBlocked = status === aa.Status.Blocked const hasInitiated = !!lastInitiatedAt const timeAgo = lastInitiatedAt ? getTimeAgo(lastInitiatedAt, new Date()) : null const diff = lastInitiatedAt ? dateDiff(lastInitiatedAt, new Date(), 'down') : null return ( <> {isBlocked ? ( You are currently unable to access Bluesky's Age Assurance flow. Please{' '} { appealControl.open() logger.metric('ageAssurance:appealDialogOpen', {}) })}> contact our moderation team {' '} if you believe this is an error. ) : ( <> {lastInitiatedAt && timeAgo && diff ? ( {diff.value === 0 ? ( Last initiated just now ) : ( Last initiated {timeAgo} ago )} ) : ( Age assurance only takes a few minutes )} )} {isNative && ( <> Is your location not accurate?{' '} { locationControl.open() })}> Tap here to confirm your location. {' '} { 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', }) }) } }} /> )} ) }