diff --git a/src/App.native.tsx b/src/App.native.tsx
index 442d7fe5..9b928778 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -16,7 +16,6 @@ import {ThemeProvider} from 'lib/ThemeContext'
import {s} from 'lib/styles'
import {Shell} from 'view/shell'
import * as notifications from 'lib/notifications/notifications'
-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'
@@ -71,15 +70,13 @@ function InnerApp() {
-
- {/* All components should be within this provider */}
-
-
-
-
-
-
-
+ {/* All components should be within this provider */}
+
+
+
+
+
+
diff --git a/src/App.web.tsx b/src/App.web.tsx
index 37115320..e29eec0d 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -9,7 +9,6 @@ import 'view/icons'
import {init as initPersistedState} from '#/state/persisted'
import {useColorMode} from 'state/shell'
-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'
@@ -58,15 +57,13 @@ function InnerApp() {
-
- {/* All components should be within this provider */}
-
-
-
-
-
-
-
+ {/* All components should be within this provider */}
+
+
+
+
+
+
diff --git a/src/lib/analytics/analytics.tsx b/src/lib/analytics/analytics.tsx
index 1650dfd3..64b66904 100644
--- a/src/lib/analytics/analytics.tsx
+++ b/src/lib/analytics/analytics.tsx
@@ -1,15 +1,10 @@
import React from 'react'
import {AppState, AppStateStatus} from 'react-native'
import AsyncStorage from '@react-native-async-storage/async-storage'
-import {
- createClient,
- AnalyticsProvider,
- useAnalytics as useAnalyticsOrig,
- ClientMethods,
-} from '@segment/analytics-react-native'
+import {createClient, SegmentClient} from '@segment/analytics-react-native'
import {useSession, SessionAccount} from '#/state/session'
import {sha256} from 'js-sha256'
-import {ScreenEvent, TrackEvent} from './types'
+import {TrackEvent, AnalyticsMethods} from './types'
import {logger} from '#/logger'
type AppInfo = {
@@ -19,53 +14,56 @@ type AppInfo = {
version?: string | undefined
}
-const segmentClient = createClient({
- writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
- trackAppLifecycleEvents: false,
- proxy: 'https://api.events.bsky.app/v1',
-})
+// Delay creating until first actual use.
+let segmentClient: SegmentClient | null = null
+function getClient(): SegmentClient {
+ if (!segmentClient) {
+ segmentClient = createClient({
+ writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
+ trackAppLifecycleEvents: false,
+ proxy: 'https://api.events.bsky.app/v1',
+ })
+ }
+ return segmentClient
+}
-export const track = segmentClient?.track?.bind?.(segmentClient) as TrackEvent
+export const track: TrackEvent = async (...args) => {
+ await getClient().track(...args)
+}
-export function useAnalytics() {
+export function useAnalytics(): AnalyticsMethods {
const {hasSession} = useSession()
- const methods: ClientMethods = useAnalyticsOrig()
return React.useMemo(() => {
if (hasSession) {
return {
- screen: methods.screen as ScreenEvent, // ScreenEvents defines all the possible screen names
- track: methods.track as TrackEvent, // TrackEvents defines all the possible track events and their properties
- identify: methods.identify,
- flush: methods.flush,
- group: methods.group,
- alias: methods.alias,
- reset: methods.reset,
+ async screen(...args) {
+ await getClient().screen(...args)
+ },
+ async track(...args) {
+ await getClient().track(...args)
+ },
}
}
// dont send analytics pings for anonymous users
return {
- screen: () => Promise,
- track: () => Promise,
- identify: () => Promise,
- flush: () => Promise,
- group: () => Promise,
- alias: () => Promise,
- reset: () => Promise,
+ screen: async () => {},
+ track: async () => {},
}
- }, [hasSession, methods])
+ }, [hasSession])
}
export function init(account: SessionAccount | undefined) {
setupListenersOnce()
if (account) {
+ const client = getClient()
if (account.did) {
const did_hashed = sha256(account.did)
- segmentClient.identify(did_hashed, {did_hashed})
+ client.identify(did_hashed, {did_hashed})
logger.debug('Ping w/hash')
} else {
logger.debug('Ping w/o hash')
- segmentClient.identify()
+ client.identify()
}
}
}
@@ -80,12 +78,13 @@ function setupListenersOnce() {
// 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
// -prf
- segmentClient.isReady.onChange(async () => {
+ const client = getClient()
+ client.isReady.onChange(async () => {
if (AppState.currentState !== 'active') {
logger.debug('Prevented a metrics ping while the app was backgrounded')
return
}
- const context = segmentClient.context.get()
+ const context = client.context.get()
if (typeof context?.app === 'undefined') {
logger.debug('Aborted metrics ping due to unavailable context')
return
@@ -97,19 +96,19 @@ function setupListenersOnce() {
logger.debug('Recording app info', {new: newAppInfo, old: oldAppInfo})
if (typeof oldAppInfo === 'undefined') {
- segmentClient.track('Application Installed', {
+ client.track('Application Installed', {
version: newAppInfo.version,
build: newAppInfo.build,
})
} else if (newAppInfo.version !== oldAppInfo.version) {
- segmentClient.track('Application Updated', {
+ client.track('Application Updated', {
version: newAppInfo.version,
build: newAppInfo.build,
previous_version: oldAppInfo.version,
previous_build: oldAppInfo.build,
})
}
- segmentClient.track('Application Opened', {
+ client.track('Application Opened', {
from_background: false,
version: newAppInfo.version,
build: newAppInfo.build,
@@ -119,25 +118,19 @@ function setupListenersOnce() {
let lastState: AppStateStatus = AppState.currentState
AppState.addEventListener('change', (state: AppStateStatus) => {
if (state === 'active' && lastState !== 'active') {
- const context = segmentClient.context.get()
- segmentClient.track('Application Opened', {
+ const context = client.context.get()
+ client.track('Application Opened', {
from_background: true,
version: context?.app?.version,
build: context?.app?.build,
})
} else if (state !== 'active' && lastState === 'active') {
- segmentClient.track('Application Backgrounded')
+ client.track('Application Backgrounded')
}
lastState = state
})
}
-export function Provider({children}: React.PropsWithChildren<{}>) {
- return (
- {children}
- )
-}
-
async function writeAppInfo(value: AppInfo) {
await AsyncStorage.setItem('BSKY_APP_INFO', JSON.stringify(value))
}
diff --git a/src/lib/analytics/analytics.web.tsx b/src/lib/analytics/analytics.web.tsx
index df03ee13..71c32d75 100644
--- a/src/lib/analytics/analytics.web.tsx
+++ b/src/lib/analytics/analytics.web.tsx
@@ -1,65 +1,68 @@
import React from 'react'
-import {
- createClient,
- AnalyticsProvider,
- useAnalytics as useAnalyticsOrig,
-} from '@segment/analytics-react'
+import {createClient} from '@segment/analytics-react'
import {sha256} from 'js-sha256'
+import {TrackEvent, AnalyticsMethods} from './types'
import {useSession, SessionAccount} from '#/state/session'
import {logger} from '#/logger'
-const segmentClient = createClient(
- {
- writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
- },
- {
- integrations: {
- 'Segment.io': {
- apiHost: 'api.events.bsky.app/v1',
- },
- },
- },
-)
-export const track = segmentClient?.track?.bind?.(segmentClient)
+type SegmentClient = ReturnType
-export function useAnalytics() {
+// Delay creating until first actual use.
+let segmentClient: SegmentClient | null = null
+function getClient(): SegmentClient {
+ if (!segmentClient) {
+ segmentClient = createClient(
+ {
+ writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
+ },
+ {
+ integrations: {
+ 'Segment.io': {
+ apiHost: 'api.events.bsky.app/v1',
+ },
+ },
+ },
+ )
+ }
+ return segmentClient
+}
+
+export const track: TrackEvent = async (...args) => {
+ await getClient().track(...args)
+}
+
+export function useAnalytics(): AnalyticsMethods {
const {hasSession} = useSession()
- const methods = useAnalyticsOrig()
return React.useMemo(() => {
if (hasSession) {
- return methods
+ return {
+ async screen(...args) {
+ await getClient().screen(...args)
+ },
+ async track(...args) {
+ await getClient().track(...args)
+ },
+ }
}
// dont send analytics pings for anonymous users
return {
- screen: () => {},
- track: () => {},
- identify: () => {},
- flush: () => {},
- group: () => {},
- alias: () => {},
- reset: () => {},
+ screen: async () => {},
+ track: async () => {},
}
- }, [hasSession, methods])
+ }, [hasSession])
}
export function init(account: SessionAccount | undefined) {
if (account) {
+ const client = getClient()
if (account.did) {
- if (account.did) {
- const did_hashed = sha256(account.did)
- segmentClient.identify(did_hashed, {did_hashed})
- logger.debug('Ping w/hash')
- } else {
- logger.debug('Ping w/o hash')
- segmentClient.identify()
- }
+ const did_hashed = sha256(account.did)
+ client.identify(did_hashed, {did_hashed})
+ logger.debug('Ping w/hash')
+ } else {
+ logger.debug('Ping w/o hash')
+ client.identify()
}
}
}
-
-export function Provider({children}: React.PropsWithChildren<{}>) {
- return (
- {children}
- )
-}
diff --git a/src/lib/analytics/types.ts b/src/lib/analytics/types.ts
index d677f321..9883cc36 100644
--- a/src/lib/analytics/types.ts
+++ b/src/lib/analytics/types.ts
@@ -7,6 +7,7 @@ export type ScreenEvent = (
name: keyof ScreenPropertiesMap,
properties?: ScreenPropertiesMap[keyof ScreenPropertiesMap],
) => Promise
+
interface TrackPropertiesMap {
// LOGIN / SIGN UP events
'Sign In': {resumedSession: boolean} // CAN BE SERVER
@@ -150,3 +151,8 @@ interface ScreenPropertiesMap {
MutedAccounts: {}
SavedFeeds: {}
}
+
+export type AnalyticsMethods = {
+ screen: ScreenEvent
+ track: TrackEvent
+}
diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx
index 3a7781ea..3624e656 100644
--- a/src/view/com/feeds/FeedPage.tsx
+++ b/src/view/com/feeds/FeedPage.tsx
@@ -4,7 +4,7 @@ import {
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native'
-import {useAnalytics} from '@segment/analytics-react-native'
+import {useAnalytics} from 'lib/analytics/analytics'
import {useQueryClient} from '@tanstack/react-query'
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'