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
+1 -1
View File
@@ -45,7 +45,7 @@ export const ChooseAccountForm = ({
}
try {
setPendingDid(account.did)
await resumeSession(account)
await resumeSession(account, true)
logEvent('account:loggedIn', {
logContext: 'ChooseAccountForm',
withPassword: false,
+93 -148
View File
@@ -12,7 +12,6 @@ import {
} from '#/lib/routes/types'
import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance'
import {
useMyLabelersQuery,
usePreferencesQuery,
@@ -22,11 +21,8 @@ import {
import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities'
import {useSetMinimalShellMode} from '#/state/shell'
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {AgeAssuranceAdmonition} from '#/components/ageAssurance/AgeAssuranceAdmonition'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
import {Button} from '#/components/Button'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {Divider} from '#/components/Divider'
import * as Toggle from '#/components/forms/Toggle'
@@ -45,6 +41,7 @@ import {ListMaybePlaceholder} from '#/components/Lists'
import {Loader} from '#/components/Loader'
import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
import {Text} from '#/components/Typography'
import {useAgeAssurance} from '#/ageAssurance'
function ErrorState({error}: {error: string}) {
const t = useTheme()
@@ -86,9 +83,8 @@ export function ModerationScreen(
error: preferencesError,
data: preferences,
} = usePreferencesQuery()
const {isReady: isAgeInfoReady} = useAgeAssurance()
const isLoading = isPreferencesLoading || !isAgeInfoReady
const isLoading = isPreferencesLoading
const error = preferencesError
return (
@@ -162,13 +158,12 @@ export function ModerationScreenInner({
const setMinimalShellMode = useSetMinimalShellMode()
const {gtMobile} = useBreakpoints()
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
const birthdateDialogControl = Dialog.useDialogControl()
const {
isLoading: isLabelersLoading,
data: labelers,
error: labelersError,
} = useMyLabelersQuery()
const {declaredAge, isDeclaredUnderage, isAgeRestricted} = useAgeAssurance()
const aa = useAgeAssurance()
useFocusEffect(
useCallback(() => {
@@ -202,24 +197,6 @@ export function ModerationScreenInner({
return (
<View style={[a.pt_2xl, a.px_lg, gtMobile && a.px_2xl]}>
{isDeclaredUnderage && (
<View style={[a.pb_2xl]}>
<Admonition type="tip" style={[a.pb_md]}>
<Trans>
Your declared age is under 18. Some settings below may be
disabled. If this was a mistake, you may edit your birthdate in
your{' '}
<InlineLinkText
to="/settings/account"
label={_(msg`Go to account settings`)}>
account settings
</InlineLinkText>
.
</Trans>
</Admonition>
</View>
)}
<Text
style={[
a.text_md,
@@ -328,134 +305,102 @@ export function ModerationScreenInner({
</Link>
</View>
{(!isDeclaredUnderage || declaredAge === undefined) && (
<Text
<Text
style={[
a.pt_2xl,
a.pb_md,
a.text_md,
a.font_semi_bold,
t.atoms.text_contrast_high,
]}>
<Trans>Content filters</Trans>
</Text>
<AgeAssuranceAdmonition style={[a.pb_md]}>
<Trans>
You must complete age assurance in order to access content filters.
</Trans>
</AgeAssuranceAdmonition>
<View style={[a.gap_md]}>
<View
style={[
a.pt_2xl,
a.pb_md,
a.text_md,
a.font_semi_bold,
t.atoms.text_contrast_high,
a.w_full,
a.rounded_md,
a.overflow_hidden,
t.atoms.bg_contrast_25,
]}>
<Trans>Content filters</Trans>
</Text>
)}
{declaredAge === undefined ? (
<>
<Button
label={_(msg`Confirm your birthdate`)}
size="small"
variant="solid"
color="secondary"
onPress={() => {
birthdateDialogControl.open()
}}
style={[a.justify_between, a.rounded_md, a.px_lg, a.py_lg]}>
<ButtonText>
<Trans>Confirm your age:</Trans>
</ButtonText>
<ButtonText>
<Trans>Set birthdate</Trans>
</ButtonText>
</Button>
<BirthDateSettingsDialog control={birthdateDialogControl} />
</>
) : !isDeclaredUnderage ? (
<>
<AgeAssuranceAdmonition style={[a.pb_md]}>
<Trans>
You must complete age assurance in order to access the settings
below.
</Trans>
</AgeAssuranceAdmonition>
<View style={[a.gap_md]}>
<View
style={[
a.w_full,
a.rounded_md,
a.overflow_hidden,
t.atoms.bg_contrast_25,
]}>
{!isDeclaredUnderage && (
<>
<View
style={[
a.py_lg,
a.px_lg,
a.flex_row,
a.align_center,
a.justify_between,
disabledOnIOS && {opacity: 0.5},
]}>
<Text
style={[a.font_semi_bold, t.atoms.text_contrast_high]}>
<Trans>Enable adult content</Trans>
{aa.state.access === aa.Access.Full && (
<>
<View
style={[
a.py_lg,
a.px_lg,
a.flex_row,
a.align_center,
a.justify_between,
disabledOnIOS && {opacity: 0.5},
]}>
<Text style={[a.font_semi_bold, t.atoms.text_contrast_high]}>
<Trans>Enable adult content</Trans>
</Text>
<Toggle.Item
label={_(msg`Toggle to enable or disable adult content`)}
disabled={disabledOnIOS}
name="adultContent"
value={adultContentEnabled}
onChange={onToggleAdultContentEnabled}>
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<Text style={[t.atoms.text_contrast_medium]}>
{adultContentEnabled ? (
<Trans>Enabled</Trans>
) : (
<Trans>Disabled</Trans>
)}
</Text>
<Toggle.Item
label={_(msg`Toggle to enable or disable adult content`)}
disabled={disabledOnIOS || isAgeRestricted}
name="adultContent"
value={adultContentEnabled}
onChange={onToggleAdultContentEnabled}>
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<Text style={[t.atoms.text_contrast_medium]}>
{adultContentEnabled ? (
<Trans>Enabled</Trans>
) : (
<Trans>Disabled</Trans>
)}
</Text>
<Toggle.Switch />
</View>
</Toggle.Item>
<Toggle.Switch />
</View>
{disabledOnIOS && (
<View style={[a.pb_lg, a.px_lg]}>
<Text>
<Trans>
Adult content can only be enabled via the Web at{' '}
<InlineLinkText
label={_(msg`The Bluesky web application`)}
to=""
onPress={evt => {
evt.preventDefault()
Linking.openURL('https://bsky.app/')
return false
}}>
bsky.app
</InlineLinkText>
.
</Trans>
</Text>
</View>
)}
</Toggle.Item>
</View>
{disabledOnIOS && (
<View style={[a.pb_lg, a.px_lg]}>
<Text>
<Trans>
Adult content can only be enabled via the Web at{' '}
<InlineLinkText
label={_(msg`The Bluesky web application`)}
to=""
onPress={evt => {
evt.preventDefault()
Linking.openURL('https://bsky.app/')
return false
}}>
bsky.app
</InlineLinkText>
.
</Trans>
</Text>
</View>
)}
{adultContentEnabled && (
<>
<Divider />
<GlobalLabelPreference labelDefinition={LABELS.porn} />
<Divider />
<GlobalLabelPreference labelDefinition={LABELS.sexual} />
<Divider />
<GlobalLabelPreference
labelDefinition={LABELS['graphic-media']}
/>
<Divider />
<GlobalLabelPreference
disabled={isDeclaredUnderage || isAgeRestricted}
labelDefinition={LABELS.nudity}
/>
</>
)}
{adultContentEnabled && (
<>
<Divider />
<GlobalLabelPreference labelDefinition={LABELS.porn} />
<Divider />
<GlobalLabelPreference labelDefinition={LABELS.sexual} />
<Divider />
<GlobalLabelPreference
labelDefinition={LABELS['graphic-media']}
/>
<Divider />
<GlobalLabelPreference labelDefinition={LABELS.nudity} />
</>
)}
</View>
</View>
</>
) : null}
</>
)}
</View>
</View>
<Text
style={[
@@ -312,7 +312,6 @@ function SuggestedProfileCard({
return (
<View
style={[
a.flex_1,
a.w_full,
a.py_lg,
a.px_xl,