Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Statsig] Prefetch configs for other accounts (#3607)

* Poll both current and other accounts

* Make createStatsigOptions a function

* Pass prefetchUsers with the initial request

* Add initializeCalled check

* Be resilient to object identity changes

* Decrease poll interval to 1 minute

authored by

dan and committed by
GitHub
6101c32b 41b5b5b2

+46 -21
+46 -21
src/lib/statsig/statsig.tsx
··· 8 8 import {isWeb} from '#/platform/detection' 9 9 import {IS_TESTFLIGHT} from 'lib/app-info' 10 10 import {useSession} from '../../state/session' 11 + import {useNonReactiveCallback} from '../hooks/useNonReactiveCallback' 11 12 import {LogEvents} from './events' 12 13 import {Gate} from './gates' 13 14 ··· 34 35 35 36 export type {LogEvents} 36 37 37 - const statsigOptions = { 38 - environment: { 39 - tier: 40 - process.env.NODE_ENV === 'development' 41 - ? 'development' 42 - : IS_TESTFLIGHT 43 - ? 'staging' 44 - : 'production', 45 - }, 46 - // Don't block on waiting for network. The fetched config will kick in on next load. 47 - // This ensures the UI is always consistent and doesn't update mid-session. 48 - // Note this makes cold load (no local storage) and private mode return `false` for all gates. 49 - initTimeoutMs: 1, 38 + function createStatsigOptions(prefetchUsers: StatsigUser[]) { 39 + return { 40 + environment: { 41 + tier: 42 + process.env.NODE_ENV === 'development' 43 + ? 'development' 44 + : IS_TESTFLIGHT 45 + ? 'staging' 46 + : 'production', 47 + }, 48 + // Don't block on waiting for network. The fetched config will kick in on next load. 49 + // This ensures the UI is always consistent and doesn't update mid-session. 50 + // Note this makes cold load (no local storage) and private mode return `false` for all gates. 51 + initTimeoutMs: 1, 52 + // Get fresh flags for other accounts as well, if any. 53 + prefetchUsers, 54 + } 50 55 } 51 56 52 57 type FlatJSONRecord = Record< ··· 160 165 }) 161 166 162 167 export function Provider({children}: {children: React.ReactNode}) { 163 - const {currentAccount} = useSession() 168 + const {currentAccount, accounts} = useSession() 164 169 const did = currentAccount?.did 165 170 const currentStatsigUser = React.useMemo(() => toStatsigUser(did), [did]) 171 + 172 + const otherDidsConcatenated = accounts 173 + .map(account => account.did) 174 + .filter(accountDid => accountDid !== did) 175 + .join(' ') // We're only interested in DID changes. 176 + const otherStatsigUsers = React.useMemo( 177 + () => otherDidsConcatenated.split(' ').map(toStatsigUser), 178 + [otherDidsConcatenated], 179 + ) 180 + const statsigOptions = React.useMemo( 181 + () => createStatsigOptions(otherStatsigUsers), 182 + [otherStatsigUsers], 183 + ) 184 + 185 + // Have our own cache in front of Statsig. 186 + // This ensures the results remain stable until the active DID changes. 166 187 const [gateCache, setGateCache] = React.useState(() => new Map()) 167 188 const [prevDid, setPrevDid] = React.useState(did) 168 189 if (did !== prevDid) { ··· 170 191 setGateCache(new Map()) 171 192 } 172 193 173 - React.useEffect(() => { 174 - function refresh() { 175 - // This will not affect the current session. 176 - // Statsig will put the results into local storage and we'll pick it up on next load. 177 - Statsig.updateUser(currentStatsigUser) 194 + // Periodically poll Statsig to get the current rule evaluations for all stored accounts. 195 + // These changes are prefetched and stored, but don't get applied until the active DID changes. 196 + // This ensures that when you switch an account, it already has fresh results by then. 197 + const handleIntervalTick = useNonReactiveCallback(() => { 198 + if (Statsig.initializeCalled()) { 199 + // Note: Only first five will be taken into account by Statsig. 200 + Statsig.prefetchUsers([currentStatsigUser, ...otherStatsigUsers]) 178 201 } 179 - const id = setInterval(refresh, 3 * 60e3 /* 3 min */) 202 + }) 203 + React.useEffect(() => { 204 + const id = setInterval(handleIntervalTick, 60e3 /* 1 min */) 180 205 return () => clearInterval(id) 181 - }, [currentStatsigUser]) 206 + }, [handleIntervalTick]) 182 207 183 208 return ( 184 209 <GateCache.Provider value={gateCache}>