Skip to content

Commit

Permalink
Implement our own lifecycle tracking to ensure it never fires while t…
Browse files Browse the repository at this point in the history
…he app is backgrounded (close #193)
  • Loading branch information
pfrazee committed Feb 15, 2023
1 parent 751c5e9 commit 4f2ae88
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 12 deletions.
5 changes: 3 additions & 2 deletions src/App.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {MobileShell} from './view/shell/mobile'
import {s} from './view/lib/styles'
import notifee, {EventType} from '@notifee/react-native'
import {segmentClient} from './lib/segmentClient'
import * as analytics from './lib/analytics'
import * as Toast from './view/com/util/Toast'

const App = observer(() => {
Expand All @@ -26,9 +26,10 @@ const App = observer(() => {
// init
useEffect(() => {
view.setup()
setSegment(segmentClient)
setSegment(analytics.segmentClient)
setupState().then(store => {
setRootStore(store)
analytics.init(store)
SplashScreen.hide()
Linking.getInitialURL().then((url: string | null) => {
if (url) {
Expand Down
68 changes: 68 additions & 0 deletions src/lib/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {AppState, AppStateStatus} from 'react-native'
import {createClient} from '@segment/analytics-react-native'
import {RootStoreModel, AppInfo} from '../state/models/root-store'
// import {createLogger} from './logger'

export const segmentClient = createClient({
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
trackAppLifecycleEvents: false,
// Uncomment to debug:
// logger: createLogger(),
})

export function init(store: RootStoreModel) {
// NOTE
// this method 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.onContextLoaded(() => {
if (AppState.currentState !== 'active') {
store.log.debug('Prevented a metrics ping while the app was backgrounded')
return
}
const context = segmentClient.context.get()
if (typeof context?.app === 'undefined') {
store.log.debug('Aborted metrics ping due to unavailable context')
return
}

const oldAppInfo = store.appInfo
const newAppInfo = context.app as AppInfo
store.setAppInfo(newAppInfo)
store.log.debug('Recording app info', {new: newAppInfo, old: oldAppInfo})

if (typeof oldAppInfo === 'undefined') {
segmentClient.track('Application Installed', {
version: newAppInfo.version,
build: newAppInfo.build,
})
} else if (newAppInfo.version !== oldAppInfo.version) {
segmentClient.track('Application Updated', {
version: newAppInfo.version,
build: newAppInfo.build,
previous_version: oldAppInfo.version,
previous_build: oldAppInfo.build,
})
}
segmentClient.track('Application Opened', {
from_background: false,
version: newAppInfo.version,
build: newAppInfo.build,
})
})

let lastState: AppStateStatus = AppState.currentState
AppState.addEventListener('change', (state: AppStateStatus) => {
if (state === 'active' && lastState !== 'active') {
const context = segmentClient.context.get()
segmentClient.track('Application Opened', {
from_background: true,
version: context?.app?.version,
build: context?.app?.build,
})
} else if (state !== 'active' && lastState === 'active') {
segmentClient.track('Application Backgrounded')
}
lastState = state
})
}
9 changes: 0 additions & 9 deletions src/lib/segmentClient.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/state/models/navigation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {RootStoreModel} from './root-store'
import {makeAutoObservable} from 'mobx'
import {TABS_ENABLED} from '../../build-flags'
import {segmentClient} from '../../lib/segmentClient'
import {segmentClient} from '../../lib/analytics'

let __id = 0
function genId() {
Expand Down
21 changes: 21 additions & 0 deletions src/state/models/root-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {AtpAgent} from '@atproto/api'
import {createContext, useContext} from 'react'
import {DeviceEventEmitter, EmitterSubscription} from 'react-native'
import BackgroundFetch from 'react-native-background-fetch'
import {z} from 'zod'
import {isObj, hasProp} from '../lib/type-guards'
import {LogModel} from './log'
import {SessionModel} from './session'
Expand All @@ -17,8 +18,17 @@ import {LinkMetasViewModel} from './link-metas-view'
import {MeModel} from './me'
import {OnboardModel} from './onboard'

export const appInfo = z.object({
build: z.string(),
name: z.string(),
namespace: z.string(),
version: z.string(),
})
export type AppInfo = z.infer<typeof appInfo>

export class RootStoreModel {
agent: AtpAgent
appInfo?: AppInfo
log = new LogModel()
session = new SessionModel(this)
nav = new NavigationModel(this)
Expand All @@ -42,8 +52,13 @@ export class RootStoreModel {
return this.agent.api
}

setAppInfo(info: AppInfo) {
this.appInfo = info
}

serialize(): unknown {
return {
appInfo: this.appInfo,
log: this.log.serialize(),
session: this.session.serialize(),
me: this.me.serialize(),
Expand All @@ -55,6 +70,12 @@ export class RootStoreModel {

hydrate(v: unknown) {
if (isObj(v)) {
if (hasProp(v, 'appInfo')) {
const appInfoParsed = appInfo.safeParse(v.appInfo)
if (appInfoParsed.success) {
this.setAppInfo(appInfoParsed.data)
}
}
if (hasProp(v, 'log')) {
this.log.hydrate(v.log)
}
Expand Down

0 comments on commit 4f2ae88

Please sign in to comment.