A decentralized music tracking and discovery platform built on AT Protocol 馃幍
rocksky.app
spotify
atproto
lastfm
musicbrainz
scrobbling
listenbrainz
1import { useParams, useRouter } from "@tanstack/react-router";
2import { LabelMedium } from "baseui/typography";
3import dayjs from "dayjs";
4import numeral from "numeral";
5import { useEffect, useState } from "react";
6import { Area, AreaChart, Tooltip, TooltipProps, XAxis } from "recharts";
7import useChart from "../../hooks/useChart";
8
9const CustomTooltip = ({
10 active,
11 payload,
12 label,
13}: TooltipProps<number, string>) => {
14 if (active && payload && payload.length) {
15 return (
16 <div className="bg-[#fff] border-[1px] border-[#ccc] p-[5px]">
17 <span className="text-[#808080]">
18 {dayjs(label).format("dddd DD MMMM YYYY")}:
19 </span>
20 <span className="text-[#710de4]">
21 {" "}
22 {numeral(payload[0].value).format("0,0")}
23 </span>
24 </div>
25 );
26 }
27
28 return null;
29};
30const formatXAxis = (tickItem: string) => dayjs(tickItem).format("MMM D");
31
32function ScrobblesAreaChart() {
33 const {
34 getScrobblesChart,
35 getAlbumChart,
36 getArtistChart,
37 getSongChart,
38 getProfileChart,
39 } = useChart();
40 const {
41 state: {
42 location: { pathname },
43 },
44 } = useRouter();
45 const { did, rkey } = useParams({ strict: false });
46 const [data, setData] = useState<
47 {
48 date: string;
49 count: number;
50 }[]
51 >([]);
52
53 useEffect(() => {
54 const fetchScrobblesChart = async () => {
55 if (pathname === "/") {
56 return;
57 }
58
59 if (pathname.startsWith("/profile")) {
60 const charts = await getProfileChart(did!);
61 setData(charts);
62 return;
63 }
64
65 if (pathname.includes("/artist/")) {
66 const charts = await getArtistChart(
67 `at://${did}/app.rocksky.artist/${rkey}`,
68 );
69 setData(charts);
70 return;
71 }
72
73 if (pathname.includes("/album/")) {
74 const charts = await getAlbumChart(
75 `at://${did}/app.rocksky.album/${rkey}`,
76 );
77 setData(charts);
78 return;
79 }
80
81 if (pathname.includes("/song/")) {
82 const charts = await getSongChart(
83 `at://${did}/app.rocksky.song/${rkey}`,
84 );
85 setData(charts);
86 return;
87 }
88
89 if (pathname.includes("/scrobble/")) {
90 const charts = await getSongChart(
91 `at://${did}/app.rocksky.scrobble/${rkey}`,
92 );
93 setData(charts);
94 return;
95 }
96 };
97 fetchScrobblesChart();
98 // eslint-disable-next-line react-hooks/exhaustive-deps
99 }, [pathname]);
100
101 return (
102 <>
103 {!pathname.includes("/playlist/") && (
104 <>
105 <LabelMedium
106 marginBottom={"20px"}
107 className="!text-[var(--color-text)]"
108 >
109 Scrobble Stats
110 </LabelMedium>
111 <AreaChart
112 width={300}
113 height={120}
114 data={
115 pathname === "/" ||
116 pathname.startsWith("/dropbox") ||
117 pathname.startsWith("/googledrive")
118 ? getScrobblesChart()
119 : data
120 }
121 className="top-[5px] right-[0px] left-[0px] bottom-[5px]"
122 >
123 <XAxis
124 dataKey="date"
125 axisLine={{ stroke: "#ccc", strokeWidth: 1 }}
126 tick={{ fontSize: 10, color: "var(--color-text-muted)" }}
127 tickFormatter={formatXAxis}
128 />
129 <Tooltip
130 content={<CustomTooltip />}
131 labelFormatter={(label) => dayjs(label).format("YYYY-MM-DD")}
132 />
133 <Area
134 type="monotone"
135 dataKey="count"
136 stroke="#710de4"
137 fill="#9754e463"
138 />
139 </AreaChart>
140 </>
141 )}
142 </>
143 );
144}
145
146export default ScrobblesAreaChart;