From 98d11a2228185303d0fcbac58cb764dc561b8b81 Mon Sep 17 00:00:00 2001
From: KeelrLeek <156356974+KeelrLeek@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:13:54 -0500
Subject: [PATCH] previews: the album button opens the record the previewed
track came from
The third action in the preview modal's control row has been a disabled
stub since the modal landed -- there was nowhere for it to go until the
album page existed. It goes there now.
The target is the item's own provider `albumId`, never a title match, so a
top-tracks preview lands on the exact record the modal's metadata line is
already naming: Elephant Gym's "Light" opens Angle, and AC/DC's "Highway
to Hell" the album rather than any of the compilations carrying it. The
manifest has carried `albumId` and an `actions.openAlbum` flag from the
start; all 20 rows of a Deezer top-track payload declare one.
The navigation lands on the previewed TRACK, not just the record -- the
album page's existing pending-track flash does the scrolling and the
highlight, the same landing the now-playing song link already gets.
Three details that are not obvious:
- The overlay must not import state.svelte.js (the module comment on
previewSession explains the cycle), so the navigator is injected by the
page exactly as `playFull` already is.
- Both ids are read out of previewUi and the snapshot BEFORE the close, not
after: closePreview() nulls the callback and drops the snapshot the
active item came from. And the close happens first, because the session
holds the audio focus and navigating under an open dialog would land the
new page behind the scrim.
- contracts.js freezes every manifest id as a String, while `catalog_album`
takes a u64 -- a JSON string fails to deserialize -- and the album page's
flash compares track ids with ===. Both cross back to numbers in
openAlbumFromPreviewItem, once, rather than at each call site.
An album session is already standing on the record it would open, so the
button greys out there with "You're already on this album" rather than
re-navigating to the page underneath. The decision is made from the
manifest's own contextKey, so it holds for any future surface that opens an
album-kind session. The album page still supplies the navigator, so a row
that ever declares a different album goes somewhere instead of being
silently dead.
Verified in the running app over CDP: artist top-tracks preview -> click ->
lands on Highway to Hell (id 9410086), pendingTrackId 92719900 as a Number,
matching track found on the page, .trow.flash present, modal gone, and the
glass bar trail reading Search -> AC/DC -> Highway to Hell. From an album
session the button is disabled and a click changes nothing. 85 JS tests
pass.
---
src/components/PreviewOverlay.svelte | 45 +++++++++++++++++++++++-
src/lib/preview/previewSession.svelte.js | 6 +++-
src/lib/state.svelte.js | 23 ++++++++++++
src/views/CatalogAlbum.svelte | 6 ++++
src/views/CatalogArtist.svelte | 2 ++
5 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/src/components/PreviewOverlay.svelte b/src/components/PreviewOverlay.svelte
index 1038800..ed931ec 100644
--- a/src/components/PreviewOverlay.svelte
+++ b/src/components/PreviewOverlay.svelte
@@ -436,6 +436,43 @@
await previewCommands.playFullTrack(async (item) => starter(item));
await closePreview();
}
+
+ // "Open album" goes to the record the PREVIEWED TRACK declares, which on a
+ // top-tracks session is often a single or a compilation rather than the
+ // artist's studio album — that is the record the metadata line above is
+ // already naming, and the id comes from the provider rather than a title
+ // match, so the two can never disagree.
+ //
+ // An album session is already standing on that record, so the action is
+ // offered only when it would actually take you somewhere.
+ const albumTarget = $derived(
+ activeItem?.actions.openAlbum && activeItem.albumId ? activeItem.albumId : null,
+ );
+ const atOwnAlbum = $derived(
+ albumTarget != null && manifest?.context.contextKey === `album:${albumTarget}`,
+ );
+ const canOpenAlbum = $derived(
+ Boolean(previewUi.openAlbum) && albumTarget != null && !atOwnAlbum,
+ );
+ const openAlbumTitle = $derived(
+ atOwnAlbum
+ ? "You're already on this album"
+ : albumTarget == null
+ ? "No album to open for this track"
+ : "Open album",
+ );
+
+ async function onOpenAlbum() {
+ if (!canOpenAlbum) return;
+ // Both are read BEFORE the close, which nulls the callback and drops
+ // the snapshot the active item came from.
+ const go = previewUi.openAlbum;
+ const item = activeItem;
+ // Close first: the session holds the audio focus, and navigating under
+ // an open dialog would land the new page behind the scrim.
+ await closePreview();
+ go(item);
+ }
@@ -570,7 +607,13 @@
-