From 5c2a799280d11456e6820e85f4cd42cdceae6dc3 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 20 Jan 2026 11:41:13 -0600 Subject: [PATCH] Move everything out of React --- src/App.native.tsx | 109 +++++++++--------- src/App.web.tsx | 101 ++++++++-------- src/components/PostControls/DiscoverDebug.tsx | 2 +- .../growthbook/{context.tsx => index.tsx} | 58 ++-------- src/logger/index.tsx | 2 +- src/logger/types.ts | 1 + src/state/session/agent.ts | 2 +- 7 files changed, 112 insertions(+), 163 deletions(-) rename src/logger/growthbook/{context.tsx => index.tsx} (76%) diff --git a/src/App.native.tsx b/src/App.native.tsx index 5a1da373d8..2d4f49f25d 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -22,10 +22,7 @@ import {s} from '#/lib/styles' import {ThemeProvider} from '#/lib/ThemeContext' import I18nProvider from '#/locale/i18nProvider' import {logger} from '#/logger' -import { - initializer as growthbookInitializer, - Provider as GrowthBookProvider, -} from '#/logger/growthbook/context' +import {initializer as growthbookInitializer} from '#/logger/growthbook' import {Provider as A11yProvider} from '#/state/a11y' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' @@ -147,59 +144,57 @@ function InnerApp() { - - - - - - - - - {/* LabelDefsProvider MUST come before ModerationOptsProvider */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + {/* LabelDefsProvider MUST come before ModerationOptsProvider */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.web.tsx b/src/App.web.tsx index 6cd4ca679a..34205a0bf3 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -13,10 +13,7 @@ import {Provider as StatsigProvider} from '#/lib/statsig/statsig' import {ThemeProvider} from '#/lib/ThemeContext' import I18nProvider from '#/locale/i18nProvider' import {logger} from '#/logger' -import { - initializer as growthbookInitializer, - Provider as GrowthBookProvider, -} from '#/logger/growthbook/context' +import {initializer as growthbookInitializer} from '#/logger/growthbook' import {Provider as A11yProvider} from '#/state/a11y' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' @@ -126,55 +123,53 @@ function InnerApp() { - - - - - - - - - {/* LabelDefsProvider MUST come before ModerationOptsProvider */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + {/* LabelDefsProvider MUST come before ModerationOptsProvider */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/PostControls/DiscoverDebug.tsx b/src/components/PostControls/DiscoverDebug.tsx index 524fd62bf9..02d852003f 100644 --- a/src/components/PostControls/DiscoverDebug.tsx +++ b/src/components/PostControls/DiscoverDebug.tsx @@ -4,7 +4,7 @@ import {t} from '@lingui/macro' import {DISCOVER_DEBUG_DIDS} from '#/lib/constants' import {useGate} from '#/lib/statsig/statsig' -import {logEvent} from '#/logger/growthbook/context' +import {logEvent} from '#/logger/growthbook' import {useSession} from '#/state/session' import {atoms as a, useTheme} from '#/alf' import * as Toast from '#/components/Toast' diff --git a/src/logger/growthbook/context.tsx b/src/logger/growthbook/index.tsx similarity index 76% rename from src/logger/growthbook/context.tsx rename to src/logger/growthbook/index.tsx index 24d837ebfe..8cfc081cce 100644 --- a/src/logger/growthbook/context.tsx +++ b/src/logger/growthbook/index.tsx @@ -1,20 +1,19 @@ -import {useCallback, useEffect} from 'react' +import {useCallback} from 'react' import {Platform} from 'react-native' import {growthbookTrackingPlugin} from '@growthbook/growthbook/plugins' -import {GrowthBook, GrowthBookProvider} from '@growthbook/growthbook-react' +import {GrowthBook} from '@growthbook/growthbook-react' import {BSKY_SERVICE} from '#/lib/constants' import { getAndMigrateStableId, getSessionId, getStableId, - getStableIdOrThrow, } from '#/logger/growthbook/identifiers' import * as referrer from '#/logger/growthbook/util/referrer' import * as persisted from '#/state/persisted' -import {type SessionAccount, useSession} from '#/state/session' +import {type SessionAccount} from '#/state/session' import * as env from '#/env' -import {type Geolocation, useGeolocation} from '#/geolocation' +import {device} from '#/storage' const TIMEOUT_INIT = 500 // TODO should base on p99 or something const TIMEOUT_PREFER_LOW_LATENCY = 250 @@ -63,18 +62,12 @@ type UserAttributes = GrowthBookDefaultUserAttributes & { } type Attributes = DefaultAttributes & UserAttributes -/** - * We cache the geolocation outside of React to use when setting - * default attributes outside React, such as during `refresh()`. - */ -let unsafeGeolocation: Geolocation | null = null - const gb = new GrowthBook({ apiHost: env.GROWTHBOOK_API_HOST, clientKey: env.GROWTHBOOK_CLIENT_KEY, plugins: [growthbookTrackingPlugin()], trackingCallback: (experiment, result) => { - console.log('Experiment Viewed', { + console.debug('Experiment Viewed', { experimentId: experiment.key, variationId: result.key, }) @@ -83,10 +76,7 @@ const gb = new GrowthBook({ variationId: result.key, }) }, - attributes: { - device_id: getStableId() || 'unset', - session_id: getSessionId(), - } satisfies GrowthBookDefaultAttributes, + attributes: getDefaultAttributes(), }) /** @@ -147,47 +137,15 @@ export function useGate() { }, []) } -/** - * Main provider for GrowthBook and feature flag context. Should be rendered - * _after_ `initializer` is complete, and _after_ the Session provider. - */ -export function Provider({children}: {children: React.ReactNode}) { - const geo = useGeolocation() - const {currentAccount} = useSession() - - /** - * Decorate existing attributes with any new default attributes - */ - useEffect(() => { - // cache outside react - unsafeGeolocation = geo - const attr = { - ...gb.getAttributes(), - ...getDefaultAttributes(), - } - gb.setAttributes(attr) - console.debug(`update attributes`, {attributes: attr}) - }, [geo]) - - /** - * Update user attributes on session change, and clear them on logout - */ - useEffect(() => { - setAttributesForAccount(currentAccount) - }, [currentAccount]) - - return {children} -} - /** * Get the default attributes that should always be set * on the GrowthBook instance */ function getDefaultAttributes() { return { - device_id: getStableIdOrThrow(), + device_id: getStableId() || 'unset', session_id: getSessionId(), - country: unsafeGeolocation?.countryCode || 'unknown', + country: device.get(['mergedGeolocation'])?.countryCode || 'unknown', } } diff --git a/src/logger/index.tsx b/src/logger/index.tsx index 3da8908c79..8e2a9b1a6c 100644 --- a/src/logger/index.tsx +++ b/src/logger/index.tsx @@ -1,6 +1,6 @@ import {nanoid} from 'nanoid/non-secure' -import {logEvent} from '#/lib/statsig/statsig' +import {logEvent} from '#/logger/growthbook' import {add} from '#/logger/logDump' import {type MetricEvents} from '#/logger/metrics' import {consoleTransport} from '#/logger/transports/console' diff --git a/src/logger/types.ts b/src/logger/types.ts index 19e12c5045..cf68cc1e01 100644 --- a/src/logger/types.ts +++ b/src/logger/types.ts @@ -15,6 +15,7 @@ export enum LogContext { AgeAssurance = 'age-assurance', PolicyUpdate = 'policy-update', Geolocation = 'geolocation', + GrowthBook = 'growthbook', /** * METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts index 7f804feedc..5f99b9a4ef 100644 --- a/src/state/session/agent.ts +++ b/src/state/session/agent.ts @@ -24,7 +24,7 @@ import { } from '#/lib/constants' import {getAge} from '#/lib/strings/time' import {logger} from '#/logger' -import {refresh as refreshGates} from '#/logger/growthbook/context' +import {refresh as refreshGates} from '#/logger/growthbook' import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate' import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' import {