forked from
chadtmiller.com/slices-teal-relay
Teal.fm frontend powered by slices.network
tealfm-slices.wisp.place
tealfm
slices
1import { graphql, useLazyLoadQuery } from "react-relay";
2import { useParams } from "react-router-dom";
3import type { ProfileTopArtistsQuery } from "./__generated__/ProfileTopArtistsQuery.graphql";
4import { useDateRangeFilter } from "./useDateRangeFilter";
5import ArtistItem from "./ArtistItem";
6import { useMemo } from "react";
7
8export default function ProfileTopArtists() {
9 const { handle, period } = useParams<{ handle: string; period?: string }>();
10 const dateRangeVariables = useDateRangeFilter(period);
11
12 const queryVariables = useMemo(() => {
13 return {
14 where: {
15 ...dateRangeVariables.where,
16 actorHandle: { eq: handle },
17 },
18 };
19 }, [handle, dateRangeVariables]);
20
21 const data = useLazyLoadQuery<ProfileTopArtistsQuery>(
22 graphql`
23 query ProfileTopArtistsQuery($where: FmTealAlphaFeedPlayWhereInput) {
24 fmTealAlphaFeedPlaysAggregated(
25 groupBy: [{ field: artists }]
26 orderBy: { count: desc }
27 limit: 50
28 where: $where
29 ) {
30 artists
31 count
32 }
33 }
34 `,
35 queryVariables,
36 { fetchKey: `${handle}-${period || "all"}`, fetchPolicy: "store-or-network" }
37 );
38
39 const processedArtists = useMemo(() => {
40 const artistCounts: { [key: string]: number } = {};
41
42 (data.fmTealAlphaFeedPlaysAggregated || []).forEach((row) => {
43 if (!row.artists) return;
44
45 let names: string[] = [];
46
47 try {
48 const parsed = typeof row.artists === 'string' ? JSON.parse(row.artists) : row.artists;
49
50 if (Array.isArray(parsed)) {
51 names = parsed.map((a: { artistName: string }) => a.artistName.trim());
52 } else if (typeof parsed === 'string') {
53 names = parsed.split(',').map(s => s.trim());
54 }
55 } catch (e) {
56 if (typeof row.artists === 'string') {
57 names = row.artists.split(',').map(s => s.trim());
58 }
59 }
60
61 names.forEach(name => {
62 if (name) {
63 artistCounts[name] = (artistCounts[name] || 0) + row.count;
64 }
65 });
66 });
67
68 return Object.entries(artistCounts)
69 .map(([name, count]) => ({ artists: name, count }))
70 .sort((a, b) => b.count - a.count)
71 .slice(0, 10);
72 }, [data.fmTealAlphaFeedPlaysAggregated]);
73
74 const maxCount = processedArtists.length > 0 ? processedArtists[0].count : 0;
75
76 return (
77 <div className="space-y-1">
78 {processedArtists.map((artist, index) => (
79 <ArtistItem
80 key={`${artist.artists}-${index}`}
81 artists={artist.artists || "Unknown Artist"}
82 count={artist.count}
83 rank={index + 1}
84 maxCount={maxCount}
85 />
86 ))}
87 </div>
88 );
89}