From 3a4bc7a83a9a21dbbce3ab7ccaeb0a360215bc01 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 1 Oct 2024 19:14:33 +0100 Subject: [PATCH] add leaderboard page --- .../(pages)/leaderboard/[...slug]/page.tsx | 108 +++++++++++++ src/app/(pages)/player/[...slug]/page.tsx | 2 +- src/app/(pages)/search/page.tsx | 2 +- src/app/layout.tsx | 4 +- src/common/image-utils.ts | 2 +- src/common/service/impl/scoresaber.ts | 31 +++- .../context/leaderboard-context.tsx | 6 + .../leaderboard/leaderboard-data.tsx | 80 ++++++++++ .../leaderboard/leaderboard-info.tsx | 65 ++++++++ .../leaderboard/leaderboard-score.tsx | 2 +- .../leaderboard/leaderboard-scores.tsx | 150 +++++++++++++----- .../leaderboard-song-star-count.tsx | 32 ++++ src/components/score/score-buttons.tsx | 27 ++-- src/components/score/score-info.tsx | 14 +- src/components/score/score.tsx | 18 ++- 15 files changed, 473 insertions(+), 70 deletions(-) create mode 100644 src/app/(pages)/leaderboard/[...slug]/page.tsx create mode 100644 src/components/context/leaderboard-context.tsx create mode 100644 src/components/leaderboard/leaderboard-data.tsx create mode 100644 src/components/leaderboard/leaderboard-info.tsx create mode 100644 src/components/leaderboard/leaderboard-song-star-count.tsx diff --git a/src/app/(pages)/leaderboard/[...slug]/page.tsx b/src/app/(pages)/leaderboard/[...slug]/page.tsx new file mode 100644 index 0000000..81e8438 --- /dev/null +++ b/src/app/(pages)/leaderboard/[...slug]/page.tsx @@ -0,0 +1,108 @@ +import { scoresaberService } from "@/common/service/impl/scoresaber"; +import { Metadata, Viewport } from "next"; +import { redirect } from "next/navigation"; +import { Colors } from "@/common/colors"; +import { getAverageColor } from "@/common/image-utils"; +import { cache } from "react"; +import ScoreSaberLeaderboardScoresPageToken from "@/common/model/token/scoresaber/score-saber-leaderboard-scores-page-token"; +import { LeaderboardData } from "@/components/leaderboard/leaderboard-data"; + +const UNKNOWN_LEADERBOARD = { + title: "ScoreSaber Reloaded - Unknown Leaderboard", + description: "The leaderboard you were looking for could not be found.", +}; + +type Props = { + params: Promise<{ + slug: string[]; + }>; + searchParams: Promise<{ + [key: string]: string | undefined; + }>; +}; + +/** + * Gets the leaderboard data and scores + * + * @param params the params + * @param fetchScores whether to fetch the scores + * @returns the leaderboard data and scores + */ +const getLeaderboardData = cache(async ({ params }: Props, fetchScores: boolean = true) => { + const { slug } = await params; + const id = slug[0]; // The leaderboard id + const page = parseInt(slug[1]) || 1; // The page number + + const leaderboard = await scoresaberService.lookupLeaderboard(id); + let scores: ScoreSaberLeaderboardScoresPageToken | undefined; + if (fetchScores) { + scores = await scoresaberService.lookupLeaderboardScores(id + "", page); + } + + return { + page: page, + leaderboard: leaderboard, + scores: scores, + }; +}); + +export async function generateMetadata(props: Props): Promise { + const { leaderboard } = await getLeaderboardData(props, false); + if (leaderboard === undefined) { + return { + title: UNKNOWN_LEADERBOARD.title, + description: UNKNOWN_LEADERBOARD.description, + openGraph: { + title: UNKNOWN_LEADERBOARD.title, + description: UNKNOWN_LEADERBOARD.description, + }, + }; + } + + return { + title: `${leaderboard.songName}`, + openGraph: { + title: `ScoreSaber Reloaded - ${leaderboard.songName}`, + description: ` + + View the scores on ${leaderboard.songName}!`, + images: [ + { + url: leaderboard.coverImage, + }, + ], + }, + twitter: { + card: "summary", + }, + }; +} + +export async function generateViewport(props: Props): Promise { + const { leaderboard } = await getLeaderboardData(props, false); + if (leaderboard === undefined) { + return { + themeColor: Colors.primary, + }; + } + + const color = await getAverageColor(leaderboard.coverImage); + if (color === undefined) { + return { + themeColor: Colors.primary, + }; + } + + return { + themeColor: color?.hex, + }; +} + +export default async function LeaderboardPage(props: Props) { + const { leaderboard, scores, page } = await getLeaderboardData(props); + if (leaderboard == undefined) { + return redirect("/"); + } + + return ; +} diff --git a/src/app/(pages)/player/[...slug]/page.tsx b/src/app/(pages)/player/[...slug]/page.tsx index cd657cb..3781990 100644 --- a/src/app/(pages)/player/[...slug]/page.tsx +++ b/src/app/(pages)/player/[...slug]/page.tsx @@ -113,7 +113,7 @@ export async function generateViewport(props: Props): Promise { }; } -export default async function Search(props: Props) { +export default async function PlayerPage(props: Props) { const { player, scores, sort, page, search } = await getPlayerData(props); if (player == undefined) { return redirect("/"); diff --git a/src/app/(pages)/search/page.tsx b/src/app/(pages)/search/page.tsx index 8d3905e..3db8fe5 100644 --- a/src/app/(pages)/search/page.tsx +++ b/src/app/(pages)/search/page.tsx @@ -5,7 +5,7 @@ export const metadata: Metadata = { title: "Search", }; -export default function Search() { +export default function SearchPage() { return (
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 77d9ff6..fc0d1da 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -77,9 +77,9 @@ export default function RootLayout({ -
+
-
+
{children}