[APP-1805] Analytics tweaks and tracking callbacks (#9861)
This commit is contained in:
@@ -18,6 +18,16 @@ export function getInitialSessionId() {
|
||||
return sessionId
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current session ID. Freshness depends on `useSessionId` being
|
||||
* mounted, which handles refreshing this value between foreground/background
|
||||
* transitions. Since that's mounted in `analytics/index.tsx`, this value can
|
||||
* generally be trusted to be up to date.
|
||||
*/
|
||||
export function getSessionId() {
|
||||
return device.get(['nativeSessionId'])
|
||||
}
|
||||
|
||||
export function useSessionId() {
|
||||
const [id, setId] = useState(() => sessionId)
|
||||
|
||||
|
||||
@@ -21,6 +21,16 @@ export function getInitialSessionId() {
|
||||
return sessionId
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current session ID. Freshness depends on `useSessionId` being
|
||||
* mounted, which handles refreshing this value between foreground/background
|
||||
* transitions. Since that's mounted in `analytics/index.tsx`, this value can
|
||||
* generally be trusted to be up to date.
|
||||
*/
|
||||
export function getSessionId() {
|
||||
return window.sessionStorage.getItem(SESSION_ID_KEY)
|
||||
}
|
||||
|
||||
export function useSessionId() {
|
||||
const [id, setId] = useState(() => sessionId)
|
||||
|
||||
|
||||
+20
-15
@@ -1,4 +1,4 @@
|
||||
import {createContext, useContext, useEffect, useMemo} from 'react'
|
||||
import {createContext, useContext, useMemo} from 'react'
|
||||
import {Platform} from 'react-native'
|
||||
|
||||
import {Logger} from '#/logger'
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import {type Metrics, metrics} from '#/analytics/metrics'
|
||||
import * as refParams from '#/analytics/misc/refParams'
|
||||
import * as env from '#/env'
|
||||
import {useGeolocation} from '#/geolocation'
|
||||
import {useGeolocationServiceResponse} from '#/geolocation/service'
|
||||
import {device} from '#/storage'
|
||||
|
||||
export * as utils from '#/analytics/utils'
|
||||
@@ -104,7 +104,7 @@ const Context = createContext<AnalyticsBaseContextType>({
|
||||
referrerSrc: refParams.src,
|
||||
referrerUrl: refParams.url,
|
||||
},
|
||||
geolocation: device.get(['mergedGeolocation']) || {
|
||||
geolocation: device.get(['geolocationServiceResponse']) || {
|
||||
countryCode: '',
|
||||
regionCode: '',
|
||||
},
|
||||
@@ -137,7 +137,7 @@ export function AnalyticsContext({
|
||||
}
|
||||
}
|
||||
const sessionId = useSessionId()
|
||||
const geolocation = useGeolocation()
|
||||
const geolocation = useGeolocationServiceResponse()
|
||||
const parentContext = useContext(Context)
|
||||
const childContext = useMemo(() => {
|
||||
const combinedMetadata = {
|
||||
@@ -181,20 +181,25 @@ export function AnalyticsFeaturesContext({
|
||||
const parentContext = useContext(Context)
|
||||
|
||||
/**
|
||||
* Side-effect: we need to synchronously set this during the
|
||||
* same render cycle. It does not trigger a re-render, it just
|
||||
* sets properties on the singleton GrowthBook instance.
|
||||
* Side-effects: we need to synchronously set these during the same render
|
||||
* cycle. These calls do not trigger re-renders, they just set properties on
|
||||
* the singleton GrowthBook instance.
|
||||
*/
|
||||
setAttributes(parentContext.metadata)
|
||||
|
||||
useEffect(() => {
|
||||
feats.setTrackingCallback((experiment, result) => {
|
||||
parentContext.metric('experiment:viewed', {
|
||||
experimentId: experiment.key,
|
||||
variationId: result.key,
|
||||
})
|
||||
feats.setTrackingCallback((experiment, result) => {
|
||||
parentContext.metric('experiment:viewed', {
|
||||
experimentId: experiment.key,
|
||||
variationId: result.key,
|
||||
})
|
||||
}, [parentContext.metric])
|
||||
})
|
||||
feats.setFeatureUsageCallback((feature, result) => {
|
||||
parentContext.metric('feature:viewed', {
|
||||
featureId: feature,
|
||||
featureResultValue: result.value,
|
||||
experimentId: result.experiment?.key,
|
||||
variationId: result.experimentResult?.key,
|
||||
})
|
||||
})
|
||||
|
||||
const childContext = useMemo<AnalyticsContextType>(() => {
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {Logger} from '#/logger'
|
||||
import * as env from '#/env'
|
||||
|
||||
type Event<M extends Record<string, any>> = {
|
||||
source: 'app'
|
||||
time: number
|
||||
event: keyof M
|
||||
payload: M[keyof M]
|
||||
@@ -43,7 +44,8 @@ export class MetricsClient<M extends Record<string, any>> {
|
||||
) {
|
||||
this.start()
|
||||
|
||||
const e = {
|
||||
const e: Event<M> = {
|
||||
source: 'app',
|
||||
time: Date.now(),
|
||||
event,
|
||||
payload,
|
||||
|
||||
@@ -15,6 +15,14 @@ export type Events = {
|
||||
experimentId: string
|
||||
variationId: string
|
||||
}
|
||||
'feature:viewed': {
|
||||
featureId: string
|
||||
featureResultValue: unknown
|
||||
/** Only available if feature has experiment rules applied */
|
||||
experimentId?: string
|
||||
/** Only available if feature has experiment rules applied */
|
||||
variationId?: string
|
||||
}
|
||||
|
||||
'account:loggedIn': {
|
||||
logContext:
|
||||
|
||||
@@ -3,6 +3,7 @@ import {type LocationGeocodedAddress} from 'expo-location'
|
||||
import {IS_ANDROID} from '#/env'
|
||||
import {logger} from '#/geolocation/logger'
|
||||
import {type Geolocation} from '#/geolocation/types'
|
||||
import {device} from '#/storage'
|
||||
|
||||
/**
|
||||
* Maps full US region names to their short codes.
|
||||
@@ -125,3 +126,26 @@ export function mergeGeolocations(
|
||||
})
|
||||
return geolocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the IP-based geolocation as a string in the format of
|
||||
* "countryCode-regionCode", or just "countryCode" if regionCode is not
|
||||
* available.
|
||||
*
|
||||
* IMPORTANT: this method should only return IP-based data, not the user's GPS
|
||||
* based data. IP-based data we can already infer from requests, but for
|
||||
* consistency between frontend and backend, we sometimes want to share the
|
||||
* value we have on the frontend with the backend.
|
||||
*/
|
||||
export function getIPGeolocationString() {
|
||||
const geo = device.get(['geolocationServiceResponse'])
|
||||
if (!geo) return
|
||||
const {countryCode, regionCode} = geo
|
||||
if (countryCode) {
|
||||
if (regionCode) {
|
||||
return `${countryCode}-${regionCode}`
|
||||
} else {
|
||||
return countryCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user