diff --git a/src/analytics/features/index.ts b/src/analytics/features/index.ts index b28ba88474..81eb9eced0 100644 --- a/src/analytics/features/index.ts +++ b/src/analytics/features/index.ts @@ -1,10 +1,26 @@ +import {MMKV} from '@bsky.app/react-native-mmkv' +import {setPolyfills} from '@growthbook/growthbook' import {GrowthBook} from '@growthbook/growthbook-react' -import {type Metadata} from '#/analytics/metadata' +import {getNavigationMetadata, type Metadata} from '#/analytics/metadata' import * as env from '#/env' export {Features} from '#/analytics/features/types' +const CACHE = new MMKV({id: 'bsky_features_cache'}) + +setPolyfills({ + localStorage: { + getItem: key => { + const value = CACHE.getString(key) + return value != null ? JSON.parse(value) : null + }, + setItem: async (key, value) => { + CACHE.set(key, JSON.stringify(value)) + }, + }, +}) + /** * We vary the amount of time we wait for GrowthBook to fetch feature * gates based on the strategy specified. @@ -46,17 +62,28 @@ export async function refresh({strategy}: {strategy: FeatureFetchStrategy}) { } /** - * Converts our metadata into GrowthBook attributes and sets them. + * Converts our metadata into GrowthBook attributes and sets them. GrowthBook + * attributes are manually configured in the GrowthBook dashboard. So these + * values need to match exactly. Therefore, let's add them here manually to and + * not spread them to avoid mistakes. */ -export function setAttributes({base, session, preferences}: Metadata) { - const {deviceId, sessionId, ...br} = base +export function setAttributes({ + base, + geolocation, + session, + preferences, +}: Metadata) { features.setAttributes({ - device_id: deviceId, // GrowthBook special field - session_id: sessionId, // GrowthBook special field - user_id: session?.did, // GrowthBook special field - id: session?.did, // GrowthBook special field - ...br, - ...(session || {}), - ...(preferences || {}), + deviceId: base.deviceId, + sessionId: base.sessionId, + platform: base.platform, + appVersion: base.appVersion, + countryCode: geolocation.countryCode, + regionCode: geolocation.regionCode, + did: session?.did, + isBskyPds: session?.isBskyPds, + appLanguage: preferences?.appLanguage, + contentLanguages: preferences?.contentLanguages, + currentScreen: getNavigationMetadata()?.currentScreen, }) } diff --git a/src/analytics/features/types.ts b/src/analytics/features/types.ts index 5cb52e8918..3987808691 100644 --- a/src/analytics/features/types.ts +++ b/src/analytics/features/types.ts @@ -1,7 +1,12 @@ export enum Features { - DebugFeedContext = 'debug_show_feedcontext', - IsBskyTeam = 'is_bsky_team_member', - DisableOnboardingFindContacts = 'disable_onboarding_find_contacts', - DisableSettingsFindContacts = 'disable_settings_find_contacts', - DisableLiveNowBeta = 'disable_live_now_beta', + // core flags + IsBskyTeam = 'is_bsky_team', + + // debug flags + DebugFeedContext = 'debug_feed_context', + + // feature flags + ImportContactsOnboardingDisable = 'import_contacts:onboarding:disable', + ImportContactsSettingsDisable = 'import_contacts:settings:disable', + LiveNowBetaDisable = 'live_now_beta:disable', } diff --git a/src/analytics/index.tsx b/src/analytics/index.tsx index 9d80ee54ba..3596464ebd 100644 --- a/src/analytics/index.tsx +++ b/src/analytics/index.tsx @@ -16,13 +16,13 @@ import { useSessionId, } from '#/analytics/identifiers' import { + getMetadataForLogger, getNavigationMetadata, type MergeableMetadata, type Metadata, } from '#/analytics/metadata' import {type Metrics, metrics} from '#/analytics/metrics' import * as refParams from '#/analytics/misc/refParams' -import {getMetadataForLogger} from '#/analytics/utils' import * as env from '#/env' import {useGeolocation} from '#/geolocation' import {device} from '#/storage' diff --git a/src/analytics/metadata.ts b/src/analytics/metadata.ts index f097c17a76..9039c38686 100644 --- a/src/analytics/metadata.ts +++ b/src/analytics/metadata.ts @@ -53,9 +53,27 @@ export type NavigationMetadata = { } let navigationMetadata: NavigationMetadata | undefined export function getNavigationMetadata() { - console.log('metadata', JSON.stringify(navigationMetadata, null, 2)) return navigationMetadata } export function setNavigationMetadata(meta: NavigationMetadata | undefined) { navigationMetadata = meta } + +/** + * We don't want or need to send all data to the logger + */ +export function getMetadataForLogger({ + base, + geolocation, + session, +}: Metadata): Record { + return { + deviceId: base.deviceId, + sessionId: base.sessionId, + platform: base.platform, + appVersion: base.appVersion, + countryCode: geolocation.countryCode, + regionCode: geolocation.regionCode, + isBskyPds: session?.isBskyPds || 'anonymous', + } +} diff --git a/src/analytics/utils.ts b/src/analytics/utils.ts index 38f7528824..95d2efb589 100644 --- a/src/analytics/utils.ts +++ b/src/analytics/utils.ts @@ -4,7 +4,6 @@ import {BSKY_SERVICE} from '#/lib/constants' import {type SessionAccount} from '#/state/session' import { type MergeableMetadata, - type Metadata, type SessionMetadata, } from '#/analytics/metadata' @@ -32,19 +31,3 @@ export function accountToSessionMetadata( } } } - -export function getMetadataForLogger({ - base, - geolocation, - session, -}: Metadata): Record { - return { - deviceId: base.deviceId, - sessionId: base.sessionId, - platform: base.platform, - appVersion: base.appVersion, - countryCode: geolocation.countryCode, - regionCode: geolocation.regionCode, - isBskyPds: session?.isBskyPds || 'anonymous', - } -} diff --git a/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx b/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx index 37ce11179a..474aad3923 100644 --- a/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx +++ b/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx @@ -24,7 +24,7 @@ export const enabled = createIsEnabledCheck(props => { '2026-01-16T00:00:00.000Z', props.currentProfile.createdAt, ) && - !props.features.enabled(props.features.DisableLiveNowBeta) + !props.features.enabled(props.features.LiveNowBetaDisable) ) }) diff --git a/src/screens/Onboarding/index.tsx b/src/screens/Onboarding/index.tsx index fe0acff570..25d9173a84 100644 --- a/src/screens/Onboarding/index.tsx +++ b/src/screens/Onboarding/index.tsx @@ -48,7 +48,7 @@ export function Onboarding() { ENV !== 'e2e' && IS_NATIVE && findContactsEnabled && - !ax.features.enabled(ax.features.DisableOnboardingFindContacts) + !ax.features.enabled(ax.features.ImportContactsOnboardingDisable) const [state, dispatch] = useReducer( reducer, diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx index 7f3e711dd9..4f6fbb602b 100644 --- a/src/screens/Settings/Settings.tsx +++ b/src/screens/Settings/Settings.tsx @@ -213,7 +213,7 @@ export function SettingsScreen({}: Props) { {IS_NATIVE && findContactsEnabled && - !ax.features.enabled(ax.features.DisableSettingsFindContacts) && ( + !ax.features.enabled(ax.features.ImportContactsSettingsDisable) && ( diff --git a/src/state/service-config.tsx b/src/state/service-config.tsx index 8393929477..12ab19506e 100644 --- a/src/state/service-config.tsx +++ b/src/state/service-config.tsx @@ -106,7 +106,7 @@ export function useCanGoLive() { const ax = useAnalytics() const {hasSession} = useSession() if (!hasSession) return false - return IS_DEV ? true : !ax.features.enabled(ax.features.DisableLiveNowBeta) + return IS_DEV ? true : !ax.features.enabled(ax.features.LiveNowBetaDisable) } export function useCheckEmailConfirmed() {