Bluesky app fork with some witchin' additions 💫

Gate bitdrift integration (#7088)

* Move Statsig init call earlier

* Gate Bitdrift init call

* Remove IS_TEST env constant

* Mock statsig

authored by danabra.mov and committed by GitHub e2a7965e 5655241b

Changed files
+38 -24
.github
workflows
src
lib
logger
state
session
__tests__
view
com
util
+1 -1
.github/workflows/lint.yml
··· 54 run: yarn intl:build 55 - name: Run tests 56 run: | 57 - NODE_ENV=test EXPO_PUBLIC_ENV=test yarn test --forceExit
··· 54 run: yarn intl:build 55 - name: Run tests 56 run: | 57 + NODE_ENV=test yarn test --forceExit
+1 -1
Makefile
··· 20 21 .PHONY: test 22 test: ## Run all tests 23 - NODE_ENV=test EXPO_PUBLIC_ENV=test yarn test 24 25 .PHONY: lint 26 lint: ## Run style checks and verify syntax
··· 20 21 .PHONY: test 22 test: ## Run all tests 23 + NODE_ENV=test yarn test 24 25 .PHONY: lint 26 lint: ## Run style checks and verify syntax
+1 -2
index.js
··· 5 import {registerRootComponent} from 'expo' 6 7 import App from '#/App' 8 - import {IS_TEST} from '#/env' 9 10 - if (IS_TEST) { 11 LogBox.ignoreAllLogs() // suppress all logs in tests 12 } else { 13 LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine
··· 5 import {registerRootComponent} from 'expo' 6 7 import App from '#/App' 8 9 + if (process.env.NODE_ENV === 'test') { 10 LogBox.ignoreAllLogs() // suppress all logs in tests 11 } else { 12 LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine
+1 -6
src/App.native.tsx
··· 17 18 import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController' 19 import {QueryProvider} from '#/lib/react-query' 20 - import { 21 - initialize, 22 - Provider as StatsigProvider, 23 - tryFetchGates, 24 - } from '#/lib/statsig/statsig' 25 import {s} from '#/lib/styles' 26 import {ThemeProvider} from '#/lib/ThemeContext' 27 import I18nProvider from '#/locale/i18nProvider' ··· 101 if (account) { 102 await resumeSession(account) 103 } else { 104 - await initialize() 105 await tryFetchGates(undefined, 'prefer-fresh-gates') 106 } 107 } catch (e) {
··· 17 18 import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController' 19 import {QueryProvider} from '#/lib/react-query' 20 + import {Provider as StatsigProvider, tryFetchGates} from '#/lib/statsig/statsig' 21 import {s} from '#/lib/styles' 22 import {ThemeProvider} from '#/lib/ThemeContext' 23 import I18nProvider from '#/locale/i18nProvider' ··· 97 if (account) { 98 await resumeSession(account) 99 } else { 100 await tryFetchGates(undefined, 'prefer-fresh-gates') 101 } 102 } catch (e) {
-1
src/env.ts
··· 1 - export const IS_TEST = process.env.EXPO_PUBLIC_ENV === 'test' 2 export const IS_DEV = __DEV__ 3 export const IS_PROD = !IS_DEV 4 export const LOG_DEBUG = process.env.EXPO_PUBLIC_LOG_DEBUG || ''
··· 1 export const IS_DEV = __DEV__ 2 export const IS_PROD = !IS_DEV 3 export const LOG_DEBUG = process.env.EXPO_PUBLIC_LOG_DEBUG || ''
+16 -3
src/lib/bitdrift.ts
··· 1 import {init} from '@bitdrift/react-native' 2 3 const BITDRIFT_API_KEY = process.env.BITDRIFT_API_KEY 4 5 - if (BITDRIFT_API_KEY) { 6 - init(BITDRIFT_API_KEY, {url: 'https://api-bsky.bitdrift.io'}) 7 - }
··· 1 import {init} from '@bitdrift/react-native' 2 + import {Statsig} from 'statsig-react-native-expo' 3 + 4 + import {initPromise} from './statsig/statsig' 5 6 const BITDRIFT_API_KEY = process.env.BITDRIFT_API_KEY 7 8 + initPromise.then(() => { 9 + let isEnabled = false 10 + try { 11 + if (Statsig.checkGate('enable_bitdrift')) { 12 + isEnabled = true 13 + } 14 + } catch (e) { 15 + // Statsig may complain about it being called too early. 16 + } 17 + if (isEnabled && BITDRIFT_API_KEY) { 18 + init(BITDRIFT_API_KEY, {url: 'https://api-bsky.bitdrift.io'}) 19 + } 20 + })
+2 -3
src/lib/statsig/statsig.tsx
··· 16 17 const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV' 18 19 type StatsigUser = { 20 userID: string | undefined 21 // TODO: Remove when enough users have custom.platform: ··· 219 // Use this for less common operations where the user would be OK with a delay. 220 timeoutMs = 1500 221 } 222 - // Note: This condition is currently false the very first render because 223 - // Statsig has not initialized yet. In the future, we can fix this by 224 - // doing the initialization ourselves instead of relying on the provider. 225 if (Statsig.initializeCalled()) { 226 await Promise.race([ 227 timeout(timeoutMs),
··· 16 17 const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV' 18 19 + export const initPromise = initialize() 20 + 21 type StatsigUser = { 22 userID: string | undefined 23 // TODO: Remove when enough users have custom.platform: ··· 221 // Use this for less common operations where the user would be OK with a delay. 222 timeoutMs = 1500 223 } 224 if (Statsig.initializeCalled()) { 225 await Promise.race([ 226 timeout(timeoutMs),
+1 -1
src/logger/README.md
··· 18 #### Modes 19 20 The "modes" referred to here are inferred from the values exported from `#/env`. 21 - Basically, the booleans `IS_DEV`, `IS_TEST`, and `IS_PROD`. 22 23 #### Log Levels 24
··· 18 #### Modes 19 20 The "modes" referred to here are inferred from the values exported from `#/env`. 21 + Basically, the booleans `IS_DEV` and `IS_PROD`. 22 23 #### Log Levels 24
-1
src/logger/__tests__/logger.test.ts
··· 5 import {Logger, LogLevel, sentryTransport} from '#/logger' 6 7 jest.mock('#/env', () => ({ 8 - IS_TEST: true, 9 IS_DEV: false, 10 IS_PROD: false, 11 /*
··· 5 import {Logger, LogLevel, sentryTransport} from '#/logger' 6 7 jest.mock('#/env', () => ({ 8 IS_DEV: false, 9 IS_PROD: false, 10 /*
+3 -3
src/logger/index.ts
··· 173 protected debugContextRegexes: RegExp[] = [] 174 175 constructor({ 176 - enabled = !env.IS_TEST, 177 level = env.LOG_LEVEL as LogLevel, 178 debug = env.LOG_DEBUG || '', 179 }: { ··· 266 */ 267 export const logger = new Logger() 268 269 - if (!env.IS_TEST) { 270 logger.addTransport(createBitdriftTransport()) 271 } 272 273 - if (env.IS_DEV && !env.IS_TEST) { 274 logger.addTransport(consoleTransport) 275 276 /*
··· 173 protected debugContextRegexes: RegExp[] = [] 174 175 constructor({ 176 + enabled = process.env.NODE_ENV !== 'test', 177 level = env.LOG_LEVEL as LogLevel, 178 debug = env.LOG_DEBUG || '', 179 }: { ··· 266 */ 267 export const logger = new Logger() 268 269 + if (process.env.NODE_ENV !== 'test') { 270 logger.addTransport(createBitdriftTransport()) 271 } 272 273 + if (env.IS_DEV && process.env.NODE_ENV !== 'test') { 274 logger.addTransport(consoleTransport) 275 276 /*
+9
src/state/session/__tests__/session-test.ts
··· 4 import {agentToSessionAccountOrThrow} from '../agent' 5 import {Action, getInitialState, reducer, State} from '../reducer' 6 7 jest.mock('jwt-decode', () => ({ 8 jwtDecode(_token: string) { 9 return {}
··· 4 import {agentToSessionAccountOrThrow} from '../agent' 5 import {Action, getInitialState, reducer, State} from '../reducer' 6 7 + jest.mock('statsig-react-native-expo', () => ({ 8 + Statsig: { 9 + initialize() {}, 10 + initializeCalled() { 11 + return false 12 + }, 13 + }, 14 + })) 15 + 16 jest.mock('jwt-decode', () => ({ 17 jwtDecode(_token: string) { 18 return {}
+3 -2
src/view/com/util/Toast.tsx
··· 25 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 26 import {atoms as a, useTheme} from '#/alf' 27 import {Text} from '#/components/Typography' 28 - import {IS_TEST} from '#/env' 29 30 const TIMEOUT = 2e3 31 ··· 33 message: string, 34 icon: FontAwesomeProps['icon'] = 'check', 35 ) { 36 - if (IS_TEST) return 37 AccessibilityInfo.announceForAccessibility(message) 38 const item = new RootSiblings( 39 <Toast message={message} icon={icon} destroy={() => item.destroy()} />,
··· 25 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 26 import {atoms as a, useTheme} from '#/alf' 27 import {Text} from '#/components/Typography' 28 29 const TIMEOUT = 2e3 30 ··· 32 message: string, 33 icon: FontAwesomeProps['icon'] = 'check', 34 ) { 35 + if (process.env.NODE_ENV === 'test') { 36 + return 37 + } 38 AccessibilityInfo.announceForAccessibility(message) 39 const item = new RootSiblings( 40 <Toast message={message} icon={icon} destroy={() => item.destroy()} />,