You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
4.1 KiB
141 lines
4.1 KiB
import { |
|
buildLiveActivitiesRelayUrls, |
|
LIVE_ACTIVITY_KINDS, |
|
mergeLiveActivityEvents, |
|
msUntilNextQuarterHour, |
|
resolveParentSpacesForLiveActivities, |
|
type TLiveActivityItem |
|
} from '@/lib/live-activities' |
|
import logger from '@/lib/logger' |
|
import client from '@/services/client.service' |
|
import { registerLiveActivitiesPrewarmCallback } from '@/services/live-activities-prewarm-bridge' |
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' |
|
import { useFavoriteRelays } from './FavoriteRelaysProvider' |
|
import { useFollowList } from './FollowListProvider' |
|
import { useNostr } from './NostrProvider' |
|
import { useUserPreferences } from './UserPreferencesProvider' |
|
|
|
type TLiveActivitiesContext = { |
|
items: TLiveActivityItem[] |
|
loading: boolean |
|
} |
|
|
|
const LiveActivitiesContext = createContext<TLiveActivitiesContext | undefined>(undefined) |
|
|
|
export function useLiveActivities(): TLiveActivitiesContext { |
|
const ctx = useContext(LiveActivitiesContext) |
|
if (!ctx) { |
|
throw new Error('useLiveActivities must be used within LiveActivitiesProvider') |
|
} |
|
return ctx |
|
} |
|
|
|
export function useLiveActivitiesOptional(): TLiveActivitiesContext | undefined { |
|
return useContext(LiveActivitiesContext) |
|
} |
|
|
|
export function LiveActivitiesProvider({ children }: { children: React.ReactNode }) { |
|
const { pubkey, relayList, isInitialized, isAccountSessionHydrating } = useNostr() |
|
const { favoriteRelays, blockedRelays } = useFavoriteRelays() |
|
const { followings } = useFollowList() |
|
const { showLiveActivitiesBanner } = useUserPreferences() |
|
|
|
const [items, setItems] = useState<TLiveActivityItem[]>([]) |
|
const [loading, setLoading] = useState(false) |
|
|
|
const relayRead = relayList?.read ?? [] |
|
const relayWrite = relayList?.write ?? [] |
|
|
|
const refresh = useCallback(async () => { |
|
if (!showLiveActivitiesBanner) { |
|
setItems([]) |
|
return |
|
} |
|
const loggedIn = Boolean(pubkey) |
|
const urls = buildLiveActivitiesRelayUrls({ |
|
loggedIn, |
|
favoriteRelays, |
|
blockedRelays, |
|
relayListRead: relayRead, |
|
relayListWrite: relayWrite |
|
}) |
|
if (loggedIn && urls.length === 0) { |
|
setItems([]) |
|
return |
|
} |
|
setLoading(true) |
|
try { |
|
const events = await client.fetchEvents( |
|
urls, |
|
{ kinds: [...LIVE_ACTIVITY_KINDS], limit: 500 }, |
|
{ eoseTimeout: 6000, globalTimeout: 14_000 } |
|
) |
|
const parentByAddress = await resolveParentSpacesForLiveActivities(events, urls, (u, f, o) => |
|
client.fetchEvents(u, f, o) |
|
) |
|
const merged = mergeLiveActivityEvents(events, followings, parentByAddress) |
|
setItems(merged) |
|
logger.debug('[LiveActivities] poll done', { relayCount: urls.length, raw: events.length, merged: merged.length }) |
|
} catch (e) { |
|
logger.warn('[LiveActivities] poll failed', { err: e }) |
|
setItems([]) |
|
} finally { |
|
setLoading(false) |
|
} |
|
}, [ |
|
showLiveActivitiesBanner, |
|
pubkey, |
|
favoriteRelays, |
|
blockedRelays, |
|
relayRead, |
|
relayWrite, |
|
followings |
|
]) |
|
|
|
const refreshRef = useRef(refresh) |
|
refreshRef.current = refresh |
|
|
|
useEffect(() => { |
|
registerLiveActivitiesPrewarmCallback(() => { |
|
void refreshRef.current() |
|
}) |
|
return () => registerLiveActivitiesPrewarmCallback(null) |
|
}, []) |
|
|
|
useEffect(() => { |
|
if (!showLiveActivitiesBanner) { |
|
setItems([]) |
|
return |
|
} |
|
if (!isInitialized) return |
|
if (pubkey && isAccountSessionHydrating) return |
|
void refresh() |
|
}, [ |
|
showLiveActivitiesBanner, |
|
isInitialized, |
|
pubkey, |
|
isAccountSessionHydrating, |
|
refresh |
|
]) |
|
|
|
useEffect(() => { |
|
if (!showLiveActivitiesBanner) return |
|
const id = window.setTimeout(() => { |
|
void refreshRef.current() |
|
}, msUntilNextQuarterHour()) |
|
const interval = window.setInterval( |
|
() => { |
|
void refreshRef.current() |
|
}, |
|
15 * 60 * 1000 |
|
) |
|
return () => { |
|
window.clearTimeout(id) |
|
window.clearInterval(interval) |
|
} |
|
}, [showLiveActivitiesBanner]) |
|
|
|
const value = useMemo(() => ({ items, loading }), [items, loading]) |
|
|
|
return <LiveActivitiesContext.Provider value={value}>{children}</LiveActivitiesContext.Provider> |
|
}
|
|
|