[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:
+67
-81
@@ -28,7 +28,7 @@ import {
|
||||
storePayloadForAccountSwitch,
|
||||
} from '#/lib/hooks/useNotificationHandler'
|
||||
import {useWebScrollRestoration} from '#/lib/hooks/useWebScrollRestoration'
|
||||
import {logger as notyLogger} from '#/lib/notifications/util'
|
||||
import {useCallOnce} from '#/lib/once'
|
||||
import {buildStateObject} from '#/lib/routes/helpers'
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
@@ -38,12 +38,11 @@ import {
|
||||
type MessagesTabNavigatorParams,
|
||||
type MyProfileTabNavigatorParams,
|
||||
type NotificationsTabNavigatorParams,
|
||||
type RouteParams,
|
||||
type SearchTabNavigatorParams,
|
||||
type State,
|
||||
} from '#/lib/routes/types'
|
||||
import {type RouteParams, type State} from '#/lib/routes/types'
|
||||
import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
|
||||
import {bskyTitle} from '#/lib/strings/headings'
|
||||
import {logger} from '#/logger'
|
||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
@@ -137,6 +136,8 @@ import {
|
||||
EmailDialogScreenID,
|
||||
useEmailDialogControl,
|
||||
} from '#/components/dialogs/EmailDialog'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {setNavigationMetadata} from '#/analytics/metadata'
|
||||
import {IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {router} from '#/routes'
|
||||
import {Referrer} from '../modules/expo-bluesky-swiss-army'
|
||||
@@ -879,11 +880,13 @@ const LINKING = {
|
||||
let lastHandledNotificationDateDedupe: number | undefined
|
||||
|
||||
function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
const ax = useAnalytics()
|
||||
const notyLogger = ax.logger.useChild(ax.logger.Context.Notifications)
|
||||
const theme = useColorSchemeStyle(DefaultTheme, DarkTheme)
|
||||
const {currentAccount, accounts} = useSession()
|
||||
const {onPressSwitchAccount} = useAccountSwitcher()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const prevLoggedRouteName = useRef<string | undefined>(undefined)
|
||||
const previousScreen = useRef<string | undefined>(undefined)
|
||||
const emailDialogControl = useEmailDialogControl()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
|
||||
@@ -945,11 +948,10 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
const payload = getNotificationPayload(response.notification)
|
||||
|
||||
if (payload) {
|
||||
notyLogger.metric(
|
||||
'notifications:openApp',
|
||||
{reason: payload.reason, causedBoot: true},
|
||||
{statsig: false},
|
||||
)
|
||||
ax.metric('notifications:openApp', {
|
||||
reason: payload.reason,
|
||||
causedBoot: true,
|
||||
})
|
||||
|
||||
if (payload.reason === 'chat-message') {
|
||||
handleChatMessage(payload)
|
||||
@@ -973,47 +975,69 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
prevLoggedRouteName.current = getCurrentRouteName()
|
||||
const onNavigationReady = useCallOnce(() => {
|
||||
const currentScreen = getCurrentRouteName()
|
||||
setNavigationMetadata({
|
||||
previousScreen: currentScreen,
|
||||
currentScreen,
|
||||
})
|
||||
previousScreen.current = currentScreen
|
||||
|
||||
handlePushNotificationEntry()
|
||||
|
||||
ax.metric('router:navigate', {})
|
||||
|
||||
if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) {
|
||||
emailDialogControl.open({
|
||||
id: EmailDialogScreenID.VerificationReminder,
|
||||
})
|
||||
snoozeEmailConfirmationPrompt()
|
||||
}
|
||||
}
|
||||
|
||||
ax.metric('init', {
|
||||
initMs: Math.round(
|
||||
// @ts-ignore Emitted by Metro in the bundle prelude
|
||||
performance.now() - global.__BUNDLE_START_TIME__,
|
||||
),
|
||||
})
|
||||
|
||||
if (IS_WEB) {
|
||||
const referrerInfo = Referrer.getReferrerInfo()
|
||||
if (referrerInfo && referrerInfo.hostname !== 'bsky.app') {
|
||||
ax.metric('deepLink:referrerReceived', {
|
||||
to: window.location.href,
|
||||
referrer: referrerInfo?.referrer,
|
||||
hostname: referrerInfo?.hostname,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavigationContainer
|
||||
ref={navigationRef}
|
||||
linking={LINKING}
|
||||
theme={theme}
|
||||
onStateChange={() => {
|
||||
logger.metric(
|
||||
'router:navigate',
|
||||
{from: prevLoggedRouteName.current},
|
||||
{statsig: false},
|
||||
)
|
||||
prevLoggedRouteName.current = getCurrentRouteName()
|
||||
}}
|
||||
onReady={() => {
|
||||
attachRouteToLogEvents(getCurrentRouteName)
|
||||
logModuleInitTime()
|
||||
onReady()
|
||||
logger.metric('router:navigate', {}, {statsig: false})
|
||||
handlePushNotificationEntry()
|
||||
}}
|
||||
// WARNING: Implicit navigation to nested navigators is depreciated in React Navigation 7.x
|
||||
// However, there's a fair amount of places we do that, especially in when popping to the top of stacks.
|
||||
// See BottomBar.tsx for an example of how to handle nested navigators in the tabs correctly.
|
||||
// I'm scared of missing a spot (esp. with push notifications etc) so let's enable this legacy behaviour for now.
|
||||
// We will need to confirm we handle nested navigators correctly by the time we migrate to React Navigation 8.x
|
||||
// -sfn
|
||||
navigationInChildEnabled>
|
||||
{children}
|
||||
</NavigationContainer>
|
||||
</>
|
||||
<NavigationContainer
|
||||
ref={navigationRef}
|
||||
linking={LINKING}
|
||||
theme={theme}
|
||||
onStateChange={() => {
|
||||
const currentScreen = getCurrentRouteName()
|
||||
// do this before metric
|
||||
setNavigationMetadata({
|
||||
previousScreen: previousScreen.current,
|
||||
currentScreen,
|
||||
})
|
||||
ax.metric('router:navigate', {from: previousScreen.current})
|
||||
previousScreen.current = currentScreen
|
||||
}}
|
||||
onReady={onNavigationReady}
|
||||
// WARNING: Implicit navigation to nested navigators is depreciated in React Navigation 7.x
|
||||
// However, there's a fair amount of places we do that, especially in when popping to the top of stacks.
|
||||
// See BottomBar.tsx for an example of how to handle nested navigators in the tabs correctly.
|
||||
// I'm scared of missing a spot (esp. with push notifications etc) so let's enable this legacy behaviour for now.
|
||||
// We will need to confirm we handle nested navigators correctly by the time we migrate to React Navigation 8.x
|
||||
// -sfn
|
||||
navigationInChildEnabled>
|
||||
{children}
|
||||
</NavigationContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1087,44 +1111,6 @@ function reset(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
let didInit = false
|
||||
function logModuleInitTime() {
|
||||
if (didInit) {
|
||||
return
|
||||
}
|
||||
didInit = true
|
||||
|
||||
const initMs = Math.round(
|
||||
// @ts-ignore Emitted by Metro in the bundle prelude
|
||||
performance.now() - global.__BUNDLE_START_TIME__,
|
||||
)
|
||||
console.log(`Time to first paint: ${initMs} ms`)
|
||||
logEvent('init', {
|
||||
initMs,
|
||||
})
|
||||
|
||||
if (IS_WEB) {
|
||||
const referrerInfo = Referrer.getReferrerInfo()
|
||||
if (referrerInfo && referrerInfo.hostname !== 'bsky.app') {
|
||||
logEvent('deepLink:referrerReceived', {
|
||||
to: window.location.href,
|
||||
referrer: referrerInfo?.referrer,
|
||||
hostname: referrerInfo?.hostname,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// This log is noisy, so keep false committed
|
||||
const shouldLog = false
|
||||
// Relies on our patch to polyfill.js in metro-runtime
|
||||
const initLogs = (global as any).__INIT_LOGS__
|
||||
if (shouldLog && Array.isArray(initLogs)) {
|
||||
console.log(initLogs.join('\n'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
FlatNavigator,
|
||||
navigate,
|
||||
|
||||
Reference in New Issue
Block a user