Restore smart back-button label on artist detail page

PR #644 removed the back-button label logic as collateral when removing
the full originStack. The label is independent of the stack — restore it
without restoring the old click-handler navigation (browser history handles
that now).

- _artistDetailLabelStack: module-level stack of {type:'page',pageId} or
  {type:'artist',name} entries, pushed on forward navigation, popped on back
- _artistDetailGoingBack flag: set by the back button click handler so
  navigateToArtistDetail knows to pop instead of push when called by the
  React route on browser-history navigation
- Backfill currentArtistName from the API response so URL-driven entries
  (which pass '' for name) have real names on state before the next similar-
  artist navigation pushes them onto the stack
- No-history fallback navigates to the recorded origin page
This commit is contained in:
Broque Thomas 2026-05-19 12:53:37 -07:00
parent 4bfa43bece
commit e7ba5408aa
2 changed files with 70 additions and 2 deletions

View file

@ -3417,6 +3417,7 @@ const WHATS_NEW = {
{ date: 'May 18, 2026 — 2.5.6 release' },
{ title: 'MusicBrainz as Primary Metadata Source', desc: 'MusicBrainz is now a full primary metadata source on equal footing with Deezer, iTunes, Spotify, and Discogs. switch to it in Settings → Metadata Source — always available, no account or API key needed, rate-limited to 1 req/sec. covers all primary source flows: search, album/track/artist lookup, watchlist scans, discover hero, similar artist backfill, artist map.', page: 'settings' },
{ title: 'Fix: MusicBrainz artist detail showing MBID as name', desc: 'clicking a MusicBrainz artist from search results was showing the raw MBID as the artist name on the detail page. URL-driven routing (PR #644) no longer passes the display name to the backend, so the source detail endpoint now looks it up directly from MusicBrainz by MBID.' },
{ title: 'Fix: artist detail back button always showing "← Back"', desc: 'PR #644 removed the back-button label logic along with the origin stack. restored: a label stack (separate from browser history, which handles actual navigation) tracks where you came from across the full similar-artist chain — "← Back to Search", "← Back to Artist A", "← Back to Artist B", etc. API response backfills the current artist name so the stack has real names when clicking similar artists.' },
],
'2.5.5': [
{ date: 'May 17, 2026 — 2.5.5 release' },

View file

@ -680,6 +680,26 @@ async function toggleLibraryCardWatchlist(btn, artist) {
// ===============================================
// Artist detail page state
const _ARTIST_DETAIL_BACK_LABELS = {
library: 'Back to Library',
search: 'Back to Search',
discover: 'Back to Discover',
watchlist: 'Back to Watchlist',
wishlist: 'Back to Wishlist',
stats: 'Back to Stats',
'playlist-explorer': 'Back to Explorer',
automations: 'Back to Automations',
dashboard: 'Back to Dashboard',
sync: 'Back to Sync',
'active-downloads': 'Back to Downloads',
};
// Stack of origins for the back-button label. Each entry: {type:'page', pageId}
// or {type:'artist', name}. Pushed on forward navigation, popped on back.
// Separate from browser history — only used for the label display.
let _artistDetailLabelStack = [];
let _artistDetailGoingBack = false;
let artistDetailPageState = {
isInitialized: false,
currentArtistId: null,
@ -797,6 +817,23 @@ function navigateToArtistDetail(artistId, artistName, sourceOverride = null, opt
}
console.log(`🎵 Navigating to artist detail: ${artistName} (ID: ${artistId}${sourceOverride ? `, source: ${sourceOverride}` : ''})`);
// Maintain the label stack. Back navigations pop; forward navigations push.
if (_artistDetailGoingBack) {
_artistDetailLabelStack.pop();
_artistDetailGoingBack = false;
} else {
if (currentPage !== 'artist-detail') {
_artistDetailLabelStack = []; // fresh chain from a non-artist page
}
if (currentPage === 'artist-detail' && artistDetailPageState.currentArtistName) {
_artistDetailLabelStack.push({ type: 'artist', name: artistDetailPageState.currentArtistName });
} else {
const pageId = (typeof currentPage === 'string' && currentPage && currentPage !== 'artist-detail')
? currentPage : 'library';
_artistDetailLabelStack.push({ type: 'page', pageId });
}
}
// Abort any in-progress completion stream
if (artistDetailPageState.completionController) {
artistDetailPageState.completionController.abort();
@ -851,10 +888,28 @@ function navigateToArtistDetail(artistId, artistName, sourceOverride = null, opt
initializeArtistDetailPage();
}
_updateArtistDetailBackButtonLabel();
// Load artist data
loadArtistDetailData(artistId, artistName);
}
function _updateArtistDetailBackButtonLabel() {
const backBtnLabel = document.querySelector('#artist-detail-back-btn span');
if (!backBtnLabel) return;
const top = _artistDetailLabelStack[_artistDetailLabelStack.length - 1];
if (!top) {
backBtnLabel.textContent = '← Back';
return;
}
if (top.type === 'artist') {
backBtnLabel.textContent = `← Back to ${top.name}`;
} else {
const friendly = _ARTIST_DETAIL_BACK_LABELS[top.pageId] || _ARTIST_DETAIL_BACK_LABELS.library;
backBtnLabel.textContent = `${friendly}`;
}
}
function initializeArtistDetailPage() {
console.log("🔧 Initializing Artist Detail page...");
@ -870,12 +925,15 @@ function initializeArtistDetailPage() {
}
if (window.history.length > 1) {
_artistDetailGoingBack = true;
window.history.back();
return;
}
// No history — default to library
navigateToPage('library');
// No history — fall back to recorded origin page or library
const top = _artistDetailLabelStack.pop();
_updateArtistDetailBackButtonLabel();
navigateToPage(top?.type === 'page' ? (top.pageId || 'library') : 'library');
});
}
@ -981,6 +1039,15 @@ async function loadArtistDetailData(artistId, artistName) {
artistDetailPageState.currentArtistId = data.artist.id;
}
// Backfill name from API response — URL-driven navigation passes '' for the
// name so the label stack has real names when the user clicks a similar artist.
if (data.artist?.name && !artistDetailPageState.currentArtistName) {
artistDetailPageState.currentArtistName = data.artist.name;
if (typeof _updateSidebarLibraryBreadcrumb === 'function') {
_updateSidebarLibraryBreadcrumb();
}
}
// Keep the resolved metadata source for album-track lookups.
artistDetailPageState.currentArtistSource = data.discography?.source || data.artist?.source || null;