From 9cfc5eaa221d6cd6dc16b479790b989536050c6a Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 19 Feb 2026 11:55:26 -0600 Subject: [PATCH] Improve header setup --- src/App.native.tsx | 3 ++ src/App.web.tsx | 3 ++ src/analytics/useSetAnalyticsHeaders.tsx | 36 ++++++++++++++++++++++++ src/analytics/utils.ts | 24 ---------------- 4 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 src/analytics/useSetAnalyticsHeaders.tsx diff --git a/src/App.native.tsx b/src/App.native.tsx index 7dc16ec058..b8abef0bb9 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -78,6 +78,7 @@ import { features, setupDeviceId, } from '#/analytics' +import {useSetAnalyticsHeaders} from '#/analytics/useSetAnalyticsHeaders' import {IS_ANDROID, IS_IOS} from '#/env' import { prefetchLiveEvents, @@ -117,6 +118,8 @@ function InnerApp() { const {_} = useLingui() const hasCheckedReferrer = useStarterPackEntry() + useSetAnalyticsHeaders() + // init useEffect(() => { async function onLaunch(account?: SessionAccount) { diff --git a/src/App.web.tsx b/src/App.web.tsx index 701167f392..6351a2e51c 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -68,6 +68,7 @@ import { features, setupDeviceId, } from '#/analytics' +import {useSetAnalyticsHeaders} from '#/analytics/useSetAnalyticsHeaders' import { prefetchLiveEvents, Provider as LiveEventsProvider, @@ -93,6 +94,8 @@ function InnerApp() { const {_} = useLingui() const hasCheckedReferrer = useStarterPackEntry() + useSetAnalyticsHeaders() + // init useEffect(() => { async function onLaunch(account?: SessionAccount) { diff --git a/src/analytics/useSetAnalyticsHeaders.tsx b/src/analytics/useSetAnalyticsHeaders.tsx new file mode 100644 index 0000000000..cce362a8f7 --- /dev/null +++ b/src/analytics/useSetAnalyticsHeaders.tsx @@ -0,0 +1,36 @@ +import {useAgent} from '#/state/session' +import {getDeviceId, useSessionId} from '#/analytics/identifiers' +import {getIPGeolocationString} from '#/geolocation/util' + +/** + * Get anonymous identifiers and construct headers we use for requests that may + * trigger experiment exposures in our backend. These values are used for A/B + * test bucketing, in addition to the user DID, if the request is + * authenticated. They ensure we can consistently deliver beta features to + * users. + * + * This should be mounted as high as possible in the tree, _after_ + * `setupDeviceId` has resolved and session context is available. + * + * The setters here are not wrapped in an effect because this hook does not + * render that often, and we want to make sure the headers are set as soon as + * possible after `agent` changes to ensure analytics events are properly + * attributed. + * + * The values here are not specific to an account. If we add new values here in + * the future, we should ensure that they are not account specific and handle + * those separately (and more carefully). + * + * These headers must stay in sync with our appview. + * @see https://github.com/bluesky-social/atproto/blob/39cf199df5847d3fd4a60d8cdeb604a0e07f9784/packages/bsky/src/feature-gates/utils.ts#L7-L8 + */ +export function useSetAnalyticsHeaders() { + const agent = useAgent() + const sessionId = useSessionId() + agent.setHeader('X-Bsky-Device-Id', getDeviceId() ?? '') + agent.setHeader('X-Bsky-Session-Id', sessionId) + const geo = getIPGeolocationString() + if (geo) { + agent.setHeader('X-Bsky-IP-Geolocation', geo) + } +} diff --git a/src/analytics/utils.ts b/src/analytics/utils.ts index 2fed18c165..95d2efb589 100644 --- a/src/analytics/utils.ts +++ b/src/analytics/utils.ts @@ -2,12 +2,10 @@ import {useMemo} from 'react' import {BSKY_SERVICE} from '#/lib/constants' import {type SessionAccount} from '#/state/session' -import {getDeviceId, getSessionId} from '#/analytics/identifiers' import { type MergeableMetadata, type SessionMetadata, } from '#/analytics/metadata' -import {getIPGeolocationString} from '#/geolocation/util' /** * Thin `useMemo` wrapper that marks the metadata as memoized and provides a @@ -33,25 +31,3 @@ export function accountToSessionMetadata( } } } - -/** - * Get anonymous identifiers and construct headers we use for requests that may - * trigger experiment exposures in our backend. These values are used for A/B - * test bucketing, in addition to the user DID, if the request is - * authenticated. They ensure we can consistently deliver beta features to - * users. - * - * These headers must stay in sync with our appview. - * @see https://github.com/bluesky-social/atproto/blob/39cf199df5847d3fd4a60d8cdeb604a0e07f9784/packages/bsky/src/feature-gates/utils.ts#L7-L8 - */ -export function getAnalyticsHeaders() { - return { - 'X-Bsky-Device-Id': getDeviceId(), - 'X-Bsky-Session-Id': getSessionId(), - /** - * This can already be inferred from server requests, but for consistency - * in feature bucketing, we also include it here. - */ - 'X-Bsky-IP-Geolocation': getIPGeolocationString(), - } -}