Add app config context
This commit is contained in:
@@ -46,3 +46,6 @@ GEOLOCATION_DEV_URL=
|
|||||||
|
|
||||||
# live-events web worker URL
|
# live-events web worker URL
|
||||||
LIVE_EVENTS_DEV_URL=
|
LIVE_EVENTS_DEV_URL=
|
||||||
|
|
||||||
|
# app-config web worker URL
|
||||||
|
APP_CONFIG_DEV_URL=
|
||||||
|
|||||||
+39
-32
@@ -22,6 +22,10 @@ import {ThemeProvider} from '#/lib/ThemeContext'
|
|||||||
import I18nProvider from '#/locale/i18nProvider'
|
import I18nProvider from '#/locale/i18nProvider'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {Provider as A11yProvider} from '#/state/a11y'
|
import {Provider as A11yProvider} from '#/state/a11y'
|
||||||
|
import {
|
||||||
|
prefetchAppConfig,
|
||||||
|
Provider as AppConfigProvider,
|
||||||
|
} from '#/state/appConfig'
|
||||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||||
import {Provider as EmailVerificationProvider} from '#/state/email-verification'
|
import {Provider as EmailVerificationProvider} from '#/state/email-verification'
|
||||||
@@ -103,6 +107,7 @@ if (IS_ANDROID) {
|
|||||||
Geo.resolve()
|
Geo.resolve()
|
||||||
prefetchAgeAssuranceConfig()
|
prefetchAgeAssuranceConfig()
|
||||||
prefetchLiveEvents()
|
prefetchLiveEvents()
|
||||||
|
prefetchAppConfig()
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const [isReady, setIsReady] = React.useState(false)
|
const [isReady, setIsReady] = React.useState(false)
|
||||||
@@ -228,38 +233,40 @@ function App() {
|
|||||||
*/
|
*/
|
||||||
return (
|
return (
|
||||||
<Geo.Provider>
|
<Geo.Provider>
|
||||||
<A11yProvider>
|
<AppConfigProvider>
|
||||||
<KeyboardControllerProvider>
|
<A11yProvider>
|
||||||
<OnboardingProvider>
|
<KeyboardControllerProvider>
|
||||||
<AnalyticsContext>
|
<OnboardingProvider>
|
||||||
<SessionProvider>
|
<AnalyticsContext>
|
||||||
<PrefsStateProvider>
|
<SessionProvider>
|
||||||
<I18nProvider>
|
<PrefsStateProvider>
|
||||||
<ShellStateProvider>
|
<I18nProvider>
|
||||||
<ModalStateProvider>
|
<ShellStateProvider>
|
||||||
<DialogStateProvider>
|
<ModalStateProvider>
|
||||||
<LightboxStateProvider>
|
<DialogStateProvider>
|
||||||
<PortalProvider>
|
<LightboxStateProvider>
|
||||||
<BottomSheetProvider>
|
<PortalProvider>
|
||||||
<StarterPackProvider>
|
<BottomSheetProvider>
|
||||||
<SafeAreaProvider
|
<StarterPackProvider>
|
||||||
initialMetrics={initialWindowMetrics}>
|
<SafeAreaProvider
|
||||||
<InnerApp />
|
initialMetrics={initialWindowMetrics}>
|
||||||
</SafeAreaProvider>
|
<InnerApp />
|
||||||
</StarterPackProvider>
|
</SafeAreaProvider>
|
||||||
</BottomSheetProvider>
|
</StarterPackProvider>
|
||||||
</PortalProvider>
|
</BottomSheetProvider>
|
||||||
</LightboxStateProvider>
|
</PortalProvider>
|
||||||
</DialogStateProvider>
|
</LightboxStateProvider>
|
||||||
</ModalStateProvider>
|
</DialogStateProvider>
|
||||||
</ShellStateProvider>
|
</ModalStateProvider>
|
||||||
</I18nProvider>
|
</ShellStateProvider>
|
||||||
</PrefsStateProvider>
|
</I18nProvider>
|
||||||
</SessionProvider>
|
</PrefsStateProvider>
|
||||||
</AnalyticsContext>
|
</SessionProvider>
|
||||||
</OnboardingProvider>
|
</AnalyticsContext>
|
||||||
</KeyboardControllerProvider>
|
</OnboardingProvider>
|
||||||
</A11yProvider>
|
</KeyboardControllerProvider>
|
||||||
|
</A11yProvider>
|
||||||
|
</AppConfigProvider>
|
||||||
</Geo.Provider>
|
</Geo.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-25
@@ -13,6 +13,10 @@ import {ThemeProvider} from '#/lib/ThemeContext'
|
|||||||
import I18nProvider from '#/locale/i18nProvider'
|
import I18nProvider from '#/locale/i18nProvider'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {Provider as A11yProvider} from '#/state/a11y'
|
import {Provider as A11yProvider} from '#/state/a11y'
|
||||||
|
import {
|
||||||
|
prefetchAppConfig,
|
||||||
|
Provider as AppConfigProvider,
|
||||||
|
} from '#/state/appConfig'
|
||||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||||
import {Provider as EmailVerificationProvider} from '#/state/email-verification'
|
import {Provider as EmailVerificationProvider} from '#/state/email-verification'
|
||||||
@@ -79,6 +83,7 @@ import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottom
|
|||||||
Geo.resolve()
|
Geo.resolve()
|
||||||
prefetchAgeAssuranceConfig()
|
prefetchAgeAssuranceConfig()
|
||||||
prefetchLiveEvents()
|
prefetchLiveEvents()
|
||||||
|
prefetchAppConfig()
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const [isReady, setIsReady] = useState(false)
|
const [isReady, setIsReady] = useState(false)
|
||||||
@@ -202,31 +207,33 @@ function App() {
|
|||||||
*/
|
*/
|
||||||
return (
|
return (
|
||||||
<Geo.Provider>
|
<Geo.Provider>
|
||||||
<A11yProvider>
|
<AppConfigProvider>
|
||||||
<OnboardingProvider>
|
<A11yProvider>
|
||||||
<AnalyticsContext>
|
<OnboardingProvider>
|
||||||
<SessionProvider>
|
<AnalyticsContext>
|
||||||
<PrefsStateProvider>
|
<SessionProvider>
|
||||||
<I18nProvider>
|
<PrefsStateProvider>
|
||||||
<ShellStateProvider>
|
<I18nProvider>
|
||||||
<ModalStateProvider>
|
<ShellStateProvider>
|
||||||
<DialogStateProvider>
|
<ModalStateProvider>
|
||||||
<LightboxStateProvider>
|
<DialogStateProvider>
|
||||||
<PortalProvider>
|
<LightboxStateProvider>
|
||||||
<StarterPackProvider>
|
<PortalProvider>
|
||||||
<InnerApp />
|
<StarterPackProvider>
|
||||||
</StarterPackProvider>
|
<InnerApp />
|
||||||
</PortalProvider>
|
</StarterPackProvider>
|
||||||
</LightboxStateProvider>
|
</PortalProvider>
|
||||||
</DialogStateProvider>
|
</LightboxStateProvider>
|
||||||
</ModalStateProvider>
|
</DialogStateProvider>
|
||||||
</ShellStateProvider>
|
</ModalStateProvider>
|
||||||
</I18nProvider>
|
</ShellStateProvider>
|
||||||
</PrefsStateProvider>
|
</I18nProvider>
|
||||||
</SessionProvider>
|
</PrefsStateProvider>
|
||||||
</AnalyticsContext>
|
</SessionProvider>
|
||||||
</OnboardingProvider>
|
</AnalyticsContext>
|
||||||
</A11yProvider>
|
</OnboardingProvider>
|
||||||
|
</A11yProvider>
|
||||||
|
</AppConfigProvider>
|
||||||
</Geo.Provider>
|
</Geo.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+10
@@ -141,3 +141,13 @@ export const LIVE_EVENTS_PROD_URL = `https://live-events.workers.bsky.app`
|
|||||||
export const LIVE_EVENTS_URL = IS_DEV
|
export const LIVE_EVENTS_URL = IS_DEV
|
||||||
? (LIVE_EVENTS_DEV_URL ?? LIVE_EVENTS_PROD_URL)
|
? (LIVE_EVENTS_DEV_URL ?? LIVE_EVENTS_PROD_URL)
|
||||||
: LIVE_EVENTS_PROD_URL
|
: LIVE_EVENTS_PROD_URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URLs for the app-config web worker. Can be a
|
||||||
|
* locally running server, see `env.example` for more.
|
||||||
|
*/
|
||||||
|
export const APP_CONFIG_DEV_URL = process.env.APP_CONFIG_DEV_URL
|
||||||
|
export const APP_CONFIG_PROD_URL = `https://app-config.workers.bsky.app`
|
||||||
|
export const APP_CONFIG_URL = IS_DEV
|
||||||
|
? (APP_CONFIG_DEV_URL ?? APP_CONFIG_PROD_URL)
|
||||||
|
: APP_CONFIG_PROD_URL
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {createContext, useContext} from 'react'
|
||||||
|
import {QueryClient, useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {APP_CONFIG_URL} from '#/env'
|
||||||
|
|
||||||
|
const qc = new QueryClient()
|
||||||
|
const appConfigQueryKey = ['app-config']
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the types defined in our `app-config` worker
|
||||||
|
*/
|
||||||
|
type AppConfigResponse = {
|
||||||
|
liveNow: {
|
||||||
|
allow: string[]
|
||||||
|
exceptions: {
|
||||||
|
did: string
|
||||||
|
allow: string[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_APP_CONFIG_RESPONSE: AppConfigResponse = {
|
||||||
|
liveNow: {
|
||||||
|
allow: [],
|
||||||
|
exceptions: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAppConfig(): Promise<AppConfigResponse | null> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${APP_CONFIG_URL}/config`)
|
||||||
|
if (!res.ok) return null
|
||||||
|
const data = await res.json()
|
||||||
|
return data
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Context = createContext<AppConfigResponse>(DEFAULT_APP_CONFIG_RESPONSE)
|
||||||
|
|
||||||
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
const {data} = useQuery(
|
||||||
|
{
|
||||||
|
staleTime: Infinity,
|
||||||
|
gcTime: Infinity,
|
||||||
|
queryKey: appConfigQueryKey,
|
||||||
|
async queryFn() {
|
||||||
|
return fetchAppConfig()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
qc,
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Context.Provider value={data ?? DEFAULT_APP_CONFIG_RESPONSE}>
|
||||||
|
{children}
|
||||||
|
</Context.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prefetchAppConfig() {
|
||||||
|
const data = await fetchAppConfig()
|
||||||
|
if (data) {
|
||||||
|
qc.setQueryData(appConfigQueryKey, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAppConfig() {
|
||||||
|
const ctx = useContext(Context)
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error('useAppConfig must be used within a Provider')
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user