···1818 return sessionId
1919}
20202121+/**
2222+ * Gets the current session ID. Freshness depends on `useSessionId` being
2323+ * mounted, which handles refreshing this value between foreground/background
2424+ * transitions. Since that's mounted in `analytics/index.tsx`, this value can
2525+ * generally be trusted to be up to date.
2626+ */
2727+export function getSessionId() {
2828+ return device.get(['nativeSessionId'])
2929+}
3030+2131export function useSessionId() {
2232 const [id, setId] = useState(() => sessionId)
2333
+10
src/analytics/identifiers/session.web.ts
···2121 return sessionId
2222}
23232424+/**
2525+ * Gets the current session ID. Freshness depends on `useSessionId` being
2626+ * mounted, which handles refreshing this value between foreground/background
2727+ * transitions. Since that's mounted in `analytics/index.tsx`, this value can
2828+ * generally be trusted to be up to date.
2929+ */
3030+export function getSessionId() {
3131+ return window.sessionStorage.getItem(SESSION_ID_KEY)
3232+}
3333+2434export function useSessionId() {
2535 const [id, setId] = useState(() => sessionId)
2636
+20-15
src/analytics/index.tsx
···11-import {createContext, useContext, useEffect, useMemo} from 'react'
11+import {createContext, useContext, useMemo} from 'react'
22import {Platform} from 'react-native'
3344import {Logger} from '#/logger'
···2424import {type Metrics, metrics} from '#/analytics/metrics'
2525import * as refParams from '#/analytics/misc/refParams'
2626import * as env from '#/env'
2727-import {useGeolocation} from '#/geolocation'
2727+import {useGeolocationServiceResponse} from '#/geolocation/service'
2828import {device} from '#/storage'
29293030export * as utils from '#/analytics/utils'
···104104 referrerSrc: refParams.src,
105105 referrerUrl: refParams.url,
106106 },
107107- geolocation: device.get(['mergedGeolocation']) || {
107107+ geolocation: device.get(['geolocationServiceResponse']) || {
108108 countryCode: '',
109109 regionCode: '',
110110 },
···137137 }
138138 }
139139 const sessionId = useSessionId()
140140- const geolocation = useGeolocation()
140140+ const geolocation = useGeolocationServiceResponse()
141141 const parentContext = useContext(Context)
142142 const childContext = useMemo(() => {
143143 const combinedMetadata = {
···181181 const parentContext = useContext(Context)
182182183183 /**
184184- * Side-effect: we need to synchronously set this during the
185185- * same render cycle. It does not trigger a re-render, it just
186186- * sets properties on the singleton GrowthBook instance.
184184+ * Side-effects: we need to synchronously set these during the same render
185185+ * cycle. These calls do not trigger re-renders, they just set properties on
186186+ * the singleton GrowthBook instance.
187187 */
188188 setAttributes(parentContext.metadata)
189189-190190- useEffect(() => {
191191- feats.setTrackingCallback((experiment, result) => {
192192- parentContext.metric('experiment:viewed', {
193193- experimentId: experiment.key,
194194- variationId: result.key,
195195- })
189189+ feats.setTrackingCallback((experiment, result) => {
190190+ parentContext.metric('experiment:viewed', {
191191+ experimentId: experiment.key,
192192+ variationId: result.key,
193193+ })
194194+ })
195195+ feats.setFeatureUsageCallback((feature, result) => {
196196+ parentContext.metric('feature:viewed', {
197197+ featureId: feature,
198198+ featureResultValue: result.value,
199199+ experimentId: result.experiment?.key,
200200+ variationId: result.experimentResult?.key,
196201 })
197197- }, [parentContext.metric])
202202+ })
198203199204 const childContext = useMemo<AnalyticsContextType>(() => {
200205 return {
···1515 experimentId: string
1616 variationId: string
1717 }
1818+ 'feature:viewed': {
1919+ featureId: string
2020+ featureResultValue: unknown
2121+ /** Only available if feature has experiment rules applied */
2222+ experimentId?: string
2323+ /** Only available if feature has experiment rules applied */
2424+ variationId?: string
2525+ }
18261927 'account:loggedIn': {
2028 logContext:
+24
src/geolocation/util.ts
···33import {IS_ANDROID} from '#/env'
44import {logger} from '#/geolocation/logger'
55import {type Geolocation} from '#/geolocation/types'
66+import {device} from '#/storage'
6778/**
89 * Maps full US region names to their short codes.
···125126 })
126127 return geolocation
127128}
129129+130130+/**
131131+ * Gets the IP-based geolocation as a string in the format of
132132+ * "countryCode-regionCode", or just "countryCode" if regionCode is not
133133+ * available.
134134+ *
135135+ * IMPORTANT: this method should only return IP-based data, not the user's GPS
136136+ * based data. IP-based data we can already infer from requests, but for
137137+ * consistency between frontend and backend, we sometimes want to share the
138138+ * value we have on the frontend with the backend.
139139+ */
140140+export function getIPGeolocationString() {
141141+ const geo = device.get(['geolocationServiceResponse'])
142142+ if (!geo) return
143143+ const {countryCode, regionCode} = geo
144144+ if (countryCode) {
145145+ if (regionCode) {
146146+ return `${countryCode}-${regionCode}`
147147+ } else {
148148+ return countryCode
149149+ }
150150+ }
151151+}