WIP
This commit is contained in:
+4
-2
@@ -21,7 +21,7 @@ import {Provider as StatsigProvider, tryFetchGates} from '#/lib/statsig/statsig'
|
||||
import {s} from '#/lib/styles'
|
||||
import {ThemeProvider} from '#/lib/ThemeContext'
|
||||
import I18nProvider from '#/locale/i18nProvider'
|
||||
import {logger} from '#/logger'
|
||||
import {logger, Provider as LoggingProvider} from '#/logger'
|
||||
import {Provider as A11yProvider} from '#/state/a11y'
|
||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||
@@ -238,7 +238,9 @@ function App() {
|
||||
<StarterPackProvider>
|
||||
<SafeAreaProvider
|
||||
initialMetrics={initialWindowMetrics}>
|
||||
<InnerApp />
|
||||
<LoggingProvider>
|
||||
<InnerApp />
|
||||
</LoggingProvider>
|
||||
</SafeAreaProvider>
|
||||
</StarterPackProvider>
|
||||
</BottomSheetProvider>
|
||||
|
||||
+6
-2
@@ -12,7 +12,8 @@ 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'
|
||||
import {logger, Provider as LoggingProvider} from '#/logger'
|
||||
import {initializer as growthbookInitializer} from '#/logger/growthbook/context'
|
||||
import {Provider as A11yProvider} from '#/state/a11y'
|
||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||
@@ -85,6 +86,7 @@ function InnerApp() {
|
||||
useEffect(() => {
|
||||
async function onLaunch(account?: SessionAccount) {
|
||||
try {
|
||||
await growthbookInitializer
|
||||
if (account) {
|
||||
await resumeSession(account)
|
||||
}
|
||||
@@ -205,7 +207,9 @@ function App() {
|
||||
<LightboxStateProvider>
|
||||
<PortalProvider>
|
||||
<StarterPackProvider>
|
||||
<InnerApp />
|
||||
<LoggingProvider>
|
||||
<InnerApp />
|
||||
</LoggingProvider>
|
||||
</StarterPackProvider>
|
||||
</PortalProvider>
|
||||
</LightboxStateProvider>
|
||||
|
||||
Vendored
+11
@@ -84,6 +84,17 @@ export const BLUESKY_PROXY_DID: Did =
|
||||
export const CHAT_PROXY_DID: Did =
|
||||
process.env.EXPO_PUBLIC_CHAT_PROXY_DID || 'did:web:api.bsky.chat'
|
||||
|
||||
/**
|
||||
* Growthbook API host
|
||||
*/
|
||||
export const GROWTHBOOK_API_HOST: string | undefined = process.env.EXPO_PUBLIC_GROWTHBOOK_API_HOST
|
||||
|
||||
/**
|
||||
* Growthbook client key
|
||||
*/
|
||||
export const GROWTHBOOK_CLIENT_KEY: string | undefined = process.env.EXPO_PUBLIC_GROWTHBOOK_CLIENT_KEY
|
||||
|
||||
|
||||
/**
|
||||
* Sentry DSN for telemetry
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import {useMemo, useEffect} from 'react'
|
||||
import {Platform} from 'react-native'
|
||||
import {
|
||||
GrowthBook,
|
||||
GrowthBookProvider,
|
||||
useFeatureIsOn,
|
||||
} from '@growthbook/growthbook-react'
|
||||
|
||||
import * as env from '#/env'
|
||||
import {useSession, SessionAccount} from '#/state/session'
|
||||
import {useGeolocation} from '#/geolocation'
|
||||
import * as persisted from '#/state/persisted'
|
||||
import * as referrer from '#/logger/growthbook/util/referrer'
|
||||
|
||||
type DefaultAttributes = {
|
||||
country: string
|
||||
}
|
||||
type UserAttributes = {
|
||||
id: string
|
||||
pds: string | undefined
|
||||
platform: string
|
||||
appVersion: string
|
||||
bundleIdentifier: string
|
||||
bundleDate: number
|
||||
refSrc: string
|
||||
refUrl: string
|
||||
appLanguage: string
|
||||
contentLanguages: string[]
|
||||
}
|
||||
|
||||
const gb = new GrowthBook({
|
||||
apiHost: env.GROWTHBOOK_API_HOST,
|
||||
clientKey: env.GROWTHBOOK_CLIENT_KEY,
|
||||
trackingCallback: (experiment, result) => {
|
||||
// TODO
|
||||
console.log('Experiment Viewed', {
|
||||
experimentId: experiment.key,
|
||||
variationId: result.key,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export const initializer = gb.init({
|
||||
timeout: 1e3,
|
||||
})
|
||||
|
||||
export function useGate(gate: string): boolean {
|
||||
return useFeatureIsOn(gate)
|
||||
}
|
||||
|
||||
export function Provider({children}: {children: React.ReactNode}) {
|
||||
const geo = useGeolocation()
|
||||
const {currentAccount} = useSession()
|
||||
const defaultAttributes = useMemo<DefaultAttributes>(
|
||||
() => ({
|
||||
country: geo.countryCode || 'unknown',
|
||||
}),
|
||||
[geo],
|
||||
)
|
||||
|
||||
/**
|
||||
* Decorate existing attributes with any new default attributes
|
||||
*/
|
||||
useEffect(() => {
|
||||
const attr = {
|
||||
...gb.getAttributes(),
|
||||
...defaultAttributes,
|
||||
}
|
||||
gb.setAttributes(attr)
|
||||
console.debug(`update attributes`, {attributes: attr})
|
||||
}, [defaultAttributes])
|
||||
|
||||
/**
|
||||
* Update user attributes on session change, and clear them on logout
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (currentAccount) {
|
||||
const attr = getUserAttributes(currentAccount)
|
||||
gb.setAttributes({
|
||||
...defaultAttributes,
|
||||
...getUserAttributes(currentAccount),
|
||||
})
|
||||
console.debug(`has session, set attributes`, {attributes: attr})
|
||||
} else {
|
||||
gb.setAttributes(defaultAttributes)
|
||||
console.debug(`no session, reset attributes`, {
|
||||
attributes: defaultAttributes,
|
||||
})
|
||||
}
|
||||
}, [defaultAttributes, currentAccount])
|
||||
|
||||
return <GrowthBookProvider growthbook={gb}>{children}</GrowthBookProvider>
|
||||
}
|
||||
|
||||
export function getUserAttributes(account: SessionAccount): UserAttributes
|
||||
export function getUserAttributes(account: undefined): null
|
||||
export function getUserAttributes(
|
||||
account?: SessionAccount,
|
||||
): UserAttributes | null {
|
||||
if (!account) return null
|
||||
const languagePrefs = persisted.get('languagePrefs')
|
||||
return {
|
||||
id: account.did,
|
||||
pds: account.pdsUrl,
|
||||
platform: Platform.OS,
|
||||
appVersion: env.RELEASE_VERSION,
|
||||
bundleIdentifier: env.BUNDLE_IDENTIFIER,
|
||||
bundleDate: env.BUNDLE_DATE,
|
||||
appLanguage: languagePrefs.appLanguage,
|
||||
contentLanguages: languagePrefs.contentLanguages,
|
||||
refSrc: referrer.src,
|
||||
refUrl: referrer.url,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const src = ''
|
||||
export const url = ''
|
||||
@@ -0,0 +1,3 @@
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
export const src = params.get('ref_src') ?? ''
|
||||
export const url = decodeURIComponent(params.get('ref_url') ?? '')
|
||||
@@ -1,5 +1,6 @@
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {Provider as GrowthbookProvider} from '#/logger/growthbook/context'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {add} from '#/logger/logDump'
|
||||
import {type MetricEvents} from '#/logger/metrics'
|
||||
@@ -171,3 +172,10 @@ export class Logger {
|
||||
* `logger.error(error[, metadata])`
|
||||
*/
|
||||
export const logger = Logger.create(Logger.Context.Default)
|
||||
|
||||
/**
|
||||
* Logger context provider
|
||||
*/
|
||||
export function Provider({children}: {children: React.ReactNode}) {
|
||||
return <GrowthbookProvider>{children}</GrowthbookProvider>
|
||||
}
|
||||
Reference in New Issue
Block a user