) {
if (changed.has('results')) {
this.loadCardData();
}
}
private async loadCardData() {
for (const r of this.results) {
if (!r.mbid || this.images.has(r.mbid)) continue;
if (r.entityType === 'artist') {
// Load artist image.
GetArtistImageURL(r.mbid)
.then((url) => {
if (url) {
this.images.set(r.mbid, url);
this.requestUpdate();
}
})
.catch(() => {});
// Load preview tracks removed — cards are cleaner without them.
} else if (r.entityType === 'release_group') {
// Load album art.
GetThumbnail(r.mbid, r.name, r.artistCredit || '')
.then((url) => {
if (url) {
this.images.set(r.mbid, url);
this.requestUpdate();
}
})
.catch(() => {});
}
}
}
private handleClick(r: explore.TopResult) {
// Record the click for learning.
RecordSearchClick(this.query, r.mbid, r.entityType).catch(() => {});
// Navigate to the appropriate explore page.
this.dispatchEvent(
new CustomEvent('top-result-click', {
detail: r,
bubbles: true,
composed: true,
}),
);
}
override render() {
if (!this.results?.length) return nothing;
return html`
Top Results
${this.results.map((r) => this.renderCard(r))}
`;
}
private renderCard(r: explore.TopResult) {
const imgUrl = this.images.get(r.mbid);
const isArtist = r.entityType === 'artist';
// The artist portion of the subtitle links to the artist page;
// the remaining metadata (type/country, year, duration) is plain
// text. Artist cards have no artist credit — their whole subtitle
// is metadata.
const artistPart = isArtist ? '' : r.artistCredit || '';
const metaPart = isArtist
? [r.artistType, r.country].filter(Boolean).join(' · ')
: r.entityType === 'release_group'
? r.year || ''
: formatDuration(r.length) || '';
const entityType: OwnableKind =
r.entityType === 'artist'
? 'artist'
: r.entityType === 'release_group'
? 'album'
: 'track';
// Ownership is the local row, not the catalog's flag — see
// `utils/ownership.ts`. An album additionally says *how much*
// of it is here, which is the one thing a tick cannot.
const owned = isOwned(r);
const badge =
entityType === 'album'
? albumBadgeFor(r, r.mbid)
: {
status: libraryStatusFor(owned, r.mbid) as LibraryStatus,
owned: 0,
expected: 0,
};
// A card navigates whether or not the entity is owned, so it is
// not `aria-disabled` the way an unplayable track row is — the
// name is what carries the state to anyone not seeing the
// dimming.
return html`
this.handleClick(r)}
@keydown=${(e: KeyboardEvent) => {
if (e.key !== 'Enter' && e.key !== ' ') return;
e.preventDefault();
this.handleClick(r);
}}
>
${badgeLabel(r.entityType)}
${isArtist || badge.status === 'in-library'
? nothing
: html``}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'top-results-row': TopResultsRow;
}
}