"use client"; import { capitalizeFirstLetter } from "@/common/string-utils"; import useWindowDimensions from "@/hooks/use-window-dimensions"; import { ClockIcon, TrophyIcon } from "@heroicons/react/24/solid"; import { useQuery } from "@tanstack/react-query"; import { motion, useAnimation, Variants } from "framer-motion"; import { useCallback, useEffect, useState } from "react"; import Card from "../card"; import Pagination from "../input/pagination"; import { Button } from "../ui/button"; import { leaderboards } from "@/common/leaderboards"; import { ScoreSort } from "@/common/service/score-sort"; import ScoreSaberPlayer from "@/common/service/types/scoresaber/scoresaber-player"; import ScoreSaberPlayerScoresPage from "@/common/service/types/scoresaber/scoresaber-player-scores-page"; import Score from "@/components/score/score"; type Props = { initialScoreData?: ScoreSaberPlayerScoresPage; player: ScoreSaberPlayer; sort: ScoreSort; page: number; }; type PageState = { /** * The current page */ page: number; /** * The current sort */ sort: ScoreSort; }; const scoreSort = [ { name: "Top", value: ScoreSort.top, icon: , }, { name: "Recent", value: ScoreSort.recent, icon: , }, ]; const scoreAnimation: Variants = { hiddenRight: { opacity: 0, x: 50, transition: { delay: 0, }, }, hiddenLeft: { opacity: 0, x: -50, transition: { delay: 0, }, }, visible: { opacity: 1, x: 0, transition: { staggerChildren: 0.03, }, }, }; export default function PlayerScores({ initialScoreData, player, sort, page, }: Props) { const leaderboard = leaderboards.ScoreSaber; const { width } = useWindowDimensions(); const controls = useAnimation(); const [firstLoad, setFirstLoad] = useState(true); const [pageState, setPageState] = useState({ page: page, sort: sort, }); const [previousPage, setPreviousPage] = useState(page); const [currentScores, setCurrentScores] = useState< ScoreSaberPlayerScoresPage | undefined >(initialScoreData); const { data: scores, isError, isLoading, refetch, } = useQuery({ queryKey: ["playerScores", player.id, leaderboard, pageState], queryFn: () => leaderboard.queries.lookupScores(player, pageState.sort, pageState.page), staleTime: 30 * 1000, // Cache data for 30 seconds }); const handleScoreLoad = useCallback(async () => { setFirstLoad(false); if (!firstLoad) { await controls.start( previousPage >= pageState.page ? "hiddenRight" : "hiddenLeft", ); } setCurrentScores(scores); await controls.start("visible"); }, [scores, controls, previousPage, firstLoad, pageState.page]); const handleSortChange = (newSort: ScoreSort) => { if (newSort !== pageState.sort) { setPageState({ page: 1, sort: newSort }); } }; useEffect(() => { if (scores) { handleScoreLoad(); } }, [scores, isError, handleScoreLoad]); useEffect(() => { const newUrl = `/player/${player.id}/${pageState.sort}/${pageState.page}`; window.history.replaceState( { ...window.history.state, as: newUrl, url: newUrl }, "", newUrl, ); refetch(); }, [pageState, refetch, player.id]); return (
{Object.values(scoreSort).map((sortOption, index) => ( ))}
{/* todo: add search */} {/**/}
{currentScores && ( <>
{isError &&

Oopsies! Something went wrong.

} {currentScores.playerScores.length === 0 && (

No scores found. Invalid Page?

)}
{currentScores.playerScores.map((playerScore, index) => ( ))} { setPreviousPage(pageState.page); setPageState({ page, sort: pageState.sort }); }} /> )}
); }