import { Link } from '@tanstack/react-router'; import type { TrackArtist } from '../types'; interface ArtistLinksProps { /** Ordered artists (first = main, rest = featured). */ artists?: TrackArtist[] | null; /** Shown when there are no structured artists (plain text, not a link). */ fallback?: string; className?: string; /** Stop row/card click handlers from firing when an artist link is clicked. */ stopPropagation?: boolean; } /** * Deduplicates artists by ID, preferring `main` over `featured` when the * same artist has both roles (can happen because track_artists has a composite * PK of track_id + artist_id + role). */ function deduplicateArtists(artists: TrackArtist[]): TrackArtist[] { const map = new Map(); for (const a of artists) { const existing = map.get(a.id); if (!existing || (existing.role === 'featured' && a.role === 'main')) { map.set(a.id, a); } } // Preserve original order, skipping duplicates. const seen = new Set(); return artists.filter((a) => { if (seen.has(a.id)) return false; seen.add(a.id); return true; }); } /** * Renders a track/album's artists as clickable links — main artist(s) then * "feat." guests. Single source of truth used by TrackRow, the playback bar, * the now-playing panel and album pages so artist navigation looks and behaves * the same everywhere. * * Artists are deduplicated by id — if the same artist appears as both main * and featured, only the main entry is shown. */ export function ArtistLinks({ artists, fallback, className = '', stopPropagation }: ArtistLinksProps) { if (!artists || artists.length === 0) { return {fallback || 'Unknown artist'}; } const unique = deduplicateArtists(artists); return ( {unique.map((a, i) => ( {i > 0 && {a.role === 'featured' && unique[i - 1].role !== 'featured' ? ' feat. ' : ', '}} e.stopPropagation() : undefined} className={`hover:text-text hover:underline ${a.role === 'featured' ? 'opacity-75' : ''}`} > {a.name} ))} ); }