diff --git a/src/App.native.tsx b/src/App.native.tsx
index fe57ba86d3..1568f7b8ae 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -17,7 +17,6 @@ import * as Sentry from '@sentry/react-native'
import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController'
import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder'
import {QueryProvider} from '#/lib/react-query'
-import {Provider as StatsigProvider} from '#/lib/statsig/statsig'
import {s} from '#/lib/styles'
import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider'
@@ -153,53 +152,51 @@ 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 0a632ccdbf..a0eff936f5 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -9,7 +9,6 @@ import {useLingui} from '@lingui/react'
import * as Sentry from '@sentry/react-native'
import {QueryProvider} from '#/lib/react-query'
-import {Provider as StatsigProvider} from '#/lib/statsig/statsig'
import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger'
@@ -132,49 +131,47 @@ function InnerApp() {
-
-
-
-
-
- {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/analytics/index.tsx b/src/analytics/index.tsx
index 28a8fe1ea9..3639c893ad 100644
--- a/src/analytics/index.tsx
+++ b/src/analytics/index.tsx
@@ -24,6 +24,7 @@ import {device} from '#/storage'
export * as utils from '#/analytics/utils'
export const features = {init, refresh}
+export {Features} from '#/analytics/features'
export {type Metrics} from '#/analytics/metrics'
type LoggerType = {
diff --git a/src/components/PostControls/DiscoverDebug.tsx b/src/components/PostControls/DiscoverDebug.tsx
index 93d6126d99..5c18322c5e 100644
--- a/src/components/PostControls/DiscoverDebug.tsx
+++ b/src/components/PostControls/DiscoverDebug.tsx
@@ -3,11 +3,11 @@ import * as Clipboard from 'expo-clipboard'
import {t} from '@lingui/macro'
import {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
-import {useGate} from '#/lib/statsig/statsig'
import {useSession} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
+import {useAnalytics} from '#/analytics'
import {IS_INTERNAL} from '#/env'
export function DiscoverDebug({
@@ -15,12 +15,12 @@ export function DiscoverDebug({
}: {
feedContext: string | undefined
}) {
+ const ax = useAnalytics()
const {currentAccount} = useSession()
- const gate = useGate()
const isDiscoverDebugUser =
IS_INTERNAL ||
DISCOVER_DEBUG_DIDS[currentAccount?.did || ''] ||
- gate('debug_show_feedcontext')
+ ax.feature(ax.Features.DebugFeedContext)
const theme = useTheme()
return (
diff --git a/src/components/PostControls/PostMenu/PostMenuItems.tsx b/src/components/PostControls/PostMenu/PostMenuItems.tsx
index 15fd3a3450..1413678d2e 100644
--- a/src/components/PostControls/PostMenu/PostMenuItems.tsx
+++ b/src/components/PostControls/PostMenu/PostMenuItems.tsx
@@ -26,7 +26,6 @@ import {
type CommonNavigatorParams,
type NavigationProp,
} from '#/lib/routes/types'
-import {useGate} from '#/lib/statsig/statsig'
import {richTextToString} from '#/lib/strings/rich-text-helpers'
import {toShareUrl} from '#/lib/strings/url-helpers'
import {logger} from '#/logger'
@@ -463,11 +462,10 @@ let PostMenuItems = ({
const onSignIn = () => requireSignIn(() => {})
- const gate = useGate()
const isDiscoverDebugUser =
IS_INTERNAL ||
DISCOVER_DEBUG_DIDS[currentAccount?.did || ''] ||
- gate('debug_show_feedcontext')
+ ax.feature(ax.Features.DebugFeedContext)
return (
<>
diff --git a/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx b/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx
index bf9f72a5b8..e8488bf6e1 100644
--- a/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx
+++ b/src/components/dialogs/nuxs/LiveNowBetaDialog.tsx
@@ -15,6 +15,7 @@ import {
} from '#/components/dialogs/nuxs/utils'
import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker'
import {Text} from '#/components/Typography'
+import {Features} from '#/analytics'
import {IS_E2E, IS_WEB} from '#/env'
export const enabled = createIsEnabledCheck(props => {
@@ -24,7 +25,7 @@ export const enabled = createIsEnabledCheck(props => {
'2026-01-16T00:00:00.000Z',
props.currentProfile.createdAt,
) &&
- !props.gate('disable_live_now_beta')
+ !props.feature(Features.DisableLiveNowBeta)
)
})
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
index eadadeb8eb..b07164ce75 100644
--- a/src/components/dialogs/nuxs/index.tsx
+++ b/src/components/dialogs/nuxs/index.tsx
@@ -8,7 +8,6 @@ import {
} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
-import {useGate} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {STALE} from '#/state/queries'
import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs'
@@ -25,6 +24,7 @@ import {
} from '#/components/dialogs/nuxs/LiveNowBetaDialog'
import {isSnoozed, snooze, unsnooze} from '#/components/dialogs/nuxs/snoozing'
import {type EnabledCheckProps} from '#/components/dialogs/nuxs/utils'
+import {useAnalytics} from '#/analytics'
import {useGeolocation} from '#/geolocation'
type Context = {
@@ -88,7 +88,7 @@ function Inner({
currentProfile: AppBskyActorDefs.ProfileViewDetailed
preferences: UsePreferencesQueryResponse
}) {
- const gate = useGate()
+ const ax = useAnalytics()
const geolocation = useGeolocation()
const {nuxs} = useNuxs()
const [snoozed, setSnoozed] = useState(() => {
@@ -133,7 +133,7 @@ function Inner({
if (
enabled &&
!enabled({
- gate,
+ feature: ax.feature,
currentAccount,
currentProfile,
preferences,
@@ -165,11 +165,11 @@ function Inner({
break
}
}, [
+ ax.feature,
nuxs,
snoozed,
snoozeNuxDialog,
saveNux,
- gate,
currentAccount,
currentProfile,
preferences,
diff --git a/src/components/dialogs/nuxs/utils.ts b/src/components/dialogs/nuxs/utils.ts
index 68ea38ebf2..b5d2463001 100644
--- a/src/components/dialogs/nuxs/utils.ts
+++ b/src/components/dialogs/nuxs/utils.ts
@@ -1,12 +1,12 @@
import {type AppBskyActorDefs} from '@atproto/api'
-import {type useGate} from '#/lib/statsig/statsig'
import {type UsePreferencesQueryResponse} from '#/state/queries/preferences'
import {type SessionAccount} from '#/state/session'
+import {type AnalyticsContextType} from '#/analytics'
import {type Geolocation} from '#/geolocation'
export type EnabledCheckProps = {
- gate: ReturnType
+ feature: AnalyticsContextType['feature']
currentAccount: SessionAccount
currentProfile: AppBskyActorDefs.ProfileViewDetailed
preferences: UsePreferencesQueryResponse
diff --git a/src/lib/hooks/useIsBskyTeam.ts b/src/lib/hooks/useIsBskyTeam.ts
index d653e7fd5e..15136597aa 100644
--- a/src/lib/hooks/useIsBskyTeam.ts
+++ b/src/lib/hooks/useIsBskyTeam.ts
@@ -1,8 +1,8 @@
import {useMemo} from 'react'
-import {useGate} from '#/lib/statsig/statsig'
+import {useAnalytics} from '#/analytics'
export function useIsBskyTeam() {
- const gate = useGate()
- return useMemo(() => gate('is_bsky_team_member'), [gate])
+ const ax = useAnalytics()
+ return useMemo(() => ax.feature(ax.Features.IsBskyTeam), [ax])
}
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
deleted file mode 100644
index b0fe1b9377..0000000000
--- a/src/lib/statsig/gates.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type Gate =
- // Keep this alphabetic please.
- | 'debug_show_feedcontext'
- | 'is_bsky_team_member' // special, do not remove
- | 'disable_onboarding_find_contacts'
- | 'disable_settings_find_contacts'
- | 'disable_live_now_beta'
diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
deleted file mode 100644
index cf27803b24..0000000000
--- a/src/lib/statsig/statsig.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type React from 'react'
-
-import {type Gate} from './gates'
-
-const fn = () => false
-export function useGate(): (_gateName: Gate, _options?: any) => boolean {
- return fn
-}
-
-export function Provider({children}: {children: React.ReactNode}) {
- return children
-}
diff --git a/src/screens/Onboarding/StepProfile/index.tsx b/src/screens/Onboarding/StepProfile/index.tsx
index 025cf12996..c1a72b7d60 100644
--- a/src/screens/Onboarding/StepProfile/index.tsx
+++ b/src/screens/Onboarding/StepProfile/index.tsx
@@ -14,7 +14,6 @@ import {compressIfNeeded} from '#/lib/media/manip'
import {openCropper} from '#/lib/media/picker'
import {getDataUriSize} from '#/lib/media/util'
import {useRequestNotificationsPermission} from '#/lib/notifications/notifications'
-import {useGate} from '#/lib/statsig/statsig'
import {isCancelledError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {
@@ -72,7 +71,6 @@ export function StepProfile() {
const t = useTheme()
const {gtMobile} = useBreakpoints()
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
- const gate = useGate()
const requestNotificationsPermission = useRequestNotificationsPermission()
const creatorControl = Dialog.useDialogControl()
@@ -91,7 +89,7 @@ export function StepProfile() {
React.useEffect(() => {
requestNotificationsPermission('StartOnboarding')
- }, [gate, requestNotificationsPermission])
+ }, [requestNotificationsPermission])
const sheetWrapper = useSheetWrapper()
const openPicker = React.useCallback(
diff --git a/src/screens/Onboarding/index.tsx b/src/screens/Onboarding/index.tsx
index c7538d471c..befebee64c 100644
--- a/src/screens/Onboarding/index.tsx
+++ b/src/screens/Onboarding/index.tsx
@@ -3,7 +3,6 @@ import {View} from 'react-native'
import * as bcp47Match from 'bcp-47-match'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
-import {useGate} from '#/lib/statsig/statsig'
import {useLanguagePrefs} from '#/state/preferences'
import {
Layout,
@@ -23,6 +22,7 @@ import {useIsFindContactsFeatureEnabledBasedOnGeolocation} from '#/components/co
import {useFindContactsFlowState} from '#/components/contacts/state'
import {Portal} from '#/components/Portal'
import {ScreenTransition} from '#/components/ScreenTransition'
+import {useAnalytics} from '#/analytics'
import {ENV, IS_NATIVE} from '#/env'
import {StepFindContacts} from './StepFindContacts'
import {StepFindContactsIntro} from './StepFindContactsIntro'
@@ -31,7 +31,7 @@ import {StepSuggestedStarterpacks} from './StepSuggestedStarterpacks'
export function Onboarding() {
const t = useTheme()
- const gate = useGate()
+ const ax = useAnalytics()
const {contentLanguages} = useLanguagePrefs()
const probablySpeaksEnglish = useMemo(() => {
@@ -48,7 +48,7 @@ export function Onboarding() {
ENV !== 'e2e' &&
IS_NATIVE &&
findContactsEnabled &&
- !gate('disable_onboarding_find_contacts')
+ !ax.feature(ax.Features.DisableOnboardingFindContacts)
const [state, dispatch] = useReducer(
reducer,
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 0b7d8aee32..e7d89a6817 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -15,7 +15,6 @@ import {
type CommonNavigatorParams,
type NavigationProp,
} from '#/lib/routes/types'
-import {useGate} from '#/lib/statsig/statsig'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {useProfileShadow} from '#/state/cache/profile-shadow'
@@ -69,12 +68,14 @@ import {
shouldShowVerificationCheckButton,
VerificationCheckButton,
} from '#/components/verification/VerificationCheckButton'
+import {useAnalytics} from '#/analytics'
import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
import {device, useStorage} from '#/storage'
import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
type Props = NativeStackScreenProps
export function SettingsScreen({}: Props) {
+ const ax = useAnalytics()
const {_} = useLingui()
const reducedMotion = useReducedMotion()
const {logoutEveryAccount} = useSessionApi()
@@ -92,7 +93,6 @@ export function SettingsScreen({}: Props) {
const [showDevOptions, setShowDevOptions] = useState(false)
const findContactsEnabled =
useIsFindContactsFeatureEnabledBasedOnGeolocation()
- const gate = useGate()
return (
@@ -213,7 +213,7 @@ export function SettingsScreen({}: Props) {
{IS_NATIVE &&
findContactsEnabled &&
- !gate('disable_settings_find_contacts') && (
+ !ax.feature(ax.Features.DisableSettingsFindContacts) && (
diff --git a/src/state/service-config.tsx b/src/state/service-config.tsx
index cea9ca114a..780cf3bba3 100644
--- a/src/state/service-config.tsx
+++ b/src/state/service-config.tsx
@@ -1,9 +1,9 @@
import {createContext, useContext, useMemo} from 'react'
-import {useGate} from '#/lib/statsig/statsig'
import {useLanguagePrefs} from '#/state/preferences/languages'
import {useServiceConfigQuery} from '#/state/queries/service-config'
import {useSession} from '#/state/session'
+import {useAnalytics} from '#/analytics'
import {IS_DEV} from '#/env'
import {device} from '#/storage'
@@ -107,10 +107,10 @@ export function useLiveNowConfig(): LiveNowConfig {
}
export function useCanGoLive() {
- const gate = useGate()
+ const ax = useAnalytics()
const {hasSession} = useSession()
if (!hasSession) return false
- return IS_DEV ? true : !gate('disable_live_now_beta')
+ return IS_DEV ? true : !ax.feature(ax.Features.DisableLiveNowBeta)
}
export function useCheckEmailConfirmed() {