Add metrics

This commit is contained in:
Eric Bailey
2025-07-16 18:47:33 -05:00
parent c9e1161688
commit 08d76f7251
12 changed files with 67 additions and 14 deletions
@@ -4,6 +4,7 @@ import {useLingui} from '@lingui/react'
import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {dateDiff, useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance'
import {logger} from '#/state/ageAssurance/util'
import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf' import {atoms as a, useBreakpoints, useTheme, type ViewStyleProp} from '#/alf'
import {Admonition} from '#/components/Admonition' import {Admonition} from '#/components/Admonition'
import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog' import {AgeAssuranceAppealDialog} from '#/components/ageAssurance/AgeAssuranceAppealDialog'
@@ -83,6 +84,7 @@ function Inner({style}: ViewStyleProp & {}) {
label={_(msg`Contact our moderation team`)} label={_(msg`Contact our moderation team`)}
{...createStaticClick(() => { {...createStaticClick(() => {
appealControl.open() appealControl.open()
logger.metric('ageAssurance:appealDialogOpen', {})
})}> })}>
contact our moderation team contact our moderation team
</InlineLinkText>{' '} </InlineLinkText>{' '}
@@ -109,7 +111,12 @@ function Inner({style}: ViewStyleProp & {}) {
size="small" size="small"
variant="solid" variant="solid"
color={hasInitiated ? 'secondary' : 'primary'} color={hasInitiated ? 'secondary' : 'primary'}
onPress={() => control.open()}> onPress={() => {
control.open()
logger.metric('ageAssurance:initDialogOpen', {
hasInitiatedPreviously: hasInitiated,
})
}}>
<ButtonText> <ButtonText>
{hasInitiated ? ( {hasInitiated ? (
<Trans>Verify again</Trans> <Trans>Verify again</Trans>
@@ -3,6 +3,7 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance'
import {logger} from '#/state/ageAssurance/util'
import {atoms as a, select, useTheme, type ViewStyleProp} from '#/alf' import {atoms as a, select, useTheme, type ViewStyleProp} from '#/alf'
import {useDialogControl} from '#/components/ageAssurance/AgeAssuranceInitDialog' import {useDialogControl} from '#/components/ageAssurance/AgeAssuranceInitDialog'
import type * as Dialog from '#/components/Dialog' import type * as Dialog from '#/components/Dialog'
@@ -87,7 +88,10 @@ function Inner({
<InlineLinkText <InlineLinkText
label={_(msg`Go to account settings`)} label={_(msg`Go to account settings`)}
to={'/settings/account'} to={'/settings/account'}
style={[a.text_sm, a.leading_snug, a.font_bold]}> style={[a.text_sm, a.leading_snug, a.font_bold]}
onPress={() => {
logger.metric('ageAssurance:navigateToSettings', {})
}}>
account settings. account settings.
</InlineLinkText> </InlineLinkText>
</Trans> </Trans>
@@ -5,7 +5,7 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useMutation} from '@tanstack/react-query' import {useMutation} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/state/ageAssurance/util'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useBreakpoints, web} from '#/alf' import {atoms as a, useBreakpoints, web} from '#/alf'
@@ -45,6 +45,8 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
const {mutate, isPending} = useMutation({ const {mutate, isPending} = useMutation({
mutationFn: async () => { mutationFn: async () => {
logger.metric('ageAssurance:appealDialogSubmit', {})
await agent.createModerationReport( await agent.createModerationReport(
{ {
reasonType: ComAtprotoModerationDefs.REASONAPPEAL, reasonType: ComAtprotoModerationDefs.REASONAPPEAL,
@@ -4,6 +4,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance'
import {logger} from '#/state/ageAssurance/util'
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
import {atoms as a, select, useTheme} from '#/alf' import {atoms as a, select, useTheme} from '#/alf'
import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy' import {useAgeAssuranceCopy} from '#/components/ageAssurance/useAgeAssuranceCopy'
@@ -72,7 +73,10 @@ export function AgeAssuranceDismissibleFeedBanner() {
<Link <Link
label={_(msg`Learn more about age assurance`)} label={_(msg`Learn more about age assurance`)}
to="/settings/account" to="/settings/account"
onPress={close} onPress={() => {
close()
logger.metric('ageAssurance:navigateToSettings', {})
}}
style={[a.w_full, a.justify_between, a.align_center, a.gap_md]}> style={[a.w_full, a.justify_between, a.align_center, a.gap_md]}>
<View <View
style={[ style={[
@@ -106,7 +110,10 @@ export function AgeAssuranceDismissibleFeedBanner() {
<Button <Button
label={_(msg`Don't show again`)} label={_(msg`Don't show again`)}
size="small" size="small"
onPress={close} onPress={() => {
close()
logger.metric('ageAssurance:dismissFeedBanner', {})
}}
style={[ style={[
a.absolute, a.absolute,
a.justify_center, a.justify_center,
@@ -3,6 +3,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance'
import {logger} from '#/state/ageAssurance/util'
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
import {atoms as a, type ViewStyleProp} from '#/alf' import {atoms as a, type ViewStyleProp} from '#/alf'
import {AgeAssuranceAdmonition} from '#/components/ageAssurance/AgeAssuranceAdmonition' import {AgeAssuranceAdmonition} from '#/components/ageAssurance/AgeAssuranceAdmonition'
@@ -37,13 +38,14 @@ export function AgeAssuranceDismissibleNotice({style}: ViewStyleProp & {}) {
variant="solid" variant="solid"
color="secondary_inverted" color="secondary_inverted"
shape="round" shape="round"
onPress={() => onPress={() => {
save({ save({
id: Nux.AgeAssuranceDismissibleNotice, id: Nux.AgeAssuranceDismissibleNotice,
completed: true, completed: true,
data: undefined, data: undefined,
}) })
} logger.metric('ageAssurance:dismissSettingsNotice', {})
}}
style={[ style={[
a.absolute, a.absolute,
{ {
@@ -16,6 +16,7 @@ import {isEmailMaybeInvalid} from '#/lib/strings/email'
import {type AppLanguage} from '#/locale/languages' import {type AppLanguage} from '#/locale/languages'
import {useAgeAssuranceContext} from '#/state/ageAssurance' import {useAgeAssuranceContext} from '#/state/ageAssurance'
import {useInitAgeAssurance} from '#/state/ageAssurance/useInitAgeAssurance' import {useInitAgeAssurance} from '#/state/ageAssurance/useInitAgeAssurance'
import {logger} from '#/state/ageAssurance/util'
import {useLanguagePrefs} from '#/state/preferences' import {useLanguagePrefs} from '#/state/preferences'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
@@ -115,6 +116,8 @@ function Inner() {
const onSubmit = async () => { const onSubmit = async () => {
setLanguageError(false) setLanguageError(false)
logger.metric('ageAssurance:initDialogSubmit', {})
try { try {
const {status} = runEmailValidation() const {status} = runEmailValidation()
@@ -138,6 +141,7 @@ function Inner() {
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'})
} else if (e.error === 'DidTooLong') { } else if (e.error === 'DidTooLong') {
setError( setError(
<> <>
@@ -153,10 +157,12 @@ function Inner() {
</Trans> </Trans>
</>, </>,
) )
logger.metric('ageAssurance:initDialogError', {code: 'DidTooLong'})
} }
} else { } else {
const {clean, raw} = cleanError(e) const {clean, raw} = cleanError(e)
setError(clean || raw || _(msg`Something went wrong, please try again`)) setError(clean || raw || _(msg`Something went wrong, please try again`))
logger.metric('ageAssurance:initDialogError', {code: 'other'})
} }
} }
} }
@@ -7,6 +7,7 @@ import {retry} from '#/lib/async/retry'
import {wait} from '#/lib/async/wait' import {wait} from '#/lib/async/wait'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {useAgeAssuranceAPIContext} from '#/state/ageAssurance' import {useAgeAssuranceAPIContext} from '#/state/ageAssurance'
import {logger} from '#/state/ageAssurance/util'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
@@ -92,6 +93,8 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
polling.current = true polling.current = true
logger.metric('ageAssurance:redirectDialogOpen', {})
wait( wait(
3e3, 3e3,
retry( retry(
@@ -124,12 +127,15 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
control.clear() control.clear()
control.control.close() control.control.close()
logger.metric('ageAssurance:redirectDialogSuccess', {})
}) })
.catch(() => { .catch(() => {
if (unmounted.current) return if (unmounted.current) return
setError(true) setError(true)
// try a refetch anyway // try a refetch anyway
refreshAgeAssuranceState() refreshAgeAssuranceState()
logger.metric('ageAssurance:redirectDialogFail', {})
}) })
return () => { return () => {
@@ -3,6 +3,7 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance' import {useAgeAssurance} from '#/state/ageAssurance/useAgeAssurance'
import {logger} from '#/state/ageAssurance/util'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {Admonition} from '#/components/Admonition' import {Admonition} from '#/components/Admonition'
import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge' import {AgeAssuranceBadge} from '#/components/ageAssurance/AgeAssuranceBadge'
@@ -77,7 +78,10 @@ export function AgeRestrictedScreen({
to="/settings/account" to="/settings/account"
size="small" size="small"
variant="solid" variant="solid"
color="primary"> color="primary"
onPress={() => {
logger.metric('ageAssurance:navigateToSettings', {})
}}>
<ButtonText> <ButtonText>
<Trans>Go to account settings</Trans> <Trans>Go to account settings</Trans>
</ButtonText> </ButtonText>
+16
View File
@@ -457,4 +457,20 @@ export type MetricEvents = {
name: string name: string
value: string value: string
} }
'ageAssurance:navigateToSettings': {}
'ageAssurance:dismissFeedBanner': {}
'ageAssurance:dismissSettingsNotice': {}
'ageAssurance:initDialogOpen': {
hasInitiatedPreviously: boolean
}
'ageAssurance:initDialogSubmit': {}
'ageAssurance:initDialogError': {
code: string
}
'ageAssurance:redirectDialogOpen': {}
'ageAssurance:redirectDialogSuccess': {}
'ageAssurance:redirectDialogFail': {}
'ageAssurance:appealDialogOpen': {}
'ageAssurance:appealDialogSubmit': {}
} }
+1 -3
View File
@@ -6,17 +6,15 @@ import {networkRetry} from '#/lib/async/retry'
import {useGetAndRegisterPushToken} from '#/lib/notifications/notifications' import {useGetAndRegisterPushToken} from '#/lib/notifications/notifications'
import {useGate} from '#/lib/statsig/statsig' import {useGate} from '#/lib/statsig/statsig'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {Logger} from '#/logger'
import { import {
type AgeAssuranceAPIContextType, type AgeAssuranceAPIContextType,
type AgeAssuranceContextType, type AgeAssuranceContextType,
} from '#/state/ageAssurance/types' } from '#/state/ageAssurance/types'
import {useIsAgeAssuranceEnabled} from '#/state/ageAssurance/useIsAgeAssuranceEnabled' import {useIsAgeAssuranceEnabled} from '#/state/ageAssurance/useIsAgeAssuranceEnabled'
import {logger} from '#/state/ageAssurance/util'
import {useGeolocation} from '#/state/geolocation' import {useGeolocation} from '#/state/geolocation'
import {useAgent} from '#/state/session' import {useAgent} from '#/state/session'
const logger = Logger.create(Logger.Context.AgeAssurance)
export const createAgeAssuranceQueryKey = (did: string) => export const createAgeAssuranceQueryKey = (did: string) =>
['ageAssurance', did] as const ['ageAssurance', did] as const
+1 -3
View File
@@ -1,11 +1,9 @@
import {useMemo} from 'react' import {useMemo} from 'react'
import {Logger} from '#/logger'
import {useAgeAssuranceContext} from '#/state/ageAssurance' import {useAgeAssuranceContext} from '#/state/ageAssurance'
import {logger} from '#/state/ageAssurance/util'
import {usePreferencesQuery} from '#/state/queries/preferences' import {usePreferencesQuery} from '#/state/queries/preferences'
const logger = Logger.create(Logger.Context.AgeAssurance)
type AgeAssurance = ReturnType<typeof useAgeAssuranceContext> & { type AgeAssurance = ReturnType<typeof useAgeAssuranceContext> & {
/** /**
* The age the user has declared in their preferences, if any. * The age the user has declared in their preferences, if any.
+3
View File
@@ -0,0 +1,3 @@
import {Logger} from '#/logger'
export const logger = Logger.create(Logger.Context.AgeAssurance)