artist page: always open at the top
.content is the app's one scroll pane and it outlives the view swap, so its scrollTop was inherited by whatever loaded next: leave one artist 900px down their discography, open another, and you arrived 900px down the new one — mid-grid, hero already squeezed into the bar, with no hint the page had a top. Reset it on catalog.artistOpenSeq, a counter bumped once per openCatalogArtist call, rather than on the artist's id. The id cannot distinguish a re-open of the artist already on screen — clicking their name from Now Playing while parked deep in their own page, where nothing moved and the click read as dead — from no navigation at all. Nor can this key on the page object: the cold path replaces it stage by stage under the same id, which would haul a reader back to the top mid-read. The counter bumps once per open and never per stage. The bump sits after openCatalogArtist's invalid-id bail-out, so a request that never opens anything cannot scroll the page the reader is still on. Verified live: three artist-to-artist hops parked at 1222 each land at 0 and stay 0 through every cold stage; re-opening the artist already on screen from 1070 lands at 0; mounting from another view with the pane genuinely parked at 1500 mounts at 0; and a reader parked at 600 on the artist they are already on holds at 600 through six seconds of stage churn.
This commit is contained in:
1 parent
0878659f6a
commit
e58410d9f8
2 files changed
+29
No files matched your search
@@ -751,6 +751,13 @@ export const catalog = $state({
|
||||
artistLoading: false,
|
||||
artistStageError: "",
|
||||
artistPage: null, // { artist, albums }
|
||||
// Bumped once per openCatalogArtist call, INCLUDING a re-open of the artist
|
||||
// already on screen. The view opens itself at the top off this rather than
|
||||
// off the artist's id: the id cannot tell a re-open from no navigation at
|
||||
// all, and it cannot tell the cold path's stage-by-stage replacement of
|
||||
// artistPage (same id, new object, several times a second) from a real one
|
||||
// — keying on that would drag a reader back to the top mid-read.
|
||||
artistOpenSeq: 0,
|
||||
albumLoading: false,
|
||||
albumPage: null, // { album, tracks }
|
||||
pendingTrackId: null, // a track to scroll to/flash on the album page
|
||||
@@ -1533,6 +1540,9 @@ export async function openCatalogArtist(artistId, seedArtist = null) {
|
||||
const request = ++artistPageRequest;
|
||||
const artistKey = artistCacheKey(artistId);
|
||||
if (!artistKey) return;
|
||||
// After the bail-out above, so a request we never act on cannot scroll the
|
||||
// page the reader is still on.
|
||||
catalog.artistOpenSeq += 1;
|
||||
activateCoverUpgradeScope(artistId);
|
||||
ui.view = "catalogArtist";
|
||||
const active = () =>
|
||||
|
||||
@@ -113,6 +113,25 @@
|
||||
return () => clearTimeout(t);
|
||||
});
|
||||
|
||||
// Every artist page opens at the top. `.content` is the app's ONE scroll
|
||||
// pane and it outlives the view swap, so its scrollTop carried over: leave
|
||||
// one artist 900px down its discography, open another, and you landed 900px
|
||||
// down the new one — mid-grid, hero already squeezed into the bar, with no
|
||||
// hint the page had a top.
|
||||
// Keyed on catalog.artistOpenSeq, NOT the artist's id. The id cannot tell a
|
||||
// re-open of the artist already on screen (clicking their name from Now
|
||||
// Playing while parked deep in their own page — nothing moved) from no
|
||||
// navigation at all. It also cannot be keyed on `page`: the cold path
|
||||
// replaces that object stage by stage under the same id, which would haul a
|
||||
// reader back to the top mid-read. The seq bumps once per open and never
|
||||
// per stage. Ask for the pane BY NAME — the same rule scrollPaneFor()
|
||||
// follows, and for the same reason.
|
||||
$effect(() => {
|
||||
void catalog.artistOpenSeq;
|
||||
const pane = document.querySelector(".content");
|
||||
if (pane) pane.scrollTop = 0;
|
||||
});
|
||||
|
||||
// THE HERO PIPELINE (Kyle, 2026-07-24 — the approved configuration,
|
||||
// now unconditional):
|
||||
// - Crossfade twins: the main-thread blur filter rides on the hero
|
||||
|
||||
Reference in new issue
Block a user