A decentralized music tracking and discovery platform built on AT Protocol 馃幍 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
at feat/pgpull 146 lines 3.7 kB view raw
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;