Bluesky app fork with some witchin' additions 馃挮
at main 112 lines 3.1 kB view raw
1import {createContext, useContext, useMemo} from 'react' 2import {QueryClient, useQuery} from '@tanstack/react-query' 3 4import {useIsBskyTeam} from '#/lib/hooks/useIsBskyTeam' 5import { 6 convertBskyAppUrlIfNeeded, 7 isBskyCustomFeedUrl, 8 makeRecordUri, 9} from '#/lib/strings/url-helpers' 10import {LIVE_EVENTS_URL} from '#/env' 11import {useLiveEventPreferences} from '#/features/liveEvents/preferences' 12import {type LiveEventsWorkerResponse} from '#/features/liveEvents/types' 13import {useDevMode} from '#/storage/hooks/dev-mode' 14 15const qc = new QueryClient() 16const liveEventsQueryKey = ['live-events'] 17 18export const DEFAULT_LIVE_EVENTS = { 19 feeds: [], 20} 21 22async function fetchLiveEvents(): Promise<LiveEventsWorkerResponse | null> { 23 try { 24 const res = await fetch(`${LIVE_EVENTS_URL}/config`) 25 if (!res.ok) return null 26 const data = await res.json() 27 return data 28 } catch { 29 return null 30 } 31} 32 33const Context = createContext<LiveEventsWorkerResponse>(DEFAULT_LIVE_EVENTS) 34 35export function Provider({children}: React.PropsWithChildren<{}>) { 36 const [isDevMode] = useDevMode() 37 const isBskyTeam = useIsBskyTeam() 38 const {data} = useQuery( 39 { 40 // keep this, prefectching handles initial load 41 staleTime: 1000 * 15, 42 queryKey: liveEventsQueryKey, 43 refetchInterval: 1000 * 60 * 5, 44 async queryFn() { 45 return fetchLiveEvents() 46 }, 47 }, 48 qc, 49 ) 50 51 const ctx = useMemo(() => { 52 if (!data) return DEFAULT_LIVE_EVENTS 53 const feeds = data.feeds.filter(f => { 54 if (f.preview && !isBskyTeam) return false 55 return true 56 }) 57 return { 58 ...data, 59 // only one at a time for now, unless bsky team and dev mode 60 feeds: isBskyTeam && isDevMode ? feeds : feeds.slice(0, 1), 61 } 62 }, [data, isBskyTeam, isDevMode]) 63 64 return <Context.Provider value={ctx}>{children}</Context.Provider> 65} 66 67export async function prefetchLiveEvents() { 68 const data = await fetchLiveEvents() 69 if (data) { 70 qc.setQueryData(liveEventsQueryKey, data) 71 } 72} 73 74export function useLiveEvents() { 75 const ctx = useContext(Context) 76 if (!ctx) { 77 throw new Error('useLiveEventsContext must be used within a Provider') 78 } 79 return ctx 80} 81 82export function useUserPreferencedLiveEvents() { 83 const events = useLiveEvents() 84 const {data, isLoading} = useLiveEventPreferences() 85 if (isLoading) return DEFAULT_LIVE_EVENTS 86 const {hideAllFeeds, hiddenFeedIds} = data 87 return { 88 ...events, 89 feeds: hideAllFeeds 90 ? [] 91 : events.feeds.filter(f => { 92 const hidden = f?.id ? hiddenFeedIds.includes(f?.id || '') : false 93 return !hidden 94 }), 95 } 96} 97 98export function useActiveLiveEventFeedUris() { 99 const {feeds} = useLiveEvents() 100 101 return new Set( 102 feeds 103 // insurance 104 .filter(f => isBskyCustomFeedUrl(f.url)) 105 .map(f => { 106 const uri = convertBskyAppUrlIfNeeded(f.url) 107 const [_0, did, _1, rkey] = uri.split('/').filter(Boolean) 108 const urip = makeRecordUri(did, 'app.bsky.feed.generator', rkey) 109 return urip.toString() 110 }), 111 ) 112}