diff --git a/src/App.native.tsx b/src/App.native.tsx index 5ffbb6a8..442d7fe5 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -16,7 +16,7 @@ import {ThemeProvider} from 'lib/ThemeContext' import {s} from 'lib/styles' import {Shell} from 'view/shell' 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 {queryClient} from 'lib/react-query' import {TestCtrls} from 'view/com/testing/TestCtrls' @@ -45,7 +45,6 @@ function InnerApp() { // init useEffect(() => { - analytics.init() notifications.init(queryClient) listenSessionDropped(() => { Toast.show('Sorry! Your session expired. Please log in again.') @@ -72,7 +71,7 @@ function InnerApp() { - + {/* All components should be within this provider */} @@ -80,7 +79,7 @@ function InnerApp() { - + diff --git a/src/App.web.tsx b/src/App.web.tsx index 74702dca..37115320 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -9,7 +9,7 @@ import 'view/icons' import {init as initPersistedState} from '#/state/persisted' 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 {ToastContainer} from 'view/com/util/Toast.web' import {ThemeProvider} from 'lib/ThemeContext' @@ -37,7 +37,6 @@ function InnerApp() { // init useEffect(() => { - analytics.init() const account = persisted.get('session').currentAccount resumeSession(account) }, [resumeSession]) @@ -59,7 +58,7 @@ function InnerApp() { - + {/* All components should be within this provider */} @@ -67,7 +66,7 @@ function InnerApp() { - + diff --git a/src/Navigation.tsx b/src/Navigation.tsx index a26f3693..3f6b5ba2 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -41,6 +41,7 @@ import { shouldRequestEmailConfirmation, setEmailConfirmationRequested, } from './state/shell/reminders' +import {init as initAnalytics} from './lib/analytics/analytics' import {HomeScreen} from './view/screens/Home' import {SearchScreen} from './view/screens/Search' @@ -474,6 +475,8 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { const {openModal} = useModalControls() function onReady() { + initAnalytics(currentAccount) + if (currentAccount && shouldRequestEmailConfirmation(currentAccount)) { openModal({name: 'verify-email', showReminder: true}) setEmailConfirmationRequested() diff --git a/src/lib/analytics/analytics.tsx b/src/lib/analytics/analytics.tsx index 3a8254eb..1650dfd3 100644 --- a/src/lib/analytics/analytics.tsx +++ b/src/lib/analytics/analytics.tsx @@ -7,20 +7,17 @@ import { useAnalytics as useAnalyticsOrig, ClientMethods, } from '@segment/analytics-react-native' -import {z} from 'zod' -import {useSession} from '#/state/session' +import {useSession, SessionAccount} from '#/state/session' import {sha256} from 'js-sha256' import {ScreenEvent, TrackEvent} from './types' import {logger} from '#/logger' -import {listenSessionLoaded} from '#/state/events' -export const appInfo = z.object({ - build: z.string().optional(), - name: z.string().optional(), - namespace: z.string().optional(), - version: z.string().optional(), -}) -export type AppInfo = z.infer +type AppInfo = { + build?: string | undefined + name?: string | undefined + namespace?: string | undefined + version?: string | undefined +} const segmentClient = createClient({ writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', @@ -58,8 +55,10 @@ export function useAnalytics() { }, [hasSession, methods]) } -export function init() { - listenSessionLoaded(account => { +export function init(account: SessionAccount | undefined) { + setupListenersOnce() + + if (account) { if (account.did) { const did_hashed = sha256(account.did) segmentClient.identify(did_hashed, {did_hashed}) @@ -68,8 +67,15 @@ export function init() { logger.debug('Ping w/o hash') segmentClient.identify() } - }) + } +} +let didSetupListeners = false +function setupListenersOnce() { + if (didSetupListeners) { + return + } + didSetupListeners = true // NOTE // 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 diff --git a/src/lib/analytics/analytics.web.tsx b/src/lib/analytics/analytics.web.tsx index 0a5d5d68..df03ee13 100644 --- a/src/lib/analytics/analytics.web.tsx +++ b/src/lib/analytics/analytics.web.tsx @@ -6,9 +6,8 @@ import { } from '@segment/analytics-react' import {sha256} from 'js-sha256' -import {useSession} from '#/state/session' +import {useSession, SessionAccount} from '#/state/session' import {logger} from '#/logger' -import {listenSessionLoaded} from '#/state/events' const segmentClient = createClient( { @@ -44,8 +43,8 @@ export function useAnalytics() { }, [hasSession, methods]) } -export function init() { - listenSessionLoaded(account => { +export function init(account: SessionAccount | undefined) { + if (account) { if (account.did) { if (account.did) { const did_hashed = sha256(account.did) @@ -56,7 +55,7 @@ export function init() { segmentClient.identify() } } - }) + } } export function Provider({children}: React.PropsWithChildren<{}>) { diff --git a/src/state/events.ts b/src/state/events.ts index f8586082..1384abde 100644 --- a/src/state/events.ts +++ b/src/state/events.ts @@ -1,6 +1,4 @@ import EventEmitter from 'eventemitter3' -import {BskyAgent} from '@atproto/api' -import {SessionAccount} from './session' type UnlistenFn = () => void @@ -16,19 +14,6 @@ export function listenSoftReset(fn: () => void): UnlistenFn { 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() { emitter.emit('session-dropped') } diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 49c1326d..76b77a08 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -8,7 +8,7 @@ import {logger} from '#/logger' import * as persisted from '#/state/persisted' import {PUBLIC_BSKY_AGENT} from '#/state/queries' import {IS_PROD} from '#/lib/constants' -import {emitSessionLoaded, emitSessionDropped} from '../events' +import {emitSessionDropped} from '../events' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' import {track} from '#/lib/analytics/analytics' @@ -210,7 +210,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) { __globalAgent = agent queryClient.clear() upsertAccount(account) - emitSessionLoaded(account, agent) logger.debug( `session: created account`, @@ -262,7 +261,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) { __globalAgent = agent queryClient.clear() upsertAccount(account) - emitSessionLoaded(account, agent) logger.debug( `session: logged in`, @@ -355,12 +353,11 @@ export function Provider({children}: React.PropsWithChildren<{}>) { __globalAgent = agent queryClient.clear() upsertAccount(account) - emitSessionLoaded(account, agent) + // Intentionally not awaited to unblock the UI: - resumeSessionWithFreshAccount().then(async freshAccount => { + resumeSessionWithFreshAccount().then(freshAccount => { if (JSON.stringify(account) !== JSON.stringify(freshAccount)) { upsertAccount(freshAccount) - emitSessionLoaded(freshAccount, agent) } }) } else { @@ -368,7 +365,6 @@ export function Provider({children}: React.PropsWithChildren<{}>) { __globalAgent = agent queryClient.clear() upsertAccount(freshAccount) - emitSessionLoaded(freshAccount, agent) } async function resumeSessionWithFreshAccount(): Promise {