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:
@@ -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
|
||||
|
||||
@@ -1386,9 +1386,9 @@ export class PlaylistDetails
|
||||
? html`<img src="${track.CoverArtSmall || track.CoverArtMedium}" alt="" />`
|
||||
: nothing}
|
||||
</div>
|
||||
<span class="cell col-title" title="${track.Title || track.FilePath}">${trackLink(track.Title, track.Album, track.ReleaseGroupMBID, track.RecordingMBID) || track.FilePath}</span>
|
||||
<span class="cell col-title" title="${track.Title || track.FilePath}">${trackLink(track.Title, track.Album, track.ReleaseGroupMBID, track.RecordingMBID, undefined, track.Artist) || track.FilePath}</span>
|
||||
<span class="cell col-artist" title="${track.Artist}">${artistLink(track.Artist, track.ArtistMBID)}</span>
|
||||
<span class="cell col-album" title="${track.Album}">${albumLink(track.Album, track.ReleaseGroupMBID)}</span>
|
||||
<span class="cell col-album" title="${track.Album}">${albumLink(track.Album, track.ReleaseGroupMBID, undefined, track.Artist)}</span>
|
||||
<span class="cell col-duration">${formatMilliseconds(track.Duration)}</span>`}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1422,7 +1422,7 @@ export class QueuePanel
|
||||
${artUrl ? html`<div class="track-art"><img src="${artUrl}" alt="" loading="lazy" /></div>` : nothing}
|
||||
<div class="track-details">
|
||||
<span class="track-title">
|
||||
${trackLink(this.getDisplayTitle(track), track.album, track.releaseGroupMbid, track.recordingMbid)}
|
||||
${trackLink(this.getDisplayTitle(track), track.album, track.releaseGroupMbid, track.recordingMbid, undefined, track.artist)}
|
||||
</span>
|
||||
<span class="track-artist">
|
||||
${artistLink(track.artist, track.artistMbid) || 'Unknown Artist'}
|
||||
|
||||
@@ -1245,9 +1245,9 @@ export class SmartPlaylistDetails
|
||||
? html`<img src="${track.CoverArtSmall || track.CoverArtMedium}" alt="" />`
|
||||
: nothing}
|
||||
</div>
|
||||
<span class="cell col-title" title="${track.Title || track.FilePath}">${trackLink(track.Title, track.Album, track.ReleaseGroupMBID, track.RecordingMBID) || track.FilePath}</span>
|
||||
<span class="cell col-title" title="${track.Title || track.FilePath}">${trackLink(track.Title, track.Album, track.ReleaseGroupMBID, track.RecordingMBID, undefined, track.Artist) || track.FilePath}</span>
|
||||
<span class="cell col-artist" title="${track.Artist}">${artistLink(track.Artist, track.ArtistMBID)}</span>
|
||||
<span class="cell col-album" title="${track.Album}">${albumLink(track.Album, track.ReleaseGroupMBID)}</span>
|
||||
<span class="cell col-album" title="${track.Album}">${albumLink(track.Album, track.ReleaseGroupMBID, undefined, track.Artist)}</span>
|
||||
<span class="cell col-duration">${formatMilliseconds(track.Duration)}</span>`}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1766,11 +1766,11 @@ export class TrackList extends LitElement implements SelectionHost, ContextMenuH
|
||||
|
||||
// Wrap artist/album/track values in explore links.
|
||||
if (col.id === 'trackName') {
|
||||
display = trackLink(track.TrackName, track.Album, track.ReleaseGroupMBID, track.RecordingMBID, display as any);
|
||||
display = trackLink(track.TrackName, track.Album, track.ReleaseGroupMBID, track.RecordingMBID, display as any, track.ArtistName);
|
||||
} else if (col.id === 'artistName') {
|
||||
display = artistLink(track.ArtistName, track.ArtistMBID, display as any);
|
||||
} else if (col.id === 'album') {
|
||||
display = albumLink(track.Album, track.ReleaseGroupMBID, display as any);
|
||||
display = albumLink(track.Album, track.ReleaseGroupMBID, display as any, track.ArtistName);
|
||||
}
|
||||
|
||||
return html`
|
||||
|
||||
Reference in New Issue
Block a user