Merge remote-tracking branch 'origin/main' into hailey/oauth-yeag

This commit is contained in:
Hailey
2025-07-18 20:00:49 -07:00
8 changed files with 102 additions and 22 deletions
@@ -134,16 +134,18 @@ function Inner() {
setSuccess(true)
} catch (e) {
let error: React.ReactNode = _(
msg`Something went wrong, please try again`,
)
if (e instanceof XRPCError) {
if (e.error === 'InvalidEmail') {
setError(
_(
msg`Please enter a valid, non-temporary email address. You may need to access this email in the future.`,
),
error = _(
msg`Please enter a valid, non-temporary email address. You may need to access this email in the future.`,
)
logger.metric('ageAssurance:initDialogError', {code: 'InvalidEmail'})
} else if (e.error === 'DidTooLong') {
setError(
error = (
<>
<Trans>
We're having issues initializing the age assurance process for
@@ -155,15 +157,19 @@ function Inner() {
</InlineLinkText>{' '}
for assistance.
</Trans>
</>,
</>
)
logger.metric('ageAssurance:initDialogError', {code: 'DidTooLong'})
} else {
logger.metric('ageAssurance:initDialogError', {code: 'other'})
}
} else {
const {clean, raw} = cleanError(e)
setError(clean || raw || _(msg`Something went wrong, please try again`))
error = clean || raw || error
logger.metric('ageAssurance:initDialogError', {code: 'other'})
}
setError(error)
}
}
@@ -188,7 +194,7 @@ function Inner() {
<>
<Text style={[a.text_sm, a.leading_snug]}>
<Trans>
We use{' '}
We have partnered with{' '}
<InlineLinkText
overridePresentation
disableMismatchWarning
@@ -198,8 +204,11 @@ function Inner() {
KWS
</InlineLinkText>{' '}
to verify that youre an adult. When you click "Begin" below,
KWS will email you instructions for verifying your age. When
youre done, you'll be brought back to continue using Bluesky.
KWS will check if you have previously verified your age using
this email address for other games/services powered by KWS
technology. If not, KWS will email you instructions for
verifying your age. When youre done, you'll be brought back
to continue using Bluesky.
</Trans>
</Text>
<Text style={[a.text_sm, a.leading_snug]}>
@@ -14,6 +14,7 @@ import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {CheckThick_Stroke2_Corner0_Rounded as SuccessIcon} from '#/components/icons/Check'
import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
@@ -66,7 +67,7 @@ export function AgeAssuranceRedirectDialog() {
// Dialog.useAutoOpen(control.control, 3e3)
return (
<Dialog.Outer control={control.control}>
<Dialog.Outer control={control.control} onClose={() => control.clear()}>
<Dialog.Handle />
<Dialog.ScrollableInner
@@ -86,6 +87,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
const unmounted = useRef(false)
const control = useAgeAssuranceRedirectDialogControl()
const [error, setError] = useState(false)
const [success, setSuccess] = useState(false)
const {refetch: refreshAgeAssuranceState} = useAgeAssuranceAPIContext()
useEffect(() => {
@@ -125,8 +127,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
// success! update state
await refreshAgeAssuranceState()
control.clear()
control.control.close()
setSuccess(true)
logger.metric('ageAssurance:redirectDialogSuccess', {})
})
@@ -143,6 +144,55 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
}
}, [agent, control, refreshAgeAssuranceState])
if (success) {
return (
<>
<View style={[a.align_start, a.w_full]}>
<AgeAssuranceBadge />
<View
style={[
a.flex_row,
a.justify_between,
a.align_center,
a.gap_sm,
a.pt_lg,
a.pb_md,
]}>
<SuccessIcon size="sm" fill={t.palette.positive_600} />
<Text style={[a.text_xl, a.font_heavy]}>
<Trans>Success</Trans>
</Text>
</View>
<Text style={[a.text_md, a.leading_snug]}>
<Trans>
We've confirmed your age assurance status. You can now close this
dialog.
</Trans>
</Text>
{isNative && (
<View style={[a.w_full, a.pt_lg]}>
<Button
label={_(msg`Close`)}
size="large"
variant="solid"
color="secondary"
onPress={() => control.control.close()}>
<ButtonText>
<Trans>Close</Trans>
</ButtonText>
</Button>
</View>
)}
</View>
<Dialog.Close />
</>
)
}
return (
<>
<View style={[a.align_start, a.w_full]}>
@@ -175,8 +225,8 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
</Trans>
) : (
<Trans>
We're confirming your status with our servers. This dialog should
close in a few seconds.
We're confirming your age assurance status with our servers. This
should only take a few seconds.
</Trans>
)}
</Text>
@@ -187,7 +237,8 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
label={_(msg`Close`)}
size="large"
variant="solid"
color="secondary">
color="secondary"
onPress={() => control.control.close()}>
<ButtonText>
<Trans>Close</Trans>
</ButtonText>
@@ -11,7 +11,7 @@ export function useAgeAssuranceCopy() {
msg`The laws in your location require you to verify you're an adult before accessing certain features on Bluesky, like adult content and direct messaging.`,
),
banner: _(
msg`The laws in your location require you to verify you're an adult. Tap to learn more.`,
msg`The laws in your location require you to verify you're an adult to access certain features. Tap to learn more.`,
),
chatsInfoText: _(
msg`Don't worry! All existing messages and settings are saved and will be available after you verify you're an adult.`,
+4 -4
View File
@@ -1,10 +1,10 @@
import {View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg, plural} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {makeProfileLink} from '#/lib/routes/links'
import {Shadow} from '#/state/cache/types'
import {type Shadow} from '#/state/cache/types'
import {formatCount} from '#/view/com/util/numeric/format'
import {atoms as a, useTheme} from '#/alf'
import {InlineLinkText} from '#/components/Link'
@@ -36,7 +36,7 @@ export function ProfileHeaderMetrics({
testID="profileHeaderFollowersButton"
style={[a.flex_row, t.atoms.text]}
to={makeProfileLink(profile, 'followers')}
label={`${followers} ${pluralizedFollowers}`}>
label={`${profile.followersCount || 0} ${pluralizedFollowers}`}>
<Text style={[a.font_bold, a.text_md]}>{followers} </Text>
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
{pluralizedFollowers}
@@ -46,7 +46,7 @@ export function ProfileHeaderMetrics({
testID="profileHeaderFollowsButton"
style={[a.flex_row, t.atoms.text]}
to={makeProfileLink(profile, 'follows')}
label={_(msg`${following} following`)}>
label={_(msg`${profile.followsCount || 0} following`)}>
<Text style={[a.font_bold, a.text_md]}>{following} </Text>
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
{pluralizedFollowings}
+21 -1
View File
@@ -1,4 +1,4 @@
import {createContext, useContext, useMemo} from 'react'
import {createContext, useContext, useMemo, useState} from 'react'
import {type AppBskyUnspeccedDefs} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
@@ -46,6 +46,7 @@ export function Provider({children}: {children: React.ReactNode}) {
const {geolocation} = useGeolocation()
const isAgeAssuranceEnabled = useIsAgeAssuranceEnabled()
const getAndRegisterPushToken = useGetAndRegisterPushToken()
const [refetchWhilePending, setRefetchWhilePending] = useState(false)
const {data, isFetched, refetch} = useQuery({
/**
@@ -56,6 +57,7 @@ export function Provider({children}: {children: React.ReactNode}) {
* However, it only needs to run if AA is enabled.
*/
enabled: isAgeAssuranceEnabled,
refetchOnWindowFocus: refetchWhilePending,
queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'),
async queryFn() {
if (!agent.session) return null
@@ -109,6 +111,24 @@ export function Provider({children}: {children: React.ReactNode}) {
return ctx
}, [isFetched, data, isAgeAssuranceEnabled])
if (
!!ageAssuranceContext.lastInitiatedAt &&
ageAssuranceContext.status === 'pending' &&
!refetchWhilePending
) {
/*
* If we have a pending state, we want to refetch on window focus to ensure
* that we get the latest state when the user returns to the app.
*/
setRefetchWhilePending(true)
} else if (
!!ageAssuranceContext.lastInitiatedAt &&
ageAssuranceContext.status !== 'pending' &&
refetchWhilePending
) {
setRefetchWhilePending(false)
}
const ageAssuranceAPIContext = useMemo<AgeAssuranceAPIContextType>(
() => ({
refetch,