import { queryClient } from '$lib/query/client'; import { qk } from '$lib/api/queries'; import { user } from '$lib/auth/store.svelte'; // Inbound server events over SSE (#968). The web client's OUTBOUND half // (play events) lives in player/events.svelte.ts; this is the inbound // listener it never had. Today it reacts to `playlist.system_rebuilt` — the // daily 03:00 rebuild or a manual refresh — by invalidating the home + // system-playlist query caches so a tab left open across the rebuild stops // serving yesterday's snapshot (the stale-browse-view bug). Active playback // self-heals separately on the failure path (store.handleLoadFailure); we // deliberately do NOT yank a playing queue here — that would interrupt a // mid-song listen to restart the new mix at track 0. // Monotonic rebuild counter (#980). Bumped on every playlist.system_rebuilt. // An open system-playlist DETAIL page can't be invalidated in place (its id // rotates on rebuild — a refetch would 404), so instead it watches this // counter and offers a "this mix was refreshed → Refresh" affordance that // re-resolves the variant to the new playlist. let _systemRebuildCount = $state(0); export const systemRebuilt = { get count(): number { return _systemRebuildCount; } }; function onSystemRebuilt(): void { _systemRebuildCount++; queryClient.invalidateQueries({ queryKey: qk.home() }); // Prefix match invalidates every kind ('user' | 'system' | 'all'). queryClient.invalidateQueries({ queryKey: ['playlists'] }); queryClient.invalidateQueries({ queryKey: qk.systemPlaylistsStatus() }); } // Wire once from +layout.svelte's mount. Opens the stream only while // authenticated and closes it on logout; EventSource auto-reconnects on // transient network drops. export function useServerEvents(): void { $effect(() => { if (!user.value) return; // Absent under SSR / jsdom — the listener simply no-ops there. if (typeof EventSource === 'undefined') return; const es = new EventSource('/api/events/stream'); es.addEventListener('playlist.system_rebuilt', onSystemRebuilt); return () => { es.removeEventListener('playlist.system_rebuilt', onSystemRebuilt); es.close(); }; }); }