"use client"; import Avatar from "@/components/Avatar"; import Card from "@/components/Card"; import Container from "@/components/Container"; import Label from "@/components/Label"; import Pagination from "@/components/Pagination"; import PlayerChart from "@/components/PlayerChart"; import Score from "@/components/Score"; import { Spinner } from "@/components/Spinner"; import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; import { ScoresaberPlayerScore } from "@/schemas/scoresaber/playerScore"; import { useSettingsStore } from "@/store/settingsStore"; import { formatNumber } from "@/utils/number"; import { fetchScores, getPlayerInfo } from "@/utils/scoresaber/api"; import useStore from "@/utils/useStore"; import { ClockIcon, GlobeAsiaAustraliaIcon, HomeIcon, TrophyIcon, } from "@heroicons/react/20/solid"; import Image from "next/image"; import { useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useState } from "react"; import ReactCountryFlag from "react-country-flag"; import { toast } from "react-toastify"; type PageInfo = { loading: boolean; page: number; totalPages: number; sortType: SortType; scores: ScoresaberPlayerScore[]; }; type PlayerInfo = { loading: boolean; player: ScoresaberPlayer | undefined; }; type SortType = { name: string; value: string; icon: JSX.Element; }; const sortTypes: { [key: string]: SortType } = { top: { name: "Top Scores", value: "top", icon: , }, recent: { name: "Recent Scores", value: "recent", icon: , }, }; const DEFAULT_SORT_TYPE = sortTypes.top; export default function Player({ params }: { params: { id: string } }) { const settingsStore = useStore(useSettingsStore, (state) => { return { userId: state.userId, setUserId: state.setUserId, refreshProfile: state.refreshProfile, }; }); const searchParams = useSearchParams(); const router = useRouter(); let page; const pageString = searchParams.get("page"); if (pageString == null) { page = 1; } else { page = Number.parseInt(pageString) || 1; } let sortType; const sortTypeString = searchParams.get("sort"); if (sortTypeString == null) { // todo: check settings to get last used sort type sortType = DEFAULT_SORT_TYPE; } else { sortType = sortTypes[sortTypeString] || DEFAULT_SORT_TYPE; } const [error, setError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const [player, setPlayer] = useState({ loading: true, player: undefined, }); const [scores, setScores] = useState({ loading: true, page: page, totalPages: 1, sortType: sortType, scores: [], }); const updateScoresPage = useCallback( (sortType: SortType, page: any) => { console.log("Switching page to", page); fetchScores(params.id, page, sortType.value, 10).then( (scoresResponse) => { if (!scoresResponse) { setError(true); setErrorMessage("No Scores"); setScores({ ...scores, loading: false }); return; } setScores({ ...scores, scores: scoresResponse.scores, totalPages: scoresResponse.pageInfo.totalPages, loading: false, page: page, sortType: sortType, }); if (page > 1) { router.push( `/player/${params.id}?page=${page}&sort=${sortType.value}`, { scroll: false, }, ); } else { router.push(`/player/${params.id}?sort=${sortType.value}`, { scroll: false, }); } }, ); }, [params.id, router, scores], ); function claimProfile() { settingsStore?.setUserId(params.id); settingsStore?.refreshProfile(); toast.success("Successfully claimed profile"); } useEffect(() => { if (!params.id) { setError(true); setPlayer({ ...player, loading: false }); return; } if (error || !player.loading) { return; } getPlayerInfo(params.id).then((playerResponse) => { if (!playerResponse) { setError(true); setErrorMessage("Failed to fetch player"); setPlayer({ ...player, loading: false }); return; } setPlayer({ ...player, player: playerResponse, loading: false }); updateScoresPage(scores.sortType, 1); }); }, [error, params.id, player, scores, updateScoresPage]); if (player.loading || error || !player.player) { return (
{player.loading && } {error && (

{errorMessage}

Sad cat
)}
); } const playerData = player.player; return (
{/* Player Info */}
{/* Avatar */}
{/* Settings Buttons */}
{settingsStore?.userId !== params.id && ( )}
{/* Name */}

{playerData.name}

{/* Global Rank */}

#{playerData.rank}

{/* Country Rank */}

#{playerData.countryRank}

{/* PP */}

{formatNumber(playerData.pp)}pp

{/* Labels */}
{/* Scores */} {/* Sort */}
{Object.values(sortTypes).map((sortType) => { return ( ); })}
{scores.loading ? (
) : (
{!scores.loading && scores.scores.length == 0 ? (

{errorMessage}

) : ( scores.scores.map((scoreData, id) => { const { score, leaderboard } = scoreData; return ( ); }) )}
)}
{/* Pagination */}
{ updateScoresPage(scores.sortType, page); }} />
); }