Add liveEvents context
This commit is contained in:
@@ -69,6 +69,10 @@ import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbe
|
|||||||
import {ToastOutlet} from '#/components/Toast'
|
import {ToastOutlet} from '#/components/Toast'
|
||||||
import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance'
|
import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance'
|
||||||
import {prefetchAgeAssuranceConfig} from '#/ageAssurance'
|
import {prefetchAgeAssuranceConfig} from '#/ageAssurance'
|
||||||
|
import {
|
||||||
|
prefetchLiveEvents,
|
||||||
|
Provider as LiveEventsProvider,
|
||||||
|
} from '#/features/liveEvents/context'
|
||||||
import * as Geo from '#/geolocation'
|
import * as Geo from '#/geolocation'
|
||||||
import {Splash} from '#/Splash'
|
import {Splash} from '#/Splash'
|
||||||
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
||||||
@@ -92,6 +96,7 @@ if (isAndroid) {
|
|||||||
*/
|
*/
|
||||||
Geo.resolve()
|
Geo.resolve()
|
||||||
prefetchAgeAssuranceConfig()
|
prefetchAgeAssuranceConfig()
|
||||||
|
prefetchLiveEvents()
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const [isReady, setIsReady] = React.useState(false)
|
const [isReady, setIsReady] = React.useState(false)
|
||||||
@@ -230,7 +235,9 @@ function App() {
|
|||||||
<StarterPackProvider>
|
<StarterPackProvider>
|
||||||
<SafeAreaProvider
|
<SafeAreaProvider
|
||||||
initialMetrics={initialWindowMetrics}>
|
initialMetrics={initialWindowMetrics}>
|
||||||
|
<LiveEventsProvider>
|
||||||
<InnerApp />
|
<InnerApp />
|
||||||
|
</LiveEventsProvider>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
</StarterPackProvider>
|
</StarterPackProvider>
|
||||||
</BottomSheetProvider>
|
</BottomSheetProvider>
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbe
|
|||||||
import {ToastOutlet} from '#/components/Toast'
|
import {ToastOutlet} from '#/components/Toast'
|
||||||
import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance'
|
import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance'
|
||||||
import {prefetchAgeAssuranceConfig} from '#/ageAssurance'
|
import {prefetchAgeAssuranceConfig} from '#/ageAssurance'
|
||||||
|
import {
|
||||||
|
prefetchLiveEvents,
|
||||||
|
Provider as LiveEventsProvider,
|
||||||
|
} from '#/features/liveEvents/context'
|
||||||
import * as Geo from '#/geolocation'
|
import * as Geo from '#/geolocation'
|
||||||
import {Splash} from '#/Splash'
|
import {Splash} from '#/Splash'
|
||||||
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
||||||
@@ -67,6 +71,7 @@ import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottom
|
|||||||
*/
|
*/
|
||||||
Geo.resolve()
|
Geo.resolve()
|
||||||
prefetchAgeAssuranceConfig()
|
prefetchAgeAssuranceConfig()
|
||||||
|
prefetchLiveEvents()
|
||||||
|
|
||||||
function InnerApp() {
|
function InnerApp() {
|
||||||
const [isReady, setIsReady] = React.useState(false)
|
const [isReady, setIsReady] = React.useState(false)
|
||||||
@@ -198,7 +203,9 @@ function App() {
|
|||||||
<LightboxStateProvider>
|
<LightboxStateProvider>
|
||||||
<PortalProvider>
|
<PortalProvider>
|
||||||
<StarterPackProvider>
|
<StarterPackProvider>
|
||||||
|
<LiveEventsProvider>
|
||||||
<InnerApp />
|
<InnerApp />
|
||||||
|
</LiveEventsProvider>
|
||||||
</StarterPackProvider>
|
</StarterPackProvider>
|
||||||
</PortalProvider>
|
</PortalProvider>
|
||||||
</LightboxStateProvider>
|
</LightboxStateProvider>
|
||||||
|
|||||||
+6
-1
@@ -11,7 +11,12 @@ import {
|
|||||||
import {themes} from '#/alf/themes'
|
import {themes} from '#/alf/themes'
|
||||||
import {type Device} from '#/storage'
|
import {type Device} from '#/storage'
|
||||||
|
|
||||||
export {type TextStyleProp, type Theme, type ViewStyleProp} from '@bsky.app/alf'
|
export {
|
||||||
|
type TextStyleProp,
|
||||||
|
type Theme,
|
||||||
|
utils,
|
||||||
|
type ViewStyleProp,
|
||||||
|
} from '@bsky.app/alf'
|
||||||
export {atoms} from '#/alf/atoms'
|
export {atoms} from '#/alf/atoms'
|
||||||
export * from '#/alf/breakpoints'
|
export * from '#/alf/breakpoints'
|
||||||
export * from '#/alf/fonts'
|
export * from '#/alf/fonts'
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import {createContext, useContext} from 'react'
|
||||||
|
import {QueryClient, useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {IS_DEV, LIVE_EVENTS_URL} from '#/env'
|
||||||
|
import {type LiveEventsWorkerResponse} from '#/features/liveEvents/types'
|
||||||
|
|
||||||
|
const qc = new QueryClient()
|
||||||
|
const liveEventsQueryKey = ['live-events']
|
||||||
|
|
||||||
|
async function fetchLiveEvents(): Promise<LiveEventsWorkerResponse | null> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${LIVE_EVENTS_URL}/config`)
|
||||||
|
if (!res.ok) return null
|
||||||
|
const data = await res.json()
|
||||||
|
return data
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Context = createContext<LiveEventsWorkerResponse>({
|
||||||
|
feeds: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
const {data} = useQuery(
|
||||||
|
{
|
||||||
|
staleTime: IS_DEV ? 5e3 : 1000 * 60,
|
||||||
|
queryKey: liveEventsQueryKey,
|
||||||
|
async queryFn() {
|
||||||
|
return fetchLiveEvents()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
qc,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider value={data || {feeds: []}}>{children}</Context.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prefetchLiveEvents() {
|
||||||
|
const data = await fetchLiveEvents()
|
||||||
|
if (data) {
|
||||||
|
qc.setQueryData(liveEventsQueryKey, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLiveEvents() {
|
||||||
|
const ctx = useContext(Context)
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error('useLiveEventsContext must be used within a Provider')
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import {createContext, useContext} from 'react'
|
||||||
|
import {QueryClient, useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {IS_DEV, LIVE_EVENTS_URL} from '#/env'
|
||||||
|
|
||||||
|
type LiveEventFeedImageLayout = 'wide' // maybe more in the future
|
||||||
|
type LiveEventFeedImage = {
|
||||||
|
alt: string
|
||||||
|
overlayColor: string
|
||||||
|
url: string
|
||||||
|
blurhash: string
|
||||||
|
}
|
||||||
|
type LiveEventFeed = {
|
||||||
|
title: string
|
||||||
|
url: string
|
||||||
|
images: Partial<Record<LiveEventFeedImageLayout, LiveEventFeedImage>>
|
||||||
|
}
|
||||||
|
type LiveEventsConfig = {
|
||||||
|
feeds: LiveEventFeed[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const qc = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
/**
|
||||||
|
* We clear this manually, so disable automatic garbage collection.
|
||||||
|
* @see https://tanstack.com/query/latest/docs/framework/react/plugins/persistQueryClient#how-it-works
|
||||||
|
*/
|
||||||
|
gcTime: Infinity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const liveEventsQueryKey = ['live-events']
|
||||||
|
|
||||||
|
async function getLiveEvents(): Promise<LiveEventsConfig | null> {
|
||||||
|
const res = await fetch(`${LIVE_EVENTS_URL}/config`)
|
||||||
|
if (!res.ok) return null
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLiveEventsCache() {
|
||||||
|
return qc.getQueryData<LiveEventsConfig>(liveEventsQueryKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prefetchLiveEvents() {
|
||||||
|
const data = await getLiveEvents()
|
||||||
|
if (data) {
|
||||||
|
qc.setQueryData(liveEventsQueryKey, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Context = createContext<LiveEventsConfig>({
|
||||||
|
feeds: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
export function useLiveEventsContext() {
|
||||||
|
const ctx = useContext(Context)
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error('useLiveEventsContext must be used within a Provider')
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
const {data} = useQuery(
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Will re-fetch when stale, at most every minute (or 5s in dev for easier
|
||||||
|
* testing).
|
||||||
|
*
|
||||||
|
* @see https://tanstack.com/query/latest/docs/framework/react/guides/initial-query-data#initial-data-from-the-cache-with-initialdataupdatedat
|
||||||
|
*/
|
||||||
|
staleTime: IS_DEV ? 5e3 : 1000 * 60,
|
||||||
|
/**
|
||||||
|
* N.B. if prefetch failed above, we'll have no `initialData`, and this
|
||||||
|
* query will run on startup.
|
||||||
|
*/
|
||||||
|
initialData: getLiveEventsCache(),
|
||||||
|
initialDataUpdatedAt: () =>
|
||||||
|
qc.getQueryState(liveEventsQueryKey)?.dataUpdatedAt,
|
||||||
|
queryKey: liveEventsQueryKey,
|
||||||
|
async queryFn() {
|
||||||
|
console.debug(`live-events: fetching config`)
|
||||||
|
return getLiveEvents()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
qc,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Context.Provider value={data || {feeds: []}}>{children}</Context.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export type LiveEventFeedImageLayout = 'wide' // maybe more in the future
|
||||||
|
|
||||||
|
export type LiveEventFeedImage = {
|
||||||
|
alt: string
|
||||||
|
overlayColor: string
|
||||||
|
url: string
|
||||||
|
blurhash: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LiveEventFeed = {
|
||||||
|
title: string
|
||||||
|
url: string
|
||||||
|
images: Record<LiveEventFeedImageLayout, LiveEventFeedImage>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LiveEventsWorkerResponse = {
|
||||||
|
feeds: LiveEventFeed[]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user