From e3c39b5d4429f2d37c6e8c0347dc9c2fb050fc76 Mon Sep 17 00:00:00 2001 From: Josh Taylor Date: Sat, 14 Sep 2024 15:11:38 -0600 Subject: [PATCH] use leaderboard by id route --- src/components/Leaderboard.tsx | 27 ++++++++------- src/components/LeaderboardRow.tsx | 9 +++-- src/components/Sidebar.tsx | 5 ++- src/hooks/UseLeaderboard.tsx | 55 +++++++++++++++++++++++++++++-- src/hooks/UseProblem.tsx | 14 -------- src/hooks/UseWeek.tsx | 30 ----------------- src/score/score.ts | 8 ++--- 7 files changed, 80 insertions(+), 68 deletions(-) delete mode 100644 src/hooks/UseWeek.tsx diff --git a/src/components/Leaderboard.tsx b/src/components/Leaderboard.tsx index a30d2c5..10e3ad0 100644 --- a/src/components/Leaderboard.tsx +++ b/src/components/Leaderboard.tsx @@ -3,12 +3,14 @@ import useUser from "../hooks/UseProfile"; import "./Leaderboard.css"; import { useUsers } from "../hooks/UseUser"; import { LeaderboardRow } from "./LeaderboardRow"; -import { useThisWeek } from "../hooks/UseWeek"; -import { useSearchParams } from "react-router-dom"; -import { useAllStudyProblems, useProblems } from "../hooks/UseProblem"; +import { useProblems } from "../hooks/UseProblem"; import { useQueries } from "@tanstack/react-query"; import { getStats } from "../score/score"; -import { useLeaderboardIndex } from "../hooks/UseLeaderboard"; +import { + useCurrentLeaderboard, + useLeaderboard, + useLeaderboardIndex, +} from "../hooks/UseLeaderboard"; function formatCodeforcesId(input: string) { const match = input.match(/^(\d+)(\D.*)$/); @@ -22,8 +24,13 @@ function formatCodeforcesId(input: string) { export function Leaderboard() { const user = useUser(); const users = useUsers(); - const thisWeek = useThisWeek(); - const allProblemsLength = thisWeek.kattis.length + thisWeek.codeforces.length; + const leaderboard = useCurrentLeaderboard(); + + const { data } = useLeaderboard(leaderboard); + const thisWeek = data?.thisWeek; + const allStudyProblems = data?.allProblems; + const allProblemsLength = + (thisWeek?.kattis?.length ?? 0) + (thisWeek?.codeforces?.length ?? 0); const solvedProblems: { kattis: Map; codeforces: Map; @@ -39,11 +46,8 @@ export function Leaderboard() { solvedProblems.codeforces.set(problem, 1); } } - const links = thisWeek.links ?? {}; - const [params] = useSearchParams(); - const leaderboard = params.get("leaderboard") || "all"; + const links = thisWeek?.links ?? {}; const { data: allProblems } = useProblems(); - const { data: allStudyProblems } = useAllStudyProblems(); const { data: leaderboardIndex } = useLeaderboardIndex(); const leaderboardData = leaderboardIndex?.[leaderboard]; @@ -56,7 +60,6 @@ export function Leaderboard() { : undefined; }, enabled: !!allProblems && !!allStudyProblems && !!leaderboardData, - staleTime: 1000 * 60 * 5, })), combine: (results) => results.map((r) => r.data).filter((a) => !!a), }); @@ -70,7 +73,7 @@ export function Leaderboard() { } return (
- {thisWeek.topic && ( + {thisWeek?.topic && (

diff --git a/src/components/LeaderboardRow.tsx b/src/components/LeaderboardRow.tsx index 6d93a38..5e26000 100644 --- a/src/components/LeaderboardRow.tsx +++ b/src/components/LeaderboardRow.tsx @@ -3,9 +3,9 @@ import Flame from "../icons/Flame"; import FlameBorder from "../icons/FlameBorder"; import DeadFlame from "../icons/DeadFlame"; import React from "react"; -import { useThisWeek } from "../hooks/UseWeek"; import { UserStats } from "../score/score"; import ProgressBar from "./ProgressBar"; +import { useCurrentLeaderboard, useLeaderboard } from "../hooks/UseLeaderboard"; function WeeklyProblemBox({ solved, allProblemsLength, @@ -36,8 +36,11 @@ type LeaderboardRowProps = { export function LeaderboardRow({ userStats, rank, isMe }: LeaderboardRowProps) { const userId = userStats.user.id; - const thisWeek = useThisWeek(); - const allProblemsLength = thisWeek.kattis.length + thisWeek.codeforces.length; + const leaderboard = useCurrentLeaderboard(); + const { data } = useLeaderboard(leaderboard); + const thisWeek = data?.thisWeek; + const allProblemsLength = + (thisWeek?.kattis?.length ?? 0) + (thisWeek?.codeforces?.length ?? 0); const solvedKattis = new Set(Object.keys(userStats.user.kattis_submissions)); const solvedCodeforces = new Set( Object.keys(userStats.user.codeforces_submissions ?? {}) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index a6b0b66..c1dc811 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,11 +1,10 @@ import React from "react"; import UserBadge from "./UserBadge"; -import { useSearchParams } from "react-router-dom"; import Countdown from "./Countdown"; +import { useCurrentLeaderboard } from "../hooks/UseLeaderboard"; const Sidebar = ({ children }: React.PropsWithChildren) => { - const [params] = useSearchParams(); - const leaderboard = params.get("leaderboard") ?? "all"; + const leaderboard = useCurrentLeaderboard(); return (
diff --git a/src/hooks/UseLeaderboard.tsx b/src/hooks/UseLeaderboard.tsx index 7971406..acef92e 100644 --- a/src/hooks/UseLeaderboard.tsx +++ b/src/hooks/UseLeaderboard.tsx @@ -1,10 +1,12 @@ import { useQuery } from "@tanstack/react-query"; import axios from "axios"; import { BACKEND_URL } from "./base"; +import { Platform, platformValues } from "../types/platform"; +import { useSearchParams } from "react-router-dom"; -export type Leaderboard = { start: Date; end: Date }; +export type LeaderboardEntry = { start: Date; end: Date }; -export type LeaderboardIndex = Record; +export type LeaderboardIndex = Record; type LeaderboardResponse = Record< string, { @@ -37,3 +39,52 @@ export const useLeaderboardIndex = () => { : undefined; return { ...query, data: transformedData }; }; + +type PracticeWeek = string; + +export type Leaderboard = { + practice_set?: Record< + PracticeWeek, + { topic: string; links: Record } & Partial< + Record + > + >; + school?: string; +}; + +export type StudyProblems = Record>; + +const getLeaderboard = async (id: string): Promise => { + return (await axios.get(`${BACKEND_URL}/leaderboard/${id}`)).data; +}; + +export const useLeaderboard = (id: string) => { + const query = useQuery({ + queryKey: ["leaderboard", id], + queryFn: () => getLeaderboard(id), + staleTime: 1000 * 60 * 5, + }); + if (!query.data) return query; + const practiceSets = Object.entries(query.data.practice_set ?? {}) + .map(([key, value]) => ({ ...value, start: key })) + .sort((a, b) => a.start.localeCompare(b.start)); + const allProblems = practiceSets.reduce((acc, week) => { + platformValues.forEach((platform) => { + const problems = week[platform]; + problems?.forEach((problem) => { + acc[platform].add(problem); + }); + }); + return acc; + }, platformValues.reduce((x, platform) => ({ ...x, [platform]: new Set() }), {}) as StudyProblems); + const thisWeek = practiceSets.at(practiceSets.length - 1); + return { + ...query, + data: { allProblems, practiceSets, thisWeek }, + }; +}; + +export const useCurrentLeaderboard = () => { + const [params] = useSearchParams(); + return params.get("leaderboard") ?? "byu_summer_24"; +}; diff --git a/src/hooks/UseProblem.tsx b/src/hooks/UseProblem.tsx index 16637b2..9488397 100644 --- a/src/hooks/UseProblem.tsx +++ b/src/hooks/UseProblem.tsx @@ -5,8 +5,6 @@ import { Platform } from "../types/platform"; type Problem = { [key: string]: { rating: number; name: string } }; export type AllProblems = Record; -export type StudyProblems = Record; - async function getAllProblems(): Promise { return (await axios.get(`${BACKEND_URL}/get_all_problems`)).data; } @@ -18,15 +16,3 @@ export const useProblems = () => { staleTime: 1000 * 60 * 60 * 24, }); }; - -async function getAllStudyProblems(): Promise { - return (await axios.get(`${BACKEND_URL}/get_all_study_problems`)).data; -} - -export const useAllStudyProblems = () => { - return useQuery({ - queryFn: getAllStudyProblems, - queryKey: ["allStudyProblems"], - staleTime: 1000 * 60 * 60, - }); -}; diff --git a/src/hooks/UseWeek.tsx b/src/hooks/UseWeek.tsx deleted file mode 100644 index de77464..0000000 --- a/src/hooks/UseWeek.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import axios from "axios"; -import { BACKEND_URL } from "./base"; -import { useQuery } from "@tanstack/react-query"; - -type Week = { - codeforces?: string[]; - kattis?: string[]; - topic: string; - start: string; - links?: { [key: string]: string }; -}; - -async function getThisWeek(): Promise { - return (await axios.get(`${BACKEND_URL}/get_this_week`)).data; -} -export const useThisWeek = () => { - const weekQuery = useQuery({ - queryKey: ["this_week"], - queryFn: getThisWeek, - refetchOnWindowFocus: true, - staleTime: 1000 * 60 * 5, - }); - const data = weekQuery.data; - if (!data) return { codeforces: [], kattis: [], topic: "", start: "" }; - return { - ...data, - kattis: data.kattis ?? [], - codeforces: data.codeforces ?? [], - }; -}; diff --git a/src/score/score.ts b/src/score/score.ts index 9aba677..3d536a5 100644 --- a/src/score/score.ts +++ b/src/score/score.ts @@ -1,5 +1,5 @@ -import type { Leaderboard } from "../hooks/UseLeaderboard"; -import { AllProblems, StudyProblems } from "../hooks/UseProblem"; +import type { LeaderboardEntry, StudyProblems } from "../hooks/UseLeaderboard"; +import { AllProblems } from "../hooks/UseProblem"; import { User } from "../hooks/UseUser"; import { Platform, platformValues } from "../types/platform"; @@ -90,7 +90,7 @@ export function getStats( user: User, allProblems: AllProblems, studyProblems: StudyProblems, - leaderboard: Leaderboard + leaderboard: LeaderboardEntry ) { const kattisSubmissions: Record = Object.keys(user.kattis_submissions).reduce( @@ -141,7 +141,7 @@ export function getStats( const current_exp = exp.get(day) ?? DAILY_BONUS; const multiplier = submission.type === "contestant" || - studyProblems[platform].includes(problemId) + studyProblems[platform].has(problemId) ? 2 : 1; if (submission.type === "contestant") {