A decentralized music tracking and discovery platform built on AT Protocol 🎵

Update weekly charts API and UI

Switch top artists/tracks requests to charts XRPC endpoints
(/xrpc/app.rocksky.charts.getTopArtists and getTopTracks).
Display 7-day date range using dayjs. Add listeners column
and format listeners/scrobbles with numeral. Tweak table head/
body styles and vertical alignment.

+43 -11
+2 -2
apps/web/src/api/library.ts
··· 226 226 endDate?: Date, 227 227 ) => { 228 228 const response = await client.get<{ artists: Artist[] }>( 229 - "/xrpc/app.rocksky.artist.getArtists", 229 + "/xrpc/app.rocksky.charts.getTopArtists", 230 230 { 231 231 params: { 232 232 limit, ··· 246 246 endDate?: Date, 247 247 ) => { 248 248 const response = await client.get<{ tracks: Track[] }>( 249 - "/xrpc/app.rocksky.song.getSongs", 249 + "/xrpc/app.rocksky.charts.getTopTracks", 250 250 { 251 251 params: { 252 252 limit,
+41 -9
apps/web/src/pages/charts/weekly/Weekly.tsx
··· 3 3 import { Link } from "@tanstack/react-router"; 4 4 import Artist from "../../../components/Icons/Artist"; 5 5 import { getLastDays } from "../../../lib/date"; 6 + import dayjs from "dayjs"; 7 + import numeral from "numeral"; 6 8 7 9 type ArtistRow = { 8 10 id: string; ··· 10 12 picture: string; 11 13 uri: string; 12 14 scrobbles: number; 15 + uniqueListeners: number; 13 16 index: number; 14 17 }; 15 18 16 19 function Weekly() { 17 20 const { data: artists } = useTopArtistsQuery(0, 20, ...getLastDays(7)); 21 + const end = dayjs(); 22 + const start = end.subtract(7, "day"); 23 + const range = `${start.format("DD MMM YYYY")} — ${end.format("DD MMM YYYY")}`; 24 + 18 25 return ( 19 26 <> 27 + <div className="mt-[15px] mb-[25px]"> 28 + <strong>{range}</strong> 29 + </div> 20 30 <TableBuilder 21 31 data={artists?.map((x, index) => ({ 22 - id: x.id, 23 - name: x.name, 24 - picture: x.picture, 25 - uri: x.uri, 26 - scrobbles: x.scrobbles, 32 + ...x, 27 33 index, 28 34 }))} 29 35 divider="clean" 30 36 overrides={{ 37 + TableBodyCell: { 38 + style: { 39 + verticalAlign: "middle", 40 + }, 41 + }, 42 + TableHead: { 43 + style: { 44 + backgroundColor: "var(--color-background) !important", 45 + }, 46 + }, 31 47 TableHeadRow: { 32 48 style: { 33 - display: "none", 49 + backgroundColor: "var(--color-background) !important", 34 50 }, 35 51 }, 36 - TableBodyCell: { 52 + TableHeadCell: { 37 53 style: { 38 - verticalAlign: "middle", 54 + backgroundColor: "var(--color-background) !important", 55 + color: "var(--color-text) !important", 56 + opacity: "90%", 39 57 }, 40 58 }, 41 59 TableBodyRow: { ··· 58 76 }, 59 77 }} 60 78 > 61 - <TableBuilderColumn header="Name"> 79 + <TableBuilderColumn header="Artist"> 62 80 {(row: ArtistRow) => ( 63 81 <div className="flex flex-row items-center"> 64 82 <div> ··· 101 119 {row.name} 102 120 </Link> 103 121 </div> 122 + </div> 123 + )} 124 + </TableBuilderColumn> 125 + <TableBuilderColumn header="Listeners"> 126 + {(row: ArtistRow) => ( 127 + <div className="flex flex-row items-center"> 128 + {numeral(row.uniqueListeners).format("0.0")} 129 + </div> 130 + )} 131 + </TableBuilderColumn> 132 + <TableBuilderColumn header="Scrobbles"> 133 + {(row: ArtistRow) => ( 134 + <div className="flex flex-row items-center"> 135 + {numeral(row.scrobbles).format("0,0")} 104 136 </div> 105 137 )} 106 138 </TableBuilderColumn>