fix(ui): make every track, album and artist name navigate somewhere

A name linked only when the entity carried an MBID — and for tracks,
only when it carried two. That rule is invisible, so a track list read
as randomly broken: some titles were clickable, most were not, and
nothing on screen said why.

A name now always goes somewhere. Tagged entities open their
MusicBrainz page as before; untagged ones open the *library* page for
the same album or artist, which both detail views already support via
a local id — they just had no caller passing one. An untagged track
highlights by title, since a recording MBID is exactly what it lacks.

Links now fire on a genuine single click only. Every list these appear
in also plays a row on double-click, and the title is the widest thing
in the row, so the first click of that gesture lands on the link:
navigating immediately meant double-clicking a track title opened a
page instead of playing it, which the e2e playback suite caught. The
navigation is held for one double-click interval and dropped if the
second click arrives, while the dblclick itself is left to bubble to
the row — so rows do not need to know links exist.
This commit is contained in:
2026-08-11 01:15:00 -04:00
parent 0ca37a31a6
commit 7c3c0e25b9
7 changed files with 254 additions and 97 deletions
@@ -97,6 +97,11 @@ export class ExploreAlbumDetails extends LitElement {
@property({ type: String, attribute: 'highlight-track-mbid' })
highlightTrackMBID = '';
/** Highlight target for a track with no recording MBID — the only
* handle an untagged track has is its title. */
@property({ type: String, attribute: 'highlight-track-title' })
highlightTrackTitle = '';
@property({ type: Number, attribute: 'local-album-id' })
localAlbumId = 0;
@@ -552,13 +557,15 @@ export class ExploreAlbumDetails extends LitElement {
override updated() {
if (
this.highlightTrackMBID &&
(this.highlightTrackMBID || this.highlightTrackTitle) &&
!this.hasScrolledToHighlight &&
!this.loadingReleases
) {
const el = this.shadowRoot?.querySelector<HTMLElement>(
`[data-track-mbid="${this.highlightTrackMBID}"]`,
);
const el = this.highlightTrackMBID
? this.shadowRoot?.querySelector<HTMLElement>(
`[data-track-mbid="${this.highlightTrackMBID}"]`,
)
: this.findRowByTitle(this.highlightTrackTitle);
if (el) {
this.hasScrolledToHighlight = true;
@@ -609,6 +616,26 @@ export class ExploreAlbumDetails extends LitElement {
}
}
/**
* Locate a track row by title, for tracks with no recording MBID.
* Matched in JS rather than by attribute selector because titles
* contain quotes and other things a selector cannot carry.
*/
private findRowByTitle(title: string): HTMLElement | null {
const wanted = title.trim().toLowerCase();
const rows = this.shadowRoot?.querySelectorAll<HTMLElement>(
'.track-row[data-track-title]',
);
for (const row of rows ?? []) {
if ((row.dataset.trackTitle ?? '').toLowerCase() === wanted) {
return row;
}
}
return null;
}
/* ── Data Loading ── */
private async loadAllData() {
@@ -1904,6 +1931,7 @@ export class ExploreAlbumDetails extends LitElement {
<div
class="track-row"
data-track-mbid="${track.mbid}"
data-track-title="${track.title}"
>
<span class="track-position"
>${track.position}</span