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