Age Assurance V2 (#9479)

* 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>
This commit is contained in:
Eric Bailey
2025-12-04 15:20:00 -06:00
committed by GitHub
parent 7735183af4
commit c4aef9f668
91 changed files with 3016 additions and 1799 deletions
+70 -38
View File
@@ -7,10 +7,13 @@ import {cleanError} from '#/lib/strings/errors'
import {getAge, getDateAgo} from '#/lib/strings/time'
import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import {
useBirthdateMutation,
useIsBirthdateUpdateAllowed,
} from '#/state/birthdate'
import {
usePreferencesQuery,
type UsePreferencesQueryResponse,
usePreferencesSetBirthDateMutation,
} from '#/state/queries/preferences'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {atoms as a, useTheme, web} from '#/alf'
@@ -18,7 +21,7 @@ import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {DateField} from '#/components/forms/DateField'
import {InlineLinkText} from '#/components/Link'
import {SimpleInlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
@@ -30,42 +33,71 @@ export function BirthDateSettingsDialog({
const t = useTheme()
const {_} = useLingui()
const {isLoading, error, data: preferences} = usePreferencesQuery()
const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed()
return (
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`My Birthday`)}
style={web({maxWidth: 400})}>
<View style={[a.gap_sm]}>
<Text style={[a.text_xl, a.font_semi_bold]}>
<Trans>My Birthday</Trans>
</Text>
<Text style={[a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
This information is private and not shared with other users.
</Trans>
</Text>
{isBirthdateUpdateAllowed ? (
<Dialog.ScrollableInner
label={_(msg`My Birthdate`)}
style={web({maxWidth: 400})}>
<View style={[a.gap_md]}>
<Text style={[a.text_xl, a.font_semi_bold]}>
<Trans>My Birthdate</Trans>
</Text>
<Text
style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
This information is private and not shared with other users.
</Trans>
</Text>
{isLoading ? (
<Loader size="xl" />
) : error || !preferences ? (
<ErrorMessage
message={
error?.toString() ||
_(
msg`We were unable to load your birth date preferences. Please try again.`,
)
}
style={[a.rounded_sm]}
/>
) : (
<BirthdayInner control={control} preferences={preferences} />
)}
</View>
{isLoading ? (
<Loader size="xl" />
) : error || !preferences ? (
<ErrorMessage
message={
error?.toString() ||
_(
msg`We were unable to load your birthdate preferences. Please try again.`,
)
}
style={[a.rounded_sm]}
/>
) : (
<BirthdayInner control={control} preferences={preferences} />
)}
</View>
<Dialog.Close />
</Dialog.ScrollableInner>
<Dialog.Close />
</Dialog.ScrollableInner>
) : (
<Dialog.ScrollableInner
label={_(msg`You recently changed your birthdate`)}
style={web({maxWidth: 400})}>
<View style={[a.gap_sm]}>
<Text
style={[
a.text_xl,
a.font_semi_bold,
a.leading_snug,
{paddingRight: 32},
]}>
<Trans>You recently changed your birthdate</Trans>
</Text>
<Text
style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
There is a limit to how often you can change your birthdate. You
may need to wait a day or two before updating it again.
</Trans>
</Text>
</View>
<Dialog.Close />
</Dialog.ScrollableInner>
)}
</Dialog.Outer>
)
}
@@ -86,7 +118,7 @@ function BirthdayInner({
isError,
error,
mutateAsync: setBirthDate,
} = usePreferencesSetBirthDateMutation()
} = useBirthdateMutation()
const hasChanged = date !== preferences.birthDate
const age = getAge(new Date(date))
@@ -112,8 +144,8 @@ function BirthdayInner({
testID="birthdayInput"
value={date}
onChangeDate={newDate => setDate(new Date(newDate))}
label={_(msg`Birthday`)}
accessibilityHint={_(msg`Enter your birth date`)}
label={_(msg`Birthdate`)}
accessibilityHint={_(msg`Enter your birthdate`)}
/>
</View>
@@ -130,11 +162,11 @@ function BirthdayInner({
<Admonition type="error">
<Trans>
You must be at least 13 years old to use Bluesky. Read our{' '}
<InlineLinkText
<SimpleInlineLinkText
to="https://bsky.social/about/support/tos"
label={_(msg`Terms of Service`)}>
Terms of Service
</InlineLinkText>{' '}
</SimpleInlineLinkText>{' '}
for more information.
</Trans>
</Admonition>
@@ -146,7 +178,7 @@ function BirthdayInner({
<View style={isWeb && [a.flex_row, a.justify_end]}>
<Button
label={hasChanged ? _(msg`Save birthday`) : _(msg`Done`)}
label={hasChanged ? _(msg`Save birthdate`) : _(msg`Done`)}
size="large"
onPress={onSave}
variant="solid"
@@ -7,12 +7,6 @@ import {wait} from '#/lib/async/wait'
import {isNetworkError, useCleanError} from '#/lib/hooks/useCleanError'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
import {
computeGeolocationStatus,
type GeolocationStatus,
useGeolocationConfig,
} from '#/state/geolocation'
import {useRequestDeviceLocation} from '#/state/geolocation/useRequestDeviceLocation'
import {atoms as a, useTheme, web} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -20,10 +14,11 @@ 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: {
geolocationStatus: GeolocationStatus
geolocation: Geolocation
setDialogError: (error: string) => void
disableDialogAction: () => void
closeDialog: (callback?: () => void) => void
@@ -57,8 +52,7 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
const t = useTheme()
const {_} = useLingui()
const {close} = Dialog.useDialogContext()
const requestDeviceLocation = useRequestDeviceLocation()
const {config} = useGeolocationConfig()
const requestDeviceLocation = useRequestDeviceGeolocation()
const cleanError = useCleanError()
const [isRequesting, setIsRequesting] = useState(false)
@@ -76,9 +70,8 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
const location = req.location
if (location && location.countryCode) {
const geolocationStatus = computeGeolocationStatus(location, config)
onLocationAcquired?.({
geolocationStatus,
geolocation: location,
setDialogError: setError,
disableDialogAction: () => setDialogDisabled(true),
closeDialog: close,