Merge remote-tracking branch 'origin/main' into hailey/oauth-yeag
This commit is contained in:
@@ -134,16 +134,18 @@ function Inner() {
|
|||||||
|
|
||||||
setSuccess(true)
|
setSuccess(true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
let error: React.ReactNode = _(
|
||||||
|
msg`Something went wrong, please try again`,
|
||||||
|
)
|
||||||
|
|
||||||
if (e instanceof XRPCError) {
|
if (e instanceof XRPCError) {
|
||||||
if (e.error === 'InvalidEmail') {
|
if (e.error === 'InvalidEmail') {
|
||||||
setError(
|
error = _(
|
||||||
_(
|
|
||||||
msg`Please enter a valid, non-temporary email address. You may need to access this email in the future.`,
|
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'})
|
logger.metric('ageAssurance:initDialogError', {code: 'InvalidEmail'})
|
||||||
} else if (e.error === 'DidTooLong') {
|
} else if (e.error === 'DidTooLong') {
|
||||||
setError(
|
error = (
|
||||||
<>
|
<>
|
||||||
<Trans>
|
<Trans>
|
||||||
We're having issues initializing the age assurance process for
|
We're having issues initializing the age assurance process for
|
||||||
@@ -155,15 +157,19 @@ function Inner() {
|
|||||||
</InlineLinkText>{' '}
|
</InlineLinkText>{' '}
|
||||||
for assistance.
|
for assistance.
|
||||||
</Trans>
|
</Trans>
|
||||||
</>,
|
</>
|
||||||
)
|
)
|
||||||
logger.metric('ageAssurance:initDialogError', {code: 'DidTooLong'})
|
logger.metric('ageAssurance:initDialogError', {code: 'DidTooLong'})
|
||||||
|
} else {
|
||||||
|
logger.metric('ageAssurance:initDialogError', {code: 'other'})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const {clean, raw} = cleanError(e)
|
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'})
|
logger.metric('ageAssurance:initDialogError', {code: 'other'})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setError(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +194,7 @@ function Inner() {
|
|||||||
<>
|
<>
|
||||||
<Text style={[a.text_sm, a.leading_snug]}>
|
<Text style={[a.text_sm, a.leading_snug]}>
|
||||||
<Trans>
|
<Trans>
|
||||||
We use{' '}
|
We have partnered with{' '}
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
overridePresentation
|
overridePresentation
|
||||||
disableMismatchWarning
|
disableMismatchWarning
|
||||||
@@ -198,8 +204,11 @@ function Inner() {
|
|||||||
KWS
|
KWS
|
||||||
</InlineLinkText>{' '}
|
</InlineLinkText>{' '}
|
||||||
to verify that you’re an adult. When you click "Begin" below,
|
to verify that you’re an adult. When you click "Begin" below,
|
||||||
KWS will email you instructions for verifying your age. When
|
KWS will check if you have previously verified your age using
|
||||||
you’re done, you'll be brought back to continue using Bluesky.
|
this email address for other games/services powered by KWS
|
||||||
|
technology. If not, KWS will email you instructions for
|
||||||
|
verifying your age. When you’re done, you'll be brought back
|
||||||
|
to continue using Bluesky.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[a.text_sm, a.leading_snug]}>
|
<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 {Button, ButtonText} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
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 {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
@@ -66,7 +67,7 @@ export function AgeAssuranceRedirectDialog() {
|
|||||||
// Dialog.useAutoOpen(control.control, 3e3)
|
// Dialog.useAutoOpen(control.control, 3e3)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control.control}>
|
<Dialog.Outer control={control.control} onClose={() => control.clear()}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner
|
<Dialog.ScrollableInner
|
||||||
@@ -86,6 +87,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
const unmounted = useRef(false)
|
const unmounted = useRef(false)
|
||||||
const control = useAgeAssuranceRedirectDialogControl()
|
const control = useAgeAssuranceRedirectDialogControl()
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
const [success, setSuccess] = useState(false)
|
||||||
const {refetch: refreshAgeAssuranceState} = useAgeAssuranceAPIContext()
|
const {refetch: refreshAgeAssuranceState} = useAgeAssuranceAPIContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -125,8 +127,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
// success! update state
|
// success! update state
|
||||||
await refreshAgeAssuranceState()
|
await refreshAgeAssuranceState()
|
||||||
|
|
||||||
control.clear()
|
setSuccess(true)
|
||||||
control.control.close()
|
|
||||||
|
|
||||||
logger.metric('ageAssurance:redirectDialogSuccess', {})
|
logger.metric('ageAssurance:redirectDialogSuccess', {})
|
||||||
})
|
})
|
||||||
@@ -143,6 +144,55 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
}
|
}
|
||||||
}, [agent, control, refreshAgeAssuranceState])
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<View style={[a.align_start, a.w_full]}>
|
<View style={[a.align_start, a.w_full]}>
|
||||||
@@ -175,8 +225,8 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
</Trans>
|
</Trans>
|
||||||
) : (
|
) : (
|
||||||
<Trans>
|
<Trans>
|
||||||
We're confirming your status with our servers. This dialog should
|
We're confirming your age assurance status with our servers. This
|
||||||
close in a few seconds.
|
should only take a few seconds.
|
||||||
</Trans>
|
</Trans>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -187,7 +237,8 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
|
|||||||
label={_(msg`Close`)}
|
label={_(msg`Close`)}
|
||||||
size="large"
|
size="large"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary">
|
color="secondary"
|
||||||
|
onPress={() => control.control.close()}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Close</Trans>
|
<Trans>Close</Trans>
|
||||||
</ButtonText>
|
</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.`,
|
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: _(
|
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: _(
|
chatsInfoText: _(
|
||||||
msg`Don't worry! All existing messages and settings are saved and will be available after you verify you're an adult.`,
|
msg`Don't worry! All existing messages and settings are saved and will be available after you verify you're an adult.`,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {type AppBskyActorDefs} from '@atproto/api'
|
||||||
import {msg, plural} from '@lingui/macro'
|
import {msg, plural} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
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 {formatCount} from '#/view/com/util/numeric/format'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {InlineLinkText} from '#/components/Link'
|
import {InlineLinkText} from '#/components/Link'
|
||||||
@@ -36,7 +36,7 @@ export function ProfileHeaderMetrics({
|
|||||||
testID="profileHeaderFollowersButton"
|
testID="profileHeaderFollowersButton"
|
||||||
style={[a.flex_row, t.atoms.text]}
|
style={[a.flex_row, t.atoms.text]}
|
||||||
to={makeProfileLink(profile, 'followers')}
|
to={makeProfileLink(profile, 'followers')}
|
||||||
label={`${followers} ${pluralizedFollowers}`}>
|
label={`${profile.followersCount || 0} ${pluralizedFollowers}`}>
|
||||||
<Text style={[a.font_bold, a.text_md]}>{followers} </Text>
|
<Text style={[a.font_bold, a.text_md]}>{followers} </Text>
|
||||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||||
{pluralizedFollowers}
|
{pluralizedFollowers}
|
||||||
@@ -46,7 +46,7 @@ export function ProfileHeaderMetrics({
|
|||||||
testID="profileHeaderFollowsButton"
|
testID="profileHeaderFollowsButton"
|
||||||
style={[a.flex_row, t.atoms.text]}
|
style={[a.flex_row, t.atoms.text]}
|
||||||
to={makeProfileLink(profile, 'follows')}
|
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={[a.font_bold, a.text_md]}>{following} </Text>
|
||||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||||
{pluralizedFollowings}
|
{pluralizedFollowings}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {createContext, useContext, useMemo} from 'react'
|
import {createContext, useContext, useMemo, useState} from 'react'
|
||||||
import {type AppBskyUnspeccedDefs} from '@atproto/api'
|
import {type AppBskyUnspeccedDefs} from '@atproto/api'
|
||||||
import {useQuery} from '@tanstack/react-query'
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
@@ -46,6 +46,7 @@ export function Provider({children}: {children: React.ReactNode}) {
|
|||||||
const {geolocation} = useGeolocation()
|
const {geolocation} = useGeolocation()
|
||||||
const isAgeAssuranceEnabled = useIsAgeAssuranceEnabled()
|
const isAgeAssuranceEnabled = useIsAgeAssuranceEnabled()
|
||||||
const getAndRegisterPushToken = useGetAndRegisterPushToken()
|
const getAndRegisterPushToken = useGetAndRegisterPushToken()
|
||||||
|
const [refetchWhilePending, setRefetchWhilePending] = useState(false)
|
||||||
|
|
||||||
const {data, isFetched, refetch} = useQuery({
|
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.
|
* However, it only needs to run if AA is enabled.
|
||||||
*/
|
*/
|
||||||
enabled: isAgeAssuranceEnabled,
|
enabled: isAgeAssuranceEnabled,
|
||||||
|
refetchOnWindowFocus: refetchWhilePending,
|
||||||
queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'),
|
queryKey: createAgeAssuranceQueryKey(agent.session?.did ?? 'never'),
|
||||||
async queryFn() {
|
async queryFn() {
|
||||||
if (!agent.session) return null
|
if (!agent.session) return null
|
||||||
@@ -109,6 +111,24 @@ export function Provider({children}: {children: React.ReactNode}) {
|
|||||||
return ctx
|
return ctx
|
||||||
}, [isFetched, data, isAgeAssuranceEnabled])
|
}, [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>(
|
const ageAssuranceAPIContext = useMemo<AgeAssuranceAPIContextType>(
|
||||||
() => ({
|
() => ({
|
||||||
refetch,
|
refetch,
|
||||||
|
|||||||
Reference in New Issue
Block a user