feat: add artist images to similar artists section
After similar artists load, fetch images for each via GetArtistImageURL in parallel. Images pop in as they resolve — letter avatars remain as fallback for artists without images. Each image is cached on disk after first resolution, so subsequent views are instant.
This commit is contained in:
@@ -91,6 +91,7 @@ export class ExploreArtistDetails extends LitElement {
|
|||||||
@state() private similarArtists: LBSimilarArtist[] = [];
|
@state() private similarArtists: LBSimilarArtist[] = [];
|
||||||
@state() private loadingSimilar = true;
|
@state() private loadingSimilar = true;
|
||||||
@state() private artistImageURL = '';
|
@state() private artistImageURL = '';
|
||||||
|
@state() private similarImageURLs = new Map<string, string>();
|
||||||
private libraryMBIDs = new Set<string>();
|
private libraryMBIDs = new Set<string>();
|
||||||
|
|
||||||
/* ── Styles ── */
|
/* ── Styles ── */
|
||||||
@@ -474,6 +475,13 @@ export class ExploreArtistDetails extends LitElement {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-avatar img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.similar-name {
|
.similar-name {
|
||||||
@@ -587,6 +595,31 @@ export class ExploreArtistDetails extends LitElement {
|
|||||||
} finally {
|
} finally {
|
||||||
this.loadingSimilar = false;
|
this.loadingSimilar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fire-and-forget: resolve images for similar artists in parallel.
|
||||||
|
if (this.similarArtists.length > 0) {
|
||||||
|
void this.fetchSimilarArtistImages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchSimilarArtistImages() {
|
||||||
|
const artists = this.similarArtists;
|
||||||
|
// Fetch in parallel — each call is cached after first resolution.
|
||||||
|
await Promise.allSettled(
|
||||||
|
artists.map(async (a) => {
|
||||||
|
try {
|
||||||
|
const url = await GetArtistImageURL(a.artistMbid);
|
||||||
|
if (url) {
|
||||||
|
this.similarImageURLs = new Map(this.similarImageURLs).set(
|
||||||
|
a.artistMbid,
|
||||||
|
url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// No image — letter avatar stays.
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchArtistImage(mbid: string) {
|
private async fetchArtistImage(mbid: string) {
|
||||||
@@ -675,6 +708,12 @@ export class ExploreArtistDetails extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** On similar-artist image error, remove the img so the letter initial shows. */
|
||||||
|
private handleSimilarImageError(e: Event) {
|
||||||
|
const img = e.target as HTMLImageElement;
|
||||||
|
img.remove();
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Helpers ── */
|
/* ── Helpers ── */
|
||||||
|
|
||||||
private getInitial(name: string): string {
|
private getInitial(name: string): string {
|
||||||
@@ -977,6 +1016,7 @@ export class ExploreArtistDetails extends LitElement {
|
|||||||
<div class="horizontal-row">
|
<div class="horizontal-row">
|
||||||
${this.similarArtists.map((a) => {
|
${this.similarArtists.map((a) => {
|
||||||
const hue = nameToHue(a.name);
|
const hue = nameToHue(a.name);
|
||||||
|
const imgURL = this.similarImageURLs.get(a.artistMbid);
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
class="similar-artist-card"
|
class="similar-artist-card"
|
||||||
@@ -997,7 +1037,13 @@ export class ExploreArtistDetails extends LitElement {
|
|||||||
class="similar-avatar"
|
class="similar-avatar"
|
||||||
style="background: hsl(${hue}, 45%, 35%)"
|
style="background: hsl(${hue}, 45%, 35%)"
|
||||||
>
|
>
|
||||||
${a.name.charAt(0).toUpperCase()}
|
${imgURL
|
||||||
|
? html`<img
|
||||||
|
src="${imgURL}"
|
||||||
|
alt="${a.name}"
|
||||||
|
@error=${this.handleSimilarImageError}
|
||||||
|
/>`
|
||||||
|
: a.name.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="similar-name"
|
class="similar-name"
|
||||||
|
|||||||
Reference in New Issue
Block a user