[APP-1782] Analytics migration (#9734)
* WIP * Clean up growthbook code, integrate into init and sessions * Move everything out of React * Add metrics client * Move to separate file * Shared metadata cache * Ensure we update metadata when session ID changes * Ensure userMetadata is cleared when logging out * WIP revamp * Integrate feature gates into analytics context * Clean up old code * Fix useMeta util * Some comments and cleanup * Add logger to base analytics context * Refactor current route handling * Rip out LogEvent from navigation * Update tracking endpoint * Migrate toClout * Clear out statsig client * Add todo, reset logger readme * Ope fix statsig noop * Refactor logging in feed-feedback, add debug logging to metrics client * Remove LogEvents alias for Metrics * Prefer root package export * Remove Metrics alias from logger * [APP-1782] Migrate to new analytics APIs (#9735) * Migrate logEvent to useAnalytics * Migrate logger.metric to useAnalytics * Migrate tricky spot, fix types * Migrate remaining tricky spot * Missed one * Remove metric() from logger * Migrate useGate to useAnalytics * Remove all other StatSig mentions * Update event payload * Update logger tests * Mock expo method * Fix session ID bug * Add session ID test * Add test for metrics client * Clarify intent * Clean up core analytics file * Clean up the call once utils * Fix TODO * Fix TODO * Fix TODO * Fix TODO * Fix TODO * Remove debug code * Fix navigation context * OK nav context is not working, todo * Checkpoint: works but feels hacky * Fix navigation context issue * Improve feature API * Improve metric logging * Update logger tests
This commit is contained in:
@@ -6,12 +6,12 @@ import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {Link} from '../Link'
|
||||
import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from './country-allowlist'
|
||||
@@ -19,6 +19,7 @@ import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from './country-allow
|
||||
export function FindContactsBannerNUX() {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {visible, close} = useInternalState()
|
||||
|
||||
if (!visible) return null
|
||||
@@ -30,7 +31,7 @@ export function FindContactsBannerNUX() {
|
||||
to={{screen: 'FindContactsFlow'}}
|
||||
label={_(msg`Import contacts to find your friends`)}
|
||||
onPress={() => {
|
||||
logger.metric('contacts:nux:bannerPressed', {})
|
||||
ax.metric('contacts:nux:bannerPressed', {})
|
||||
}}
|
||||
style={[
|
||||
a.w_full,
|
||||
@@ -84,6 +85,7 @@ export function FindContactsBannerNUX() {
|
||||
)
|
||||
}
|
||||
function useInternalState() {
|
||||
const ax = useAnalytics()
|
||||
const {nux} = useNux(Nux.FindContactsDismissibleBanner)
|
||||
const {mutate: save, variables} = useSaveNux()
|
||||
const hidden = !!variables
|
||||
@@ -103,7 +105,7 @@ function useInternalState() {
|
||||
completed: true,
|
||||
data: undefined,
|
||||
})
|
||||
logger.metric('contacts:nux:bannerDismissed', {})
|
||||
ax.metric('contacts:nux:bannerDismissed', {})
|
||||
}
|
||||
|
||||
return {visible, close}
|
||||
|
||||
@@ -28,6 +28,7 @@ import * as Layout from '#/components/Layout'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {
|
||||
contactsWithPhoneNumbersOnly,
|
||||
filterMatchedNumbers,
|
||||
@@ -51,6 +52,7 @@ export function GetContacts({
|
||||
context: 'Onboarding' | 'Standalone'
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const agent = useAgent()
|
||||
const insets = useSafeAreaInsets()
|
||||
const gutters = useGutters([0, 'wide'])
|
||||
@@ -100,16 +102,16 @@ export function GetContacts({
|
||||
},
|
||||
onSuccess: (result, contacts) => {
|
||||
if (context === 'Onboarding') {
|
||||
logger.metric('onboarding:contacts:contactsShared', {})
|
||||
ax.metric('onboarding:contacts:contactsShared', {})
|
||||
}
|
||||
if (result.matches.length > 0) {
|
||||
logger.metric('contacts:import:success', {
|
||||
ax.metric('contacts:import:success', {
|
||||
contactCount: contacts.length,
|
||||
matchCount: result.matches.length,
|
||||
entryPoint: context,
|
||||
})
|
||||
} else {
|
||||
logger.metric('contacts:import:failure', {
|
||||
ax.metric('contacts:import:failure', {
|
||||
reason: 'noValidNumbers',
|
||||
entryPoint: context,
|
||||
})
|
||||
@@ -134,7 +136,7 @@ export function GetContacts({
|
||||
})
|
||||
},
|
||||
onError: err => {
|
||||
logger.metric('contacts:import:failure', {
|
||||
ax.metric('contacts:import:failure', {
|
||||
reason: isNetworkError(err) ? 'networkError' : 'unknown',
|
||||
entryPoint: context,
|
||||
})
|
||||
@@ -180,7 +182,7 @@ export function GetContacts({
|
||||
permissions = await Contacts.requestPermissionsAsync()
|
||||
}
|
||||
|
||||
logger.metric('contacts:permission:request', {
|
||||
ax.metric('contacts:permission:request', {
|
||||
status: permissions.granted ? 'granted' : 'denied',
|
||||
accessLevelIOS: ios(permissions.accessPrivileges),
|
||||
})
|
||||
|
||||
@@ -31,6 +31,7 @@ import * as Layout from '#/components/Layout'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {useGeolocation} from '#/geolocation'
|
||||
import {isFindContactsFeatureEnabled} from '../country-allowlist'
|
||||
import {
|
||||
@@ -52,6 +53,7 @@ export function PhoneInput({
|
||||
onSkip: () => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const t = useTheme()
|
||||
const agent = useAgent()
|
||||
const location = useGeolocation()
|
||||
@@ -85,7 +87,7 @@ export function PhoneInput({
|
||||
payload: {phoneCountryCode, phoneNumber},
|
||||
})
|
||||
|
||||
logger.metric('contacts:phone:phoneEntered', {entryPoint: context})
|
||||
ax.metric('contacts:phone:phoneEntered', {entryPoint: context})
|
||||
},
|
||||
onMutate: () => {
|
||||
Keyboard.dismiss()
|
||||
|
||||
@@ -23,6 +23,7 @@ import * as Layout from '#/components/Layout'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {OTPInput} from '../components/OTPInput'
|
||||
import {constructFullPhoneNumber, prettyPhoneNumber} from '../phone-number'
|
||||
import {type Action, type State, useOnPressBackButton} from '../state'
|
||||
@@ -40,6 +41,7 @@ export function VerifyNumber({
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const agent = useAgent()
|
||||
const gutters = useGutters([0, 'wide'])
|
||||
|
||||
@@ -83,7 +85,7 @@ export function VerifyNumber({
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
logger.metric('contacts:phone:phoneVerified', {entryPoint: context})
|
||||
ax.metric('contacts:phone:phoneVerified', {entryPoint: context})
|
||||
},
|
||||
onMutate: () => setError(null),
|
||||
onError: err => {
|
||||
|
||||
@@ -39,6 +39,7 @@ import {Loader} from '#/components/Loader'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {InviteInfo} from '../components/InviteInfo'
|
||||
import {type Action, type Contact, type Match, type State} from '../state'
|
||||
@@ -83,6 +84,7 @@ export function ViewMatches({
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const gutter = useGutters([0, 'wide'])
|
||||
const moderationOpts = useModerationOpts()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -109,9 +111,9 @@ export function ViewMatches({
|
||||
|
||||
const cumulativeFollowCount = useRef(0)
|
||||
const onFollow = useCallback(() => {
|
||||
logger.metric('contacts:matches:follow', {entryPoint: context})
|
||||
ax.metric('contacts:matches:follow', {entryPoint: context})
|
||||
cumulativeFollowCount.current += 1
|
||||
}, [context])
|
||||
}, [ax, context])
|
||||
|
||||
const {mutate: followAll, isPending: isFollowingAll} = useMutation({
|
||||
mutationFn: async () => {
|
||||
@@ -132,7 +134,7 @@ export function ViewMatches({
|
||||
return followableDids
|
||||
},
|
||||
onMutate: () =>
|
||||
logger.metric('contacts:matches:followAll', {
|
||||
ax.metric('contacts:matches:followAll', {
|
||||
followCount: followableDids.length,
|
||||
entryPoint: context,
|
||||
}),
|
||||
@@ -218,7 +220,7 @@ export function ViewMatches({
|
||||
await agent.app.bsky.contact.dismissMatch({subject: did})
|
||||
},
|
||||
onMutate: did => {
|
||||
logger.metric('contacts:matches:dismiss', {entryPoint: context})
|
||||
ax.metric('contacts:matches:dismiss', {entryPoint: context})
|
||||
dispatch({type: 'DISMISS_MATCH', payload: {did}})
|
||||
},
|
||||
onSuccess: (_res, did) => {
|
||||
@@ -392,7 +394,7 @@ export function ViewMatches({
|
||||
label={context === 'Onboarding' ? _(msg`Next`) : _(msg`Done`)}
|
||||
onPress={() => {
|
||||
if (context === 'Onboarding') {
|
||||
logger.metric('onboarding:contacts:nextPressed', {
|
||||
ax.metric('onboarding:contacts:nextPressed', {
|
||||
matchCount: allMatches.length,
|
||||
followCount: cumulativeFollowCount.current,
|
||||
dismissedMatchCount: state.dismissedMatches.length,
|
||||
@@ -516,6 +518,7 @@ function ContactItem({
|
||||
const gutter = useGutters([0, 'wide'])
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const name = contact.name ?? contact.firstName ?? contact.lastName
|
||||
@@ -564,7 +567,7 @@ function ContactItem({
|
||||
color="secondary"
|
||||
size="small"
|
||||
onPress={async () => {
|
||||
logger.metric('contacts:matches:invite', {
|
||||
ax.metric('contacts:matches:invite', {
|
||||
entryPoint: context,
|
||||
})
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user