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

Add genre chart and normalize track types

Add API and hook for fetching genre scrobbles charts and wire it into
ScrobblesAreaChart. Rename Track fields from snake_case to camelCase to
match app conventions and update mapping in useLibrary. Remove an
unnecessary any cast.

+34 -11
+10
apps/web/src/api/charts.ts
··· 44 } 45 return response.data; 46 };
··· 44 } 45 return response.data; 46 }; 47 + 48 + export const getGenreChart = async (genre: string) => { 49 + const response = await axios.get( 50 + `${API_URL}/xrpc/app.rocksky.charts.getScrobblesChart?genre=${genre}`, 51 + ); 52 + if (response.status !== 200) { 53 + return []; 54 + } 55 + return response.data; 56 + };
+7 -2
apps/web/src/components/ScrobblesAreaChart/ScrobblesAreaChart.tsx
··· 4 import numeral from "numeral"; 5 import { useEffect, useState } from "react"; 6 import { Area, AreaChart, Tooltip, TooltipProps, XAxis } from "recharts"; 7 - import useChart from "../../hooks/useChart"; 8 9 const CustomTooltip = ({ 10 active, ··· 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 () => {
··· 4 import numeral from "numeral"; 5 import { useEffect, useState } from "react"; 6 import { Area, AreaChart, Tooltip, TooltipProps, XAxis } from "recharts"; 7 + import useChart, { useGenreChartQuery } from "../../hooks/useChart"; 8 9 const CustomTooltip = ({ 10 active, ··· 42 location: { pathname }, 43 }, 44 } = useRouter(); 45 + const { did, rkey, id: genre } = useParams({ strict: false }); 46 const [data, setData] = useState< 47 { 48 date: string; 49 count: number; 50 }[] 51 >([]); 52 + const { data: genreCharts } = useGenreChartQuery(genre!); 53 + 54 + useEffect(() => { 55 + setData(genreCharts); 56 + }, [genreCharts]); 57 58 useEffect(() => { 59 const fetchScrobblesChart = async () => {
+8
apps/web/src/hooks/useChart.tsx
··· 4 import { 5 getAlbumChart, 6 getArtistChart, 7 getProfileChart, 8 getSongChart, 9 } from "../api/charts"; ··· 41 useQuery({ 42 queryKey: ["profileChart", did], 43 queryFn: () => getProfileChart(did), 44 select: (data) => data.scrobbles || [], 45 }); 46
··· 4 import { 5 getAlbumChart, 6 getArtistChart, 7 + getGenreChart, 8 getProfileChart, 9 getSongChart, 10 } from "../api/charts"; ··· 42 useQuery({ 43 queryKey: ["profileChart", did], 44 queryFn: () => getProfileChart(did), 45 + select: (data) => data.scrobbles || [], 46 + }); 47 + 48 + export const useGenreChartQuery = (genre: string) => 49 + useQuery({ 50 + queryKey: ["genreChart", genre], 51 + queryFn: () => getGenreChart(genre), 52 select: (data) => data.scrobbles || [], 53 }); 54
+1 -1
apps/web/src/hooks/useLibrary.tsx
··· 207 queryFn: async ({ pageParam = 0 }) => { 208 const data = await getTracksByGenre(genre, pageParam * limit, limit); 209 return { 210 - tracks: data?.tracks.map((x: any) => ({ 211 ...x, 212 scrobbles: x.playCount, 213 })),
··· 207 queryFn: async ({ pageParam = 0 }) => { 208 const data = await getTracksByGenre(genre, pageParam * limit, limit); 209 return { 210 + tracks: data?.tracks.map((x) => ({ 211 ...x, 212 scrobbles: x.playCount, 213 })),
+8 -8
apps/web/src/types/track.ts
··· 1 export type Track = { 2 id: string; 3 uri: string; 4 - unique_listeners: number; 5 - play_count: number; 6 title: string; 7 artist: string; 8 - artist_uri: string; 9 album: string; 10 - album_uri: string; 11 - album_art: string; 12 - album_artist: string; 13 - copyright_message: string; 14 - disc_number: number; 15 duration: number; 16 sha256: string; 17 track_number: number;
··· 1 export type Track = { 2 id: string; 3 uri: string; 4 + uniqueListeners: number; 5 + playCount: number; 6 title: string; 7 artist: string; 8 + artistUri: string; 9 album: string; 10 + albumUri: string; 11 + albumArt: string; 12 + albumArtist: string; 13 + copyrightMessage: string; 14 + discNumber: number; 15 duration: number; 16 sha256: string; 17 track_number: number;