diff --git a/web/src/lib/components/AlbumMenu.svelte b/web/src/lib/components/AlbumMenu.svelte index ab4775bb..2b5d99be 100644 --- a/web/src/lib/components/AlbumMenu.svelte +++ b/web/src/lib/components/AlbumMenu.svelte @@ -28,7 +28,12 @@ let menuOpen = $state(false); let addOpen = $state(false); - let cachedTracks: TrackRef[] | null = $state(tracks ?? null); + // Lazy track cache. Starts null so the `tracks` prop is read inside + // ensureTracks() each call rather than captured at component-creation + // time — the menu is reused across different album rows, and pinning + // the prop's initial value here would serve a stale list when the + // user opens the menu on a second album. + let cachedTracks: TrackRef[] | null = $state(null); const queryClient = useQueryClient(); const likedQ = $derived(createLikedIdsQuery()); @@ -50,6 +55,10 @@ async function ensureTracks(): Promise { if (cachedTracks) return cachedTracks; + if (tracks) { + cachedTracks = tracks; + return tracks; + } const fetched = await listAlbumTracks(albumId); cachedTracks = fetched; return fetched; @@ -105,6 +114,11 @@ {#if menuOpen} + +
{ if (cachedTracks) return cachedTracks; + if (tracks) { + cachedTracks = tracks; + return tracks; + } const fetched = await listArtistTracks(artistId); cachedTracks = fetched; return fetched; @@ -97,6 +104,10 @@ {#if menuOpen} + +
{track.artist_name}
+ + +
e.stopPropagation()}> {#if menuOpen} + +
(readPref()); +// svelte-ignore state_referenced_locally +// Module-scope state with manual sync — _theme is read once at module +// init to seed _resolved; subsequent updates flow through setTheme() +// (which writes both) and the matchMedia listener (which writes +// _resolved when prefers-color-scheme flips). Not a missed-reactivity +// case; $derived doesn't apply at module scope here. let _resolved = $state(resolve(_theme)); export const theme = {