forked from
chadtmiller.com/slices-teal-relay
Teal.fm frontend powered by slices.network
tealfm-slices.wisp.place
tealfm
slices
1import { graphql, useFragment } from "react-relay";
2import type { TrackItem_play$key } from "./__generated__/TrackItem_play.graphql";
3import AlbumArt from "./AlbumArt";
4import MusicBrainzLink from "./MusicBrainzLink";
5
6interface TrackItemProps {
7 play: TrackItem_play$key;
8}
9
10export default function TrackItem({ play }: TrackItemProps) {
11 const data = useFragment(
12 graphql`
13 fragment TrackItem_play on FmTealAlphaFeedPlay {
14 trackName
15 playedTime
16 artists {
17 artistName
18 artistMbId
19 }
20 releaseName
21 releaseMbId
22 actorHandle
23 musicServiceBaseDomain
24 appBskyActorProfile {
25 displayName
26 }
27 }
28 `,
29 play
30 );
31
32 return (
33 <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors">
34 <div className="flex items-center gap-4">
35 <div className="flex-shrink-0">
36 <AlbumArt releaseMbId={data.releaseMbId} alt={`${data.trackName} album art`} />
37 </div>
38
39 <div className="flex-1 min-w-0 grid grid-cols-2 gap-4">
40 <div className="min-w-0">
41 <h3 className="text-sm font-medium text-zinc-100 truncate flex items-center gap-2">
42 <span className="truncate">{data.trackName}</span>
43 {data.musicServiceBaseDomain === "nts.live" && (
44 <a
45 href={`https://${data.musicServiceBaseDomain}`}
46 target="_blank"
47 rel="noopener noreferrer"
48 className="text-[10px] px-1.5 py-0.5 bg-violet-500/20 text-violet-400 rounded flex-shrink-0 hover:bg-violet-500/30 transition-colors"
49 >
50 NTS
51 </a>
52 )}
53 </h3>
54 <p className="text-xs text-zinc-500 truncate">
55 {Array.isArray(data.artists)
56 ? data.artists.map((a) => a.artistName).join(", ")
57 : data.artists}
58 </p>
59 </div>
60
61 <div className="text-right min-w-0">
62 <p className="text-xs text-zinc-400 truncate">
63 <MusicBrainzLink releaseMbId={data.releaseMbId}>
64 {data.releaseName}
65 </MusicBrainzLink>
66 </p>
67 <div className="flex items-center justify-end gap-2 mt-0.5 min-w-0 overflow-hidden">
68 {data.playedTime && (
69 <p className="text-xs text-zinc-600 flex-shrink-0">
70 {new Date(data.playedTime).toLocaleTimeString("en-US", {
71 hour: "numeric",
72 minute: "2-digit",
73 })}
74 </p>
75 )}
76 <a
77 href={`/profile/${data.actorHandle}`}
78 className="text-xs text-violet-500 hover:text-violet-400 transition-colors truncate block max-w-[120px]"
79 >
80 @{data.actorHandle}
81 </a>
82 </div>
83 </div>
84 </div>
85 </div>
86 </div>
87 );
88}