From e932ab438ca2b473e0c2c1000826d09b0ebee2e6 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 20 Jun 2026 19:48:52 -0400 Subject: [PATCH] feat(web/playlists): stale-view banner + Refresh on an open system-playlist after rebuild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #980. When the daily rebuild fires while a system-playlist detail page is open, its cached data goes stale and can't be refetched in place — the playlist id rotated, so the old id 404s. serverEvents now exposes a monotonic rebuild counter; the detail page shows a "this mix was refreshed" banner with a Refresh that re-resolves the variant (systemShuffle) to the new playlist id and navigates there. No forced redirect, no auto-reload — the user refreshes on their terms. Functional behaviors were already correct (tapping a song plays it; tiles load the current mix); this closes the cosmetic list-staleness. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/lib/serverEvents.svelte.ts | 13 +++++ web/src/routes/playlists/[id]/+page.svelte | 57 +++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/web/src/lib/serverEvents.svelte.ts b/web/src/lib/serverEvents.svelte.ts index d3ab5bcc..98699045 100644 --- a/web/src/lib/serverEvents.svelte.ts +++ b/web/src/lib/serverEvents.svelte.ts @@ -12,7 +12,20 @@ import { user } from '$lib/auth/store.svelte'; // 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'] }); diff --git a/web/src/routes/playlists/[id]/+page.svelte b/web/src/routes/playlists/[id]/+page.svelte index f35fcd06..5581aecb 100644 --- a/web/src/routes/playlists/[id]/+page.svelte +++ b/web/src/routes/playlists/[id]/+page.svelte @@ -1,6 +1,7 @@ @@ -187,6 +224,24 @@ playlistQuery.refetch()} /> {:else if playlistQuery?.data} {@const pl = playlistQuery.data} + {#if staleSystemView} +
+ This mix was refreshed since you opened it. + +
+ {/if} {#if !editing}