[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
+24 -38
View File
@@ -13,8 +13,6 @@ import * as bcp47Match from 'bcp-47-match'
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {type MetricEvents} from '#/logger/metrics'
import {useLanguagePrefs} from '#/state/preferences/languages'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {RQKEY_ROOT as useActorSearchQueryKeyRoot} from '#/state/queries/actor-search'
@@ -68,6 +66,7 @@ import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
import {type Metrics, useAnalytics} from '#/analytics'
import {ExploreScreenLiveEventFeedsBanner} from '#/features/liveEvents/components/ExploreScreenLiveEventFeedsBanner'
import * as ModuleHeader from './components/ModuleHeader'
import {
@@ -124,7 +123,7 @@ type ExploreScreenItems =
bottomBorder?: boolean
searchButton?: {
label: string
metricsTag: MetricEvents['explore:module:searchButtonPress']['module']
metricsTag: Metrics['explore:module:searchButtonPress']['module']
tab: 'user' | 'profile' | 'feed'
}
}
@@ -135,7 +134,7 @@ type ExploreScreenItems =
icon: React.ComponentType<SVGIconProps>
searchButton?: {
label: string
metricsTag: MetricEvents['explore:module:searchButtonPress']['module']
metricsTag: Metrics['explore:module:searchButtonPress']['module']
tab: 'user' | 'profile' | 'feed'
}
hideDefaultTab?: boolean
@@ -213,6 +212,7 @@ export function Explore({
focusSearchInput: (tab: 'user' | 'profile' | 'feed') => void
headerHeight: number
}) {
const ax = useAnalytics()
const {_} = useLingui()
const t = useTheme()
const {data: preferences, error: preferencesError} = usePreferencesQuery()
@@ -273,9 +273,10 @@ export function Explore({
try {
await fetchNextFeedsPage()
} catch (err) {
logger.error('Failed to load more suggested follows', {message: err})
ax.logger.error('Failed to load more suggested follows', {message: err})
}
}, [
ax,
isFetchingNextFeedsPage,
hasNextFeedsPage,
feedsError,
@@ -333,9 +334,10 @@ export function Explore({
try {
await fetchNextPageFeedPreviews()
} catch (err) {
logger.error('Failed to load more feed previews', {message: err})
ax.logger.error('Failed to load more feed previews', {message: err})
}
}, [
ax,
isPendingFeedPreviews,
isFetchingNextPageFeedPreviews,
hasNextPageFeedPreviews,
@@ -492,11 +494,7 @@ export function Explore({
if (hasPressedLoadMoreFeeds && index < 6) {
continue
}
logger.metric(
'feed:suggestion:seen',
{feedUrl: item.feed.uri},
{statsig: false},
)
ax.metric('feed:suggestion:seen', {feedUrl: item.feed.uri})
}
}
if (!hasPressedLoadMoreFeeds) {
@@ -609,6 +607,7 @@ export function Explore({
return i
}, [
_,
ax,
useFullExperience,
suggestedFeeds,
preferences,
@@ -729,12 +728,7 @@ export function Explore({
<ModuleHeader.SearchButton
{...item.searchButton}
onPress={() =>
focusSearchInput(
(item.searchButton?.tab || 'user') as
| 'user'
| 'profile'
| 'feed',
)
focusSearchInput(item.searchButton?.tab || 'user')
}
/>
)}
@@ -751,12 +745,7 @@ export function Explore({
<ModuleHeader.SearchButton
{...item.searchButton}
onPress={() =>
focusSearchInput(
(item.searchButton?.tab || 'user') as
| 'user'
| 'profile'
| 'feed',
)
focusSearchInput(item.searchButton?.tab || 'user')
}
/>
)}
@@ -822,7 +811,7 @@ export function Explore({
if (!useFullExperience) {
return
}
logger.metric('feed:suggestion:press', {
ax.metric('feed:suggestion:press', {
feedUrl: item.feed.uri,
})
}}
@@ -1011,6 +1000,7 @@ export function Explore({
}
},
[
ax,
t.atoms.border_contrast_low,
t.atoms.bg_contrast_25,
t.atoms.text_contrast_medium,
@@ -1043,7 +1033,7 @@ export function Explore({
const seenProfilesRef = useRef<Set<string>>(new Set())
const onItemSeen = useCallback(
(item: ExploreScreenItems) => {
let module: MetricEvents['explore:module:seen']['module']
let module: Metrics['explore:module:seen']['module']
if (item.type === 'trendingTopics' || item.type === 'trendingVideos') {
module = item.type
} else if (item.type === 'profile') {
@@ -1054,17 +1044,13 @@ export function Explore({
const position = suggestedFollowsModule.findIndex(
i => i.type === 'profile' && i.profile.did === item.profile.did,
)
logger.metric(
'suggestedUser:seen',
{
logContext: 'Explore',
recId: item.recId,
position: position !== -1 ? position - 1 : 0, // -1 to account for header
suggestedDid: item.profile.did,
category: null,
},
{statsig: true},
)
ax.metric('suggestedUser:seen', {
logContext: 'Explore',
recId: item.recId,
position: position !== -1 ? position - 1 : 0, // -1 to account for header
suggestedDid: item.profile.did,
category: null,
})
}
} else if (item.type === 'feed') {
module = 'suggestedFeeds'
@@ -1077,10 +1063,10 @@ export function Explore({
}
if (!alreadyReportedRef.current.has(module)) {
alreadyReportedRef.current.set(module, module)
logger.metric('explore:module:seen', {module}, {statsig: false})
ax.metric('explore:module:seen', {module})
}
},
[suggestedFollowsModule],
[ax, suggestedFollowsModule],
)
return (