forked from
chadtmiller.com/slices-teal-relay
Teal.fm frontend powered by slices.network
tealfm-slices.wisp.place
tealfm
slices
1import { useAlbumArt } from "./useAlbumArt";
2
3interface Artist {
4 artistName: string;
5}
6
7interface TopTrackItemProps {
8 trackName: string;
9 releaseMbId: string | null | undefined;
10 artists: string | null | undefined;
11 count: number;
12 rank: number;
13 maxCount: number;
14}
15
16export default function TopTrackItem({
17 trackName,
18 releaseMbId,
19 artists,
20 count,
21 rank,
22 maxCount,
23}: TopTrackItemProps) {
24 const { albumArtUrl, isLoading } = useAlbumArt(releaseMbId);
25 const barWidth = maxCount > 0 ? (count / maxCount) * 100 : 0;
26
27 // Parse artists JSON
28 let artistNames = "Unknown Artist";
29 if (artists) {
30 try {
31 const parsed = typeof artists === 'string' ? JSON.parse(artists) : artists;
32 if (Array.isArray(parsed)) {
33 artistNames = parsed.map((a: Artist) => a.artistName).join(", ");
34 } else if (typeof parsed === 'string') {
35 artistNames = parsed;
36 }
37 } catch {
38 artistNames = String(artists);
39 }
40 }
41
42 return (
43 <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors relative overflow-hidden">
44 <div
45 className="absolute inset-y-0 left-0 bg-violet-500/10 transition-all"
46 style={{ width: `${barWidth}%` }}
47 />
48 <div className="flex items-center gap-4 relative">
49 <div className="text-xs text-zinc-600 w-8 text-right flex-shrink-0 font-medium">
50 {rank}
51 </div>
52
53 <div className="flex-shrink-0">
54 {isLoading ? (
55 <div className="w-10 h-10 bg-zinc-800 animate-pulse" />
56 ) : albumArtUrl ? (
57 <img
58 src={albumArtUrl}
59 alt={`${trackName} album art`}
60 className="w-10 h-10 object-cover"
61 loading="lazy"
62 />
63 ) : (
64 <div className="w-10 h-10 bg-zinc-800 flex items-center justify-center">
65 <svg
66 className="w-5 h-5 text-zinc-600"
67 fill="currentColor"
68 viewBox="0 0 20 20"
69 >
70 <path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
71 </svg>
72 </div>
73 )}
74 </div>
75
76 <div className="flex-1 min-w-0">
77 <h3 className="text-sm font-medium text-zinc-100 truncate">
78 {trackName}
79 </h3>
80 <p className="text-xs text-zinc-500 truncate">{artistNames}</p>
81 </div>
82
83 <div className="text-right flex-shrink-0">
84 <p className="text-xs text-zinc-400 font-medium">
85 {count.toLocaleString()}
86 </p>
87 </div>
88 </div>
89 </div>
90 );
91}