Demo using Slices Network GraphQL Relay API to make a teal.fm client
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 }
19 releaseName
20 releaseMbId
21 actorHandle
22 musicServiceBaseDomain
23 appBskyActorProfileByDid {
24 displayName
25 }
26 }
27 `,
28 play,
29 );
30
31 return (
32 <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors">
33 <div className="flex items-center gap-4">
34 <div className="flex-shrink-0">
35 <AlbumArt
36 releaseMbId={data.releaseMbId}
37 alt={`${data.trackName} album art`}
38 />
39 </div>
40
41 <div className="flex-1 min-w-0 grid grid-cols-2 gap-4">
42 <div className="min-w-0">
43 <h3 className="text-sm font-medium text-zinc-100 truncate flex items-center gap-2">
44 <span className="truncate">{data.trackName}</span>
45 {data.musicServiceBaseDomain === "nts.live" && (
46 <a
47 href={`https://${data.musicServiceBaseDomain}`}
48 target="_blank"
49 rel="noopener noreferrer"
50 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"
51 >
52 NTS
53 </a>
54 )}
55 </h3>
56 <p className="text-xs text-zinc-500 truncate">
57 {Array.isArray(data.artists)
58 ? data.artists.map((a) => a.artistName).join(", ")
59 : "Unknown Artist"}
60 </p>
61 </div>
62
63 <div className="text-right min-w-0">
64 <p className="text-xs text-zinc-400 truncate">
65 <MusicBrainzLink releaseMbId={data.releaseMbId}>
66 {data.releaseName}
67 </MusicBrainzLink>
68 </p>
69 <div className="flex items-center justify-end gap-2 mt-0.5 min-w-0 overflow-hidden">
70 {data.playedTime && (
71 <p className="text-xs text-zinc-600 flex-shrink-0">
72 {new Date(data.playedTime).toLocaleTimeString("en-US", {
73 hour: "numeric",
74 minute: "2-digit",
75 })}
76 </p>
77 )}
78 <a
79 href={`/profile/${data.actorHandle}`}
80 className="text-xs text-violet-500 hover:text-violet-400 transition-colors truncate block max-w-[120px]"
81 >
82 @{data.actorHandle}
83 </a>
84 </div>
85 </div>
86 </div>
87 </div>
88 </div>
89 );
90}