Files
bsky-social-app/src/features/liveEvents/context.tsx
T
2026-01-15 16:09:48 -06:00

113 lines
3.0 KiB
TypeScript

import {createContext, useContext, useMemo} from 'react'
import {QueryClient, useQuery} from '@tanstack/react-query'
import {useIsBskyTeam} from '#/lib/hooks/useIsBskyTeam'
import {
convertBskyAppUrlIfNeeded,
isBskyCustomFeedUrl,
makeRecordUri,
} from '#/lib/strings/url-helpers'
import {LIVE_EVENTS_URL} from '#/env'
import {useLiveEventPreferences} from '#/features/liveEvents/preferences'
import {type LiveEventsWorkerResponse} from '#/features/liveEvents/types'
import {useDevMode} from '#/storage/hooks/dev-mode'
const qc = new QueryClient()
const liveEventsQueryKey = ['live-events']
export const DEFAULT_LIVE_EVENTS = {
feeds: [],
}
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>(DEFAULT_LIVE_EVENTS)
export function Provider({children}: React.PropsWithChildren<{}>) {
const [isDevMode] = useDevMode()
const isBskyTeam = useIsBskyTeam()
const {data} = useQuery(
{
// keep this, prefectching handles initial load
staleTime: 1000 * 15,
queryKey: liveEventsQueryKey,
refetchInterval: 1000 * 60 * 5,
async queryFn() {
return fetchLiveEvents()
},
},
qc,
)
const ctx = useMemo(() => {
if (!data) return DEFAULT_LIVE_EVENTS
const feeds = data.feeds.filter(f => {
if (f.preview && !isBskyTeam) return false
return true
})
return {
...data,
// only one at a time for now, unless bsky team and dev mode
feeds: isBskyTeam && isDevMode ? feeds : feeds.slice(0, 1),
}
}, [data, isBskyTeam, isDevMode])
return <Context.Provider value={ctx}>{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
}
export function useUserPreferencedLiveEvents() {
const events = useLiveEvents()
const {data, isLoading} = useLiveEventPreferences()
if (isLoading) return DEFAULT_LIVE_EVENTS
const {hideAllFeeds, hiddenFeedIds} = data
return {
...events,
feeds: hideAllFeeds
? []
: events.feeds.filter(f => {
const hidden = f?.id ? hiddenFeedIds.includes(f?.id || '') : false
return !hidden
}),
}
}
export function useActiveLiveEventFeedUris() {
const {feeds} = useLiveEvents()
return new Set(
feeds
// insurance
.filter(f => isBskyCustomFeedUrl(f.url))
.map(f => {
const uri = convertBskyAppUrlIfNeeded(f.url)
const [_0, did, _1, rkey] = uri.split('/').filter(Boolean)
const urip = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
return urip.toString()
}),
)
}