[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:
Eric Bailey
2026-01-22 15:33:45 -06:00
committed by GitHub
parent 8614e5e105
commit 25a1ed1209
190 changed files with 2358 additions and 1689 deletions
+6 -4
View File
@@ -58,6 +58,7 @@ import {useQueryClient} from '@tanstack/react-query'
import * as apilib from '#/lib/api/index'
import {EmbeddingDisabledError} from '#/lib/api/resolve'
import {useAppState} from '#/lib/appState'
import {retry} from '#/lib/async/retry'
import {until} from '#/lib/async/until'
import {
@@ -65,14 +66,12 @@ import {
SUPPORTED_MIME_TYPES,
type SupportedMimeTypes,
} from '#/lib/constants'
import {useAppState} from '#/lib/hooks/useAppState'
import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {mimeToExt} from '#/lib/media/video/util'
import {type NavigationProp} from '#/lib/routes/types'
import {logEvent} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {colors} from '#/lib/styles'
import {logger} from '#/logger'
@@ -129,6 +128,7 @@ import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed'
import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {Text as NewText} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
import {PostLanguageSelect} from './select-language/PostLanguageSelect'
@@ -178,6 +178,7 @@ export const ComposePost = ({
cancelRef?: React.RefObject<CancelRef | null>
}) => {
const {currentAccount} = useSession()
const ax = useAnalytics()
const agent = useAgent()
const queryClient = useQueryClient()
const currentDid = currentAccount!.did
@@ -520,7 +521,7 @@ export const ComposePost = ({
if (postUri) {
let index = 0
for (let post of thread.posts) {
logEvent('post:create', {
ax.metric('post:create', {
imageCount:
post.embed.media?.type === 'images'
? post.embed.media.images.length
@@ -536,7 +537,7 @@ export const ComposePost = ({
}
}
if (thread.posts.length > 1) {
logEvent('thread:create', {
ax.metric('thread:create', {
postCount: thread.posts.length,
isReply: !!replyTo,
})
@@ -594,6 +595,7 @@ export const ComposePost = ({
}, 500)
}, [
_,
ax,
agent,
thread,
canPost,
@@ -3,12 +3,12 @@ import {Keyboard} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logEvent} from '#/lib/statsig/statsig'
import {type Gif} from '#/state/queries/tenor'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {GifSelectDialog} from '#/components/dialogs/GifSelect'
import {GifSquare_Stroke2_Corner0_Rounded as GifIcon} from '#/components/icons/Gif'
import {useAnalytics} from '#/analytics'
type Props = {
onClose?: () => void
@@ -17,15 +17,16 @@ type Props = {
}
export function SelectGifBtn({onClose, onSelectGif, disabled}: Props) {
const ax = useAnalytics()
const {_} = useLingui()
const ref = useRef<{open: () => void}>(null)
const t = useTheme()
const onPressSelectGif = useCallback(async () => {
logEvent('composer:gif:open', {})
ax.metric('composer:gif:open', {})
Keyboard.dismiss()
ref.current?.open()
}, [])
}, [ax])
return (
<>
@@ -24,6 +24,7 @@ import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Glo
import {Group3_Stroke2_Corner0_Rounded as GroupIcon} from '#/components/icons/Group'
import * as Tooltip from '#/components/Tooltip'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import {useThreadgateNudged} from '#/storage/hooks/threadgate-nudged'
@@ -42,6 +43,7 @@ export function ThreadgateBtn({
style?: StyleProp<AnimatedStyle<ViewStyle>>
}) {
const {_} = useLingui()
const ax = useAnalytics()
const control = Dialog.useDialogControl()
const [threadgateNudged, setThreadgateNudged] = useThreadgateNudged()
const [showTooltip, setShowTooltip] = useState(false)
@@ -66,7 +68,7 @@ export function ThreadgateBtn({
const [persist, setPersist] = useState(false)
const onPress = () => {
logger.metric('composer:threadgate:open', {
ax.metric('composer:threadgate:open', {
nudged: tooltipWasShown,
})