Move analytics out of init (#2115)

* Remove listenSessionLoaded from analytics

* Move analytics init call to navigation ready

* Remove zod dependency from analytics

* Mirror changes on the web

* Delete listenSessionLoaded

* Only set up listeners once
zio/stable
dan 2023-12-06 20:04:05 +00:00 committed by GitHub
parent 748212e000
commit 6335be14e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 35 additions and 48 deletions

View File

@ -16,7 +16,7 @@ import {ThemeProvider} from 'lib/ThemeContext'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {Shell} from 'view/shell' import {Shell} from 'view/shell'
import * as notifications from 'lib/notifications/notifications' import * as notifications from 'lib/notifications/notifications'
import * as analytics from 'lib/analytics/analytics' import {Provider as AnalyticsProvider} from 'lib/analytics/analytics'
import * as Toast from 'view/com/util/Toast' import * as Toast from 'view/com/util/Toast'
import {queryClient} from 'lib/react-query' import {queryClient} from 'lib/react-query'
import {TestCtrls} from 'view/com/testing/TestCtrls' import {TestCtrls} from 'view/com/testing/TestCtrls'
@ -45,7 +45,6 @@ function InnerApp() {
// init // init
useEffect(() => { useEffect(() => {
analytics.init()
notifications.init(queryClient) notifications.init(queryClient)
listenSessionDropped(() => { listenSessionDropped(() => {
Toast.show('Sorry! Your session expired. Please log in again.') Toast.show('Sorry! Your session expired. Please log in again.')
@ -72,7 +71,7 @@ function InnerApp() {
<LoggedOutViewProvider> <LoggedOutViewProvider>
<UnreadNotifsProvider> <UnreadNotifsProvider>
<ThemeProvider theme={colorMode}> <ThemeProvider theme={colorMode}>
<analytics.Provider> <AnalyticsProvider>
{/* All components should be within this provider */} {/* All components should be within this provider */}
<RootSiblingParent> <RootSiblingParent>
<GestureHandlerRootView style={s.h100pct}> <GestureHandlerRootView style={s.h100pct}>
@ -80,7 +79,7 @@ function InnerApp() {
<Shell /> <Shell />
</GestureHandlerRootView> </GestureHandlerRootView>
</RootSiblingParent> </RootSiblingParent>
</analytics.Provider> </AnalyticsProvider>
</ThemeProvider> </ThemeProvider>
</UnreadNotifsProvider> </UnreadNotifsProvider>
</LoggedOutViewProvider> </LoggedOutViewProvider>

View File

@ -9,7 +9,7 @@ import 'view/icons'
import {init as initPersistedState} from '#/state/persisted' import {init as initPersistedState} from '#/state/persisted'
import {useColorMode} from 'state/shell' import {useColorMode} from 'state/shell'
import * as analytics from 'lib/analytics/analytics' import {Provider as AnalyticsProvider} from 'lib/analytics/analytics'
import {Shell} from 'view/shell/index' import {Shell} from 'view/shell/index'
import {ToastContainer} from 'view/com/util/Toast.web' import {ToastContainer} from 'view/com/util/Toast.web'
import {ThemeProvider} from 'lib/ThemeContext' import {ThemeProvider} from 'lib/ThemeContext'
@ -37,7 +37,6 @@ function InnerApp() {
// init // init
useEffect(() => { useEffect(() => {
analytics.init()
const account = persisted.get('session').currentAccount const account = persisted.get('session').currentAccount
resumeSession(account) resumeSession(account)
}, [resumeSession]) }, [resumeSession])
@ -59,7 +58,7 @@ function InnerApp() {
<LoggedOutViewProvider> <LoggedOutViewProvider>
<UnreadNotifsProvider> <UnreadNotifsProvider>
<ThemeProvider theme={colorMode}> <ThemeProvider theme={colorMode}>
<analytics.Provider> <AnalyticsProvider>
{/* All components should be within this provider */} {/* All components should be within this provider */}
<RootSiblingParent> <RootSiblingParent>
<SafeAreaProvider> <SafeAreaProvider>
@ -67,7 +66,7 @@ function InnerApp() {
</SafeAreaProvider> </SafeAreaProvider>
</RootSiblingParent> </RootSiblingParent>
<ToastContainer /> <ToastContainer />
</analytics.Provider> </AnalyticsProvider>
</ThemeProvider> </ThemeProvider>
</UnreadNotifsProvider> </UnreadNotifsProvider>
</LoggedOutViewProvider> </LoggedOutViewProvider>

View File

@ -41,6 +41,7 @@ import {
shouldRequestEmailConfirmation, shouldRequestEmailConfirmation,
setEmailConfirmationRequested, setEmailConfirmationRequested,
} from './state/shell/reminders' } from './state/shell/reminders'
import {init as initAnalytics} from './lib/analytics/analytics'
import {HomeScreen} from './view/screens/Home' import {HomeScreen} from './view/screens/Home'
import {SearchScreen} from './view/screens/Search' import {SearchScreen} from './view/screens/Search'
@ -474,6 +475,8 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
const {openModal} = useModalControls() const {openModal} = useModalControls()
function onReady() { function onReady() {
initAnalytics(currentAccount)
if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) { if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) {
openModal({name: 'verify-email', showReminder: true}) openModal({name: 'verify-email', showReminder: true})
setEmailConfirmationRequested() setEmailConfirmationRequested()

View File

@ -7,20 +7,17 @@ import {
useAnalytics as useAnalyticsOrig, useAnalytics as useAnalyticsOrig,
ClientMethods, ClientMethods,
} from '@segment/analytics-react-native' } from '@segment/analytics-react-native'
import {z} from 'zod' import {useSession, SessionAccount} from '#/state/session'
import {useSession} from '#/state/session'
import {sha256} from 'js-sha256' import {sha256} from 'js-sha256'
import {ScreenEvent, TrackEvent} from './types' import {ScreenEvent, TrackEvent} from './types'
import {logger} from '#/logger' import {logger} from '#/logger'
import {listenSessionLoaded} from '#/state/events'
export const appInfo = z.object({ type AppInfo = {
build: z.string().optional(), build?: string | undefined
name: z.string().optional(), name?: string | undefined
namespace: z.string().optional(), namespace?: string | undefined
version: z.string().optional(), version?: string | undefined
}) }
export type AppInfo = z.infer<typeof appInfo>
const segmentClient = createClient({ const segmentClient = createClient({
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
@ -58,8 +55,10 @@ export function useAnalytics() {
}, [hasSession, methods]) }, [hasSession, methods])
} }
export function init() { export function init(account: SessionAccount | undefined) {
listenSessionLoaded(account => { setupListenersOnce()
if (account) {
if (account.did) { if (account.did) {
const did_hashed = sha256(account.did) const did_hashed = sha256(account.did)
segmentClient.identify(did_hashed, {did_hashed}) segmentClient.identify(did_hashed, {did_hashed})
@ -68,8 +67,15 @@ export function init() {
logger.debug('Ping w/o hash') logger.debug('Ping w/o hash')
segmentClient.identify() segmentClient.identify()
} }
}) }
}
let didSetupListeners = false
function setupListenersOnce() {
if (didSetupListeners) {
return
}
didSetupListeners = true
// NOTE // NOTE
// this is a copy of segment's own lifecycle event tracking // this is a copy of segment's own lifecycle event tracking
// we handle it manually to ensure that it never fires while the app is backgrounded // we handle it manually to ensure that it never fires while the app is backgrounded

View File

@ -6,9 +6,8 @@ import {
} from '@segment/analytics-react' } from '@segment/analytics-react'
import {sha256} from 'js-sha256' import {sha256} from 'js-sha256'
import {useSession} from '#/state/session' import {useSession, SessionAccount} from '#/state/session'
import {logger} from '#/logger' import {logger} from '#/logger'
import {listenSessionLoaded} from '#/state/events'
const segmentClient = createClient( const segmentClient = createClient(
{ {
@ -44,8 +43,8 @@ export function useAnalytics() {
}, [hasSession, methods]) }, [hasSession, methods])
} }
export function init() { export function init(account: SessionAccount | undefined) {
listenSessionLoaded(account => { if (account) {
if (account.did) { if (account.did) {
if (account.did) { if (account.did) {
const did_hashed = sha256(account.did) const did_hashed = sha256(account.did)
@ -56,7 +55,7 @@ export function init() {
segmentClient.identify() segmentClient.identify()
} }
} }
}) }
} }
export function Provider({children}: React.PropsWithChildren<{}>) { export function Provider({children}: React.PropsWithChildren<{}>) {

View File

@ -1,6 +1,4 @@
import EventEmitter from 'eventemitter3' import EventEmitter from 'eventemitter3'
import {BskyAgent} from '@atproto/api'
import {SessionAccount} from './session'
type UnlistenFn = () => void type UnlistenFn = () => void
@ -16,19 +14,6 @@ export function listenSoftReset(fn: () => void): UnlistenFn {
return () => emitter.off('soft-reset', fn) return () => emitter.off('soft-reset', fn)
} }
export function emitSessionLoaded(
sessionAccount: SessionAccount,
agent: BskyAgent,
) {
emitter.emit('session-loaded', sessionAccount, agent)
}
export function listenSessionLoaded(
fn: (sessionAccount: SessionAccount, agent: BskyAgent) => void,
): UnlistenFn {
emitter.on('session-loaded', fn)
return () => emitter.off('session-loaded', fn)
}
export function emitSessionDropped() { export function emitSessionDropped() {
emitter.emit('session-dropped') emitter.emit('session-dropped')
} }

View File

@ -8,7 +8,7 @@ import {logger} from '#/logger'
import * as persisted from '#/state/persisted' import * as persisted from '#/state/persisted'
import {PUBLIC_BSKY_AGENT} from '#/state/queries' import {PUBLIC_BSKY_AGENT} from '#/state/queries'
import {IS_PROD} from '#/lib/constants' import {IS_PROD} from '#/lib/constants'
import {emitSessionLoaded, emitSessionDropped} from '../events' import {emitSessionDropped} from '../events'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util' import {useCloseAllActiveElements} from '#/state/util'
import {track} from '#/lib/analytics/analytics' import {track} from '#/lib/analytics/analytics'
@ -210,7 +210,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
__globalAgent = agent __globalAgent = agent
queryClient.clear() queryClient.clear()
upsertAccount(account) upsertAccount(account)
emitSessionLoaded(account, agent)
logger.debug( logger.debug(
`session: created account`, `session: created account`,
@ -262,7 +261,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
__globalAgent = agent __globalAgent = agent
queryClient.clear() queryClient.clear()
upsertAccount(account) upsertAccount(account)
emitSessionLoaded(account, agent)
logger.debug( logger.debug(
`session: logged in`, `session: logged in`,
@ -355,12 +353,11 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
__globalAgent = agent __globalAgent = agent
queryClient.clear() queryClient.clear()
upsertAccount(account) upsertAccount(account)
emitSessionLoaded(account, agent)
// Intentionally not awaited to unblock the UI: // Intentionally not awaited to unblock the UI:
resumeSessionWithFreshAccount().then(async freshAccount => { resumeSessionWithFreshAccount().then(freshAccount => {
if (JSON.stringify(account) !== JSON.stringify(freshAccount)) { if (JSON.stringify(account) !== JSON.stringify(freshAccount)) {
upsertAccount(freshAccount) upsertAccount(freshAccount)
emitSessionLoaded(freshAccount, agent)
} }
}) })
} else { } else {
@ -368,7 +365,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
__globalAgent = agent __globalAgent = agent
queryClient.clear() queryClient.clear()
upsertAccount(freshAccount) upsertAccount(freshAccount)
emitSessionLoaded(freshAccount, agent)
} }
async function resumeSessionWithFreshAccount(): Promise<SessionAccount> { async function resumeSessionWithFreshAccount(): Promise<SessionAccount> {