[APP-1805] Analytics tweaks and tracking callbacks (#9861)
This commit is contained in:
+2
-1
@@ -93,7 +93,8 @@
|
|||||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||||
"@fortawesome/react-native-fontawesome": "^0.3.2",
|
"@fortawesome/react-native-fontawesome": "^0.3.2",
|
||||||
"@growthbook/growthbook-react": "^1.6.2",
|
"@growthbook/growthbook": "^1.6.5",
|
||||||
|
"@growthbook/growthbook-react": "^1.6.5",
|
||||||
"@haileyok/bluesky-video": "0.3.2",
|
"@haileyok/bluesky-video": "0.3.2",
|
||||||
"@ipld/dag-cbor": "^9.2.0",
|
"@ipld/dag-cbor": "^9.2.0",
|
||||||
"@lingui/react": "^4.14.1",
|
"@lingui/react": "^4.14.1",
|
||||||
|
|||||||
@@ -18,6 +18,16 @@ export function getInitialSessionId() {
|
|||||||
return sessionId
|
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() {
|
export function useSessionId() {
|
||||||
const [id, setId] = useState(() => sessionId)
|
const [id, setId] = useState(() => sessionId)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,16 @@ export function getInitialSessionId() {
|
|||||||
return sessionId
|
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() {
|
export function useSessionId() {
|
||||||
const [id, setId] = useState(() => sessionId)
|
const [id, setId] = useState(() => sessionId)
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -1,4 +1,4 @@
|
|||||||
import {createContext, useContext, useEffect, useMemo} from 'react'
|
import {createContext, useContext, useMemo} from 'react'
|
||||||
import {Platform} from 'react-native'
|
import {Platform} from 'react-native'
|
||||||
|
|
||||||
import {Logger} from '#/logger'
|
import {Logger} from '#/logger'
|
||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
import {type Metrics, metrics} from '#/analytics/metrics'
|
import {type Metrics, metrics} from '#/analytics/metrics'
|
||||||
import * as refParams from '#/analytics/misc/refParams'
|
import * as refParams from '#/analytics/misc/refParams'
|
||||||
import * as env from '#/env'
|
import * as env from '#/env'
|
||||||
import {useGeolocation} from '#/geolocation'
|
import {useGeolocationServiceResponse} from '#/geolocation/service'
|
||||||
import {device} from '#/storage'
|
import {device} from '#/storage'
|
||||||
|
|
||||||
export * as utils from '#/analytics/utils'
|
export * as utils from '#/analytics/utils'
|
||||||
@@ -104,7 +104,7 @@ const Context = createContext<AnalyticsBaseContextType>({
|
|||||||
referrerSrc: refParams.src,
|
referrerSrc: refParams.src,
|
||||||
referrerUrl: refParams.url,
|
referrerUrl: refParams.url,
|
||||||
},
|
},
|
||||||
geolocation: device.get(['mergedGeolocation']) || {
|
geolocation: device.get(['geolocationServiceResponse']) || {
|
||||||
countryCode: '',
|
countryCode: '',
|
||||||
regionCode: '',
|
regionCode: '',
|
||||||
},
|
},
|
||||||
@@ -137,7 +137,7 @@ export function AnalyticsContext({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sessionId = useSessionId()
|
const sessionId = useSessionId()
|
||||||
const geolocation = useGeolocation()
|
const geolocation = useGeolocationServiceResponse()
|
||||||
const parentContext = useContext(Context)
|
const parentContext = useContext(Context)
|
||||||
const childContext = useMemo(() => {
|
const childContext = useMemo(() => {
|
||||||
const combinedMetadata = {
|
const combinedMetadata = {
|
||||||
@@ -181,20 +181,25 @@ export function AnalyticsFeaturesContext({
|
|||||||
const parentContext = useContext(Context)
|
const parentContext = useContext(Context)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Side-effect: we need to synchronously set this during the
|
* Side-effects: we need to synchronously set these during the same render
|
||||||
* same render cycle. It does not trigger a re-render, it just
|
* cycle. These calls do not trigger re-renders, they just set properties on
|
||||||
* sets properties on the singleton GrowthBook instance.
|
* the singleton GrowthBook instance.
|
||||||
*/
|
*/
|
||||||
setAttributes(parentContext.metadata)
|
setAttributes(parentContext.metadata)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
feats.setTrackingCallback((experiment, result) => {
|
feats.setTrackingCallback((experiment, result) => {
|
||||||
parentContext.metric('experiment:viewed', {
|
parentContext.metric('experiment:viewed', {
|
||||||
experimentId: experiment.key,
|
experimentId: experiment.key,
|
||||||
variationId: result.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>(() => {
|
const childContext = useMemo<AnalyticsContextType>(() => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {Logger} from '#/logger'
|
|||||||
import * as env from '#/env'
|
import * as env from '#/env'
|
||||||
|
|
||||||
type Event<M extends Record<string, any>> = {
|
type Event<M extends Record<string, any>> = {
|
||||||
|
source: 'app'
|
||||||
time: number
|
time: number
|
||||||
event: keyof M
|
event: keyof M
|
||||||
payload: M[keyof M]
|
payload: M[keyof M]
|
||||||
@@ -43,7 +44,8 @@ export class MetricsClient<M extends Record<string, any>> {
|
|||||||
) {
|
) {
|
||||||
this.start()
|
this.start()
|
||||||
|
|
||||||
const e = {
|
const e: Event<M> = {
|
||||||
|
source: 'app',
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
event,
|
event,
|
||||||
payload,
|
payload,
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ export type Events = {
|
|||||||
experimentId: string
|
experimentId: string
|
||||||
variationId: 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': {
|
'account:loggedIn': {
|
||||||
logContext:
|
logContext:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {type LocationGeocodedAddress} from 'expo-location'
|
|||||||
import {IS_ANDROID} from '#/env'
|
import {IS_ANDROID} from '#/env'
|
||||||
import {logger} from '#/geolocation/logger'
|
import {logger} from '#/geolocation/logger'
|
||||||
import {type Geolocation} from '#/geolocation/types'
|
import {type Geolocation} from '#/geolocation/types'
|
||||||
|
import {device} from '#/storage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps full US region names to their short codes.
|
* Maps full US region names to their short codes.
|
||||||
@@ -125,3 +126,26 @@ export function mergeGeolocations(
|
|||||||
})
|
})
|
||||||
return geolocation
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4592,12 +4592,12 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^3.3.1"
|
nanoid "^3.3.1"
|
||||||
|
|
||||||
"@growthbook/growthbook-react@^1.6.2":
|
"@growthbook/growthbook-react@^1.6.5":
|
||||||
version "1.6.2"
|
version "1.6.5"
|
||||||
resolved "https://registry.yarnpkg.com/@growthbook/growthbook-react/-/growthbook-react-1.6.2.tgz#847135be0c46b167f980dbe6015e5f4ed010475c"
|
resolved "https://registry.yarnpkg.com/@growthbook/growthbook-react/-/growthbook-react-1.6.5.tgz#e849cff64a54e56d5c566512bdc839148ae613ed"
|
||||||
integrity sha512-96Bo2Jwd4NBn/kBLN4ceF299PhTw8fQltRykD32hu2xMW8/LXhB8swxbPshGK+Xfa2gjgt24kpZ5oSvaVLLT7w==
|
integrity sha512-afi/RUbwazVNKv2acn6wDQz4BJNRAEpwIuHfggQup2/aE5PLAxy3+95gjjRMgCcPR0Pf3sFmhYGvOmxLD0ZRbQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@growthbook/growthbook" "^1.6.2"
|
"@growthbook/growthbook" "^1.6.5"
|
||||||
|
|
||||||
"@growthbook/growthbook@^1.6.2":
|
"@growthbook/growthbook@^1.6.2":
|
||||||
version "1.6.2"
|
version "1.6.2"
|
||||||
@@ -4606,6 +4606,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
dom-mutator "^0.6.0"
|
dom-mutator "^0.6.0"
|
||||||
|
|
||||||
|
"@growthbook/growthbook@^1.6.5":
|
||||||
|
version "1.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz#c9e2119187ee3288525a77a7c64353276f5ba91b"
|
||||||
|
integrity sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A==
|
||||||
|
dependencies:
|
||||||
|
dom-mutator "^0.6.0"
|
||||||
|
|
||||||
"@grpc/grpc-js@^1.8.20":
|
"@grpc/grpc-js@^1.8.20":
|
||||||
version "1.13.3"
|
version "1.13.3"
|
||||||
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.13.3.tgz#6ad08d186c2a8651697085f790c5c68eaca45904"
|
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.13.3.tgz#6ad08d186c2a8651697085f790c5c68eaca45904"
|
||||||
|
|||||||
Reference in New Issue
Block a user