diff --git a/jest/jestSetup.js b/jest/jestSetup.js index c3160df3bc..b58ccb974f 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -110,14 +110,4 @@ jest.mock('expo-localization', () => ({ getLocales: () => [], })) -jest.mock('statsig-react-native-expo', () => ({ - Statsig: { - initialize() {}, - initializeCalled() { - return false - }, - }, -})) - jest.mock('../src/lib/bitdrift', () => ({})) -jest.mock('../src/lib/statsig/statsig', () => ({})) diff --git a/package.json b/package.json index 2c64fe78ba..cf9e54b80e 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,8 @@ "@react-navigation/native": "^6.1.17", "@react-navigation/native-stack": "^6.9.26", "@sentry/react-native": "5.24.3", + "@statsig/expo-bindings": "^3.11.1", + "@statsig/js-client": "^3.11.1", "@tanstack/query-async-storage-persister": "^5.25.0", "@tanstack/react-query": "^5.8.1", "@tanstack/react-query-persist-client": "^5.25.0", @@ -201,7 +203,6 @@ "react-responsive": "^9.0.2", "react-textarea-autosize": "^8.5.3", "rn-fetch-blob": "^0.12.0", - "statsig-react-native-expo": "^4.6.1", "tippy.js": "^6.3.7", "tlds": "^1.234.0", "tldts": "^6.1.46", diff --git a/src/lib/bitdrift.ts b/src/lib/bitdrift.ts index 71493d0bcf..d6dfd5cf3f 100644 --- a/src/lib/bitdrift.ts +++ b/src/lib/bitdrift.ts @@ -1,15 +1,14 @@ import {init, SessionStrategy} from '@bitdrift/react-native' -import {Statsig} from 'statsig-react-native-expo' export {debug, error, info, warn} from '@bitdrift/react-native' -import {initPromise} from './statsig/statsig' +import {StatsigClient} from './statsig/statsig' const BITDRIFT_API_KEY = process.env.BITDRIFT_API_KEY initPromise.then(() => { let isEnabled = false try { - if (Statsig.checkGate('enable_bitdrift')) { + if (StatsigClient.checkGate('enable_bitdrift')) { isEnabled = true } } catch (e) { diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx index fb85e43560..89b69a2bc6 100644 --- a/src/lib/statsig/statsig.tsx +++ b/src/lib/statsig/statsig.tsx @@ -1,7 +1,7 @@ import React from 'react' import {Platform} from 'react-native' import {AppState, AppStateStatus} from 'react-native' -import {Statsig, StatsigProvider} from 'statsig-react-native-expo' +import {StatsigClientExpo, StatsigProviderExpo} from '@statsig/expo-bindings' import {BUNDLE_DATE, BUNDLE_IDENTIFIER, IS_TESTFLIGHT} from '#/lib/app-info' import * as bitdrift from '#/lib/bitdrift' @@ -18,6 +18,12 @@ const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV' export const initPromise = initialize() +export const StatsigClient = new StatsigClientExpo( + SDK_KEY, + {}, + createStatsigOptions([]), +) + type StatsigUser = { userID: string | undefined // TODO: Remove when enough users have custom.platform: @@ -99,8 +105,8 @@ export function logEvent( try { const fullMetadata = toStringRecord(rawMetadata) fullMetadata.routeName = getCurrentRouteName() ?? '(Uninitialized)' - if (Statsig.initializeCalled()) { - Statsig.logEvent(eventName, null, fullMetadata) + if (isStatsigInitialized()) { + StatsigClient.logEvent(eventName, undefined, fullMetadata) } // Intentionally bypass the logger abstraction to log rich objects. console.groupCollapsed(eventName) @@ -150,12 +156,10 @@ export function useGate(): (gateName: Gate, options?: GateOptions) => boolean { return cachedValue } let value = false - if (Statsig.initializeCalled()) { - if (options.dangerouslyDisableExposureLogging) { - value = Statsig.checkGateWithExposureLoggingDisabled(gateName) - } else { - value = Statsig.checkGate(gateName) - } + if (isStatsigInitialized()) { + value = StatsigClient.checkGate(gateName, { + disableExposureLog: !!options?.dangerouslyDisableExposureLogging, + }) } cache.set(gateName, value) return value @@ -236,10 +240,11 @@ export async function tryFetchGates( // Use this for less common operations where the user would be OK with a delay. timeoutMs = 1500 } - if (Statsig.initializeCalled()) { + + if (isStatsigInitialized()) { await Promise.race([ timeout(timeoutMs), - Statsig.prefetchUsers([toStatsigUser(did)]), + StatsigClient.dataAdapter.prefetchData(toStatsigUser(did)), ]) } } catch (e) { @@ -249,7 +254,15 @@ export async function tryFetchGates( } export function initialize() { - return Statsig.initialize(SDK_KEY, null, createStatsigOptions([])) + StatsigClient.initializeAsync() +} + +export function isStatsigInitialized() { + return StatsigClient.loadingStatus === 'Ready' +} + +export function getStableID() { + return StatsigClient.getContext().stableID } export function Provider({children}: {children: React.ReactNode}) { @@ -283,9 +296,13 @@ export function Provider({children}: {children: React.ReactNode}) { // These changes are prefetched and stored, but don't get applied until the active DID changes. // This ensures that when you switch an account, it already has fresh results by then. const handleIntervalTick = useNonReactiveCallback(() => { - if (Statsig.initializeCalled()) { + if (isStatsigInitialized()) { // Note: Only first five will be taken into account by Statsig. - Statsig.prefetchUsers([currentStatsigUser, ...otherStatsigUsers]) + // + StatsigClient.dataAdapter.prefetchData(currentStatsigUser) + for (const user of otherStatsigUsers) { + StatsigClient.dataAdapter.prefetchData(user) + } } }) React.useEffect(() => { @@ -295,17 +312,13 @@ export function Provider({children}: {children: React.ReactNode}) { return ( - {children} - + ) } diff --git a/src/state/session/logging.ts b/src/state/session/logging.ts index 98de5a3964..d2ffc4e86f 100644 --- a/src/state/session/logging.ts +++ b/src/state/session/logging.ts @@ -1,8 +1,12 @@ import {AtpSessionData, AtpSessionEvent} from '@atproto/api' import {sha256} from 'js-sha256' -import {Statsig} from 'statsig-react-native-expo' import {IS_INTERNAL} from '#/lib/app-info' +import { + getStableID, + isStatsigInitialized, + StatsigClient, +} from '#/lib/statsig/statsig' import {Schema} from '../persisted' import {Action, State} from './reducer' import {SessionAccount} from './types' @@ -74,11 +78,12 @@ const MAX_SLICE_LENGTH = 1000 // Not gated. export function addSessionErrorLog(did: string, event: AtpSessionEvent) { try { - if (!Statsig.initializeCalled() || !Statsig.getStableID()) { + const stableID = getStableID() + if (!isStatsigInitialized() || !stableID) { return } const stack = (new Error().stack ?? '').slice(0, MAX_SLICE_LENGTH) - Statsig.logEvent('session:error', null, { + StatsigClient.logEvent('session:error', undefined, { did, event, stack, @@ -90,7 +95,8 @@ export function addSessionErrorLog(did: string, event: AtpSessionEvent) { export function addSessionDebugLog(log: Log) { try { - if (!Statsig.initializeCalled() || !Statsig.getStableID()) { + const stableID = getStableID() + if (!isStatsigInitialized() || !stableID) { // Drop these logs for now. return } @@ -110,7 +116,7 @@ export function addSessionDebugLog(log: Log) { const sliceIndex = nextSliceIndex++ const slice = payload.slice(0, MAX_SLICE_LENGTH) payload = payload.slice(MAX_SLICE_LENGTH) - Statsig.logEvent('session:debug', null, { + StatsigClient.logEvent('session:debug', undefined, { realmId, messageIndex: String(messageIndex), messageType: type, diff --git a/yarn.lock b/yarn.lock index 942656d218..482aa85678 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5650,13 +5650,6 @@ dependencies: merge-options "^3.0.4" -"@react-native-async-storage/async-storage@^1.15.2": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.22.0.tgz#202a9afd15a5b829c39b709d0ca3942612441efc" - integrity sha512-b5KD010iiZnot86RbAaHpLuHwmPW2qA3SSN/OSZhd1kBoINEQEVBuv+uFtcaTxAhX27bT0wd13GOb2IOSDUXSA== - dependencies: - merge-options "^3.0.4" - "@react-native-menu/menu@^1.1.7": version "1.1.7" resolved "https://registry.yarnpkg.com/@react-native-menu/menu/-/menu-1.1.7.tgz#47c345038951712fd61f2bbb7d591c181c7e9742" @@ -6590,6 +6583,43 @@ "@smithy/types" "^2.6.0" tslib "^2.5.0" +"@statsig/client-core@3.11.1": + version "3.11.1" + resolved "https://registry.yarnpkg.com/@statsig/client-core/-/client-core-3.11.1.tgz#c0a7a254021b3e9b647a04a2f1c9ce71e34fe65f" + integrity sha512-Ds2jw7nCmAtxEsTIp8x9SbjUYcJjF7nq9XGSzN9mLk3yJPZCz6Mknwq+9CvFjfTkK5zBWO46P0z9z4SDg0tNKQ== + +"@statsig/expo-bindings@^3.11.1": + version "3.11.1" + resolved "https://registry.yarnpkg.com/@statsig/expo-bindings/-/expo-bindings-3.11.1.tgz#9307caed4415cda2950fdfd0678e0ca5c79d593f" + integrity sha512-VX+pUJLLH1OtgePKU7FLx1AtqoSB0yP3qyIKKmdcNXfAk2ZmCRvsrjau/fnHwNwyhjUpHuj/11+DIag4xv3TIA== + dependencies: + "@statsig/client-core" "3.11.1" + "@statsig/js-client" "3.11.1" + "@statsig/react-bindings" "3.11.1" + "@statsig/react-native-core" "3.11.1" + +"@statsig/js-client@3.11.1", "@statsig/js-client@^3.11.1": + version "3.11.1" + resolved "https://registry.yarnpkg.com/@statsig/js-client/-/js-client-3.11.1.tgz#6877738b803d01573593ad0704261c30731339e7" + integrity sha512-eblEQfpPfqDbkF1a5l1rLGb+vCjhpcAwzvDUClNpLSs1KF23DLbvs1eJZpolHwZ2CiAFHgtZL7uhUJlzKi4nBA== + dependencies: + "@statsig/client-core" "3.11.1" + +"@statsig/react-bindings@3.11.1": + version "3.11.1" + resolved "https://registry.yarnpkg.com/@statsig/react-bindings/-/react-bindings-3.11.1.tgz#56461365b6c021a39f986dfc992c41151c4893c8" + integrity sha512-9/Ay87RGO1b5A+UCV2Z3saRwZi97rrkXQLNRkozAi7IVKGDTfuZXBOQsQvnNRlH3H8Q1LXo+nEHS9g9YtbfXgQ== + dependencies: + "@statsig/client-core" "3.11.1" + "@statsig/js-client" "3.11.1" + +"@statsig/react-native-core@3.11.1": + version "3.11.1" + resolved "https://registry.yarnpkg.com/@statsig/react-native-core/-/react-native-core-3.11.1.tgz#c6ca69e9355ab5291ab8c26e4cb8532d7f3132fb" + integrity sha512-fdJc5Da4dnln6ldId8jxKzvfbc5mmRBXReAERATZuVKCvrlsu2S5U49+V54E7sJelfBLjJSEaLXAYaiOSDPBFw== + dependencies: + "@statsig/client-core" "3.11.1" + "@tanstack/query-async-storage-persister@^5.25.0": version "5.25.0" resolved "https://registry.yarnpkg.com/@tanstack/query-async-storage-persister/-/query-async-storage-persister-5.25.0.tgz#0e8a2a781b8e32a81a5d02a688d6fcdfd055235b" @@ -10532,7 +10562,7 @@ expo-clipboard@^7.0.0: resolved "https://registry.yarnpkg.com/expo-clipboard/-/expo-clipboard-7.0.0.tgz#066b1a781fdaf05e30f282522d3a58f2e651e4cf" integrity sha512-4Vuv1zZPTOiKzIeC0BIGUN8nyzkXlE6jKchtLxcoksBjHPdG5W2eH05B+hppTrK9N3+Xh02z4j3h1cFRqPJ1fw== -expo-constants@17.0.3, expo-constants@^13.0.2, expo-constants@~17.0.0, expo-constants@~17.0.3: +expo-constants@17.0.3, expo-constants@~17.0.0, expo-constants@~17.0.3: version "17.0.3" resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-17.0.3.tgz#a05b38e0417d59759ece1642b4d483889e04dbda" integrity sha512-lnbcX2sAu8SucHXEXxSkhiEpqH+jGrf+TF+MO6sHWIESjwOUVVYlT8qYdjR9xbxWmqFtrI4KV44FkeJf2DaFjQ== @@ -10573,7 +10603,7 @@ expo-dev-menu@6.0.11: dependencies: expo-dev-menu-interface "1.9.2" -expo-device@7.0.1, expo-device@~4.1.1, expo-device@~7.0.1: +expo-device@7.0.1, expo-device@~7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-7.0.1.tgz#3702fe8b4475eac63ed27f9d580ec8a78546e0d1" integrity sha512-/3lk0f9wvle+6svHqWSCBC1B5NYFmXp1D7hmIyecJJVYRLwzrwwTDyNs76oG/UDU5Appdu8QyDKycsx2hqv71w== @@ -16114,13 +16144,6 @@ react-native-gesture-handler@2.20.2: invariant "^2.2.4" prop-types "^15.7.2" -react-native-get-random-values@^1.6.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.10.0.tgz#c2c5f12a4ef8b1175145347b4a4b9f9a40d9ffc8" - integrity sha512-gZ1zbXhbb8+Jy9qYTV8c4Nf45/VB4g1jmXuavY5rPfUn7x3ok9Vl3FTl0dnE92Z4FFtfbUNNwtSfcmomdtWg+A== - dependencies: - fast-base64-decode "^1.0.0" - react-native-get-random-values@~1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/react-native-get-random-values/-/react-native-get-random-values-1.11.0.tgz#1ca70d1271f4b08af92958803b89dccbda78728d" @@ -17560,14 +17583,6 @@ standard-as-callback@^2.1.0: resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== -statsig-js@4.45.1: - version "4.45.1" - resolved "https://registry.yarnpkg.com/statsig-js/-/statsig-js-4.45.1.tgz#b1f5b9c52adc4a8aece376fb011416c89227932f" - integrity sha512-h94RzFQsJCQCNwQXpZ9OBXcvCxDnkXF6OrCekd81ySvY2l4JSowpxMWX3Iw6IDFzfTfKdER9JQzFLhMSQbT+YQ== - dependencies: - js-sha256 "^0.10.1" - uuid "^8.3.2" - statsig-node@^5.23.1: version "5.25.1" resolved "https://registry.yarnpkg.com/statsig-node/-/statsig-node-5.25.1.tgz#6d8ea9ecaad6c09250e5ff7d33eda9fd0f9c05f4" @@ -17578,26 +17593,6 @@ statsig-node@^5.23.1: ua-parser-js "^1.0.2" uuid "^8.3.2" -statsig-react-native-expo@^4.6.1: - version "4.6.1" - resolved "https://registry.yarnpkg.com/statsig-react-native-expo/-/statsig-react-native-expo-4.6.1.tgz#0bdf49fee7112f7f28bff2405f4ba0c1727bb3d6" - integrity sha512-rB60c+WSrQPmjW9j75d+acUtwSOe38PE2KTDHiOv1Mf+0TCcFtGYlJmKCibWvbeXR7ZAyjjGeroh23bCSEZauQ== - dependencies: - "@react-native-async-storage/async-storage" "^1.15.2" - expo-constants "^13.0.2" - expo-device "~4.1.1" - js-sha256 "^0.9.0" - react-native-get-random-values "^1.6.0" - statsig-react "^1.21.1" - uuid "^8.3.2" - -statsig-react@^1.21.1: - version "1.35.0" - resolved "https://registry.yarnpkg.com/statsig-react/-/statsig-react-1.35.0.tgz#ad5730b83f564c640623e954fcbcbe848e939946" - integrity sha512-KLN7dhq6FvAl25Z0QN6IINFBgM3yn0GMafoE698tYZqRf911xvevFaR7qUXiTz3W9vmFYrmFRouqVMfCv7DW0A== - dependencies: - statsig-js "4.45.1" - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"