From 84c25cafd4084460f42b5050aaf6bb03b0f671bb Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 23 Oct 2023 07:01:55 +0100 Subject: [PATCH] cleanup --- src/app/ranking/country/[country]/page.tsx | 179 +------------------- src/app/ranking/global/page.tsx | 170 +------------------ src/components/player/GlobalRanking.tsx | 184 +++++++++++++++++++++ src/middleware.ts | 9 + src/utils/utils.ts | 30 ++++ 5 files changed, 233 insertions(+), 339 deletions(-) create mode 100644 src/components/player/GlobalRanking.tsx diff --git a/src/app/ranking/country/[country]/page.tsx b/src/app/ranking/country/[country]/page.tsx index 5d4e271..10e84a8 100644 --- a/src/app/ranking/country/[country]/page.tsx +++ b/src/app/ranking/country/[country]/page.tsx @@ -1,183 +1,14 @@ -"use client"; - -import Card from "@/components/Card"; -import Container from "@/components/Container"; -import Error from "@/components/Error"; -import Pagination from "@/components/Pagination"; -import { Spinner } from "@/components/Spinner"; -import PlayerRanking from "@/components/player/PlayerRanking"; -import PlayerRankingMobile from "@/components/player/PlayerRankingMobile"; -import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; -import { ScoreSaberAPI } from "@/utils/scoresaber/api"; -import { normalizedRegionName } from "@/utils/utils"; -import Link from "next/link"; -import { useRouter, useSearchParams } from "next/navigation"; -import { useCallback, useEffect, useState } from "react"; -import ReactCountryFlag from "react-country-flag"; - -type PageInfo = { - loading: boolean; - page: number; - totalPages: number; - players: ScoresaberPlayer[]; -}; +import GlobalRanking from "@/components/player/GlobalRanking"; +import { getPageFromSearchQuery } from "@/utils/utils"; +import { headers } from "next/headers"; type RankingCountryProps = { params: { country: string }; }; export default function RankingCountry({ params }: RankingCountryProps) { - const searchParams = useSearchParams(); - const router = useRouter(); - + const page = getPageFromSearchQuery(headers()); const country = params.country; - let page; - const pageString = searchParams.get("page"); - if (pageString == null) { - page = 1; - } else { - page = Number.parseInt(pageString) || 1; - } - - const [error, setError] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); - - const [pageInfo, setPageInfo] = useState({ - loading: true, - page: page, - totalPages: 1, - players: [], - }); - - const updatePage = useCallback( - (page: any) => { - console.log("Switching page to", page); - ScoreSaberAPI.fetchTopPlayers(page, country).then((response) => { - if (!response) { - setError(true); - setErrorMessage("No players found"); - setPageInfo({ ...pageInfo, loading: false }); - return; - } - setPageInfo({ - ...pageInfo, - players: response.players, - totalPages: response.pageInfo.totalPages, - loading: false, - page: page, - }); - if (page > 1) { - router.push(`/ranking/country/${country}?page=${page}`, { - scroll: false, - }); - } else { - router.push(`/ranking/country/${country}`, { - scroll: false, - }); - } - }); - }, - [country, pageInfo, router], - ); - - useEffect(() => { - if (!pageInfo.loading || error) return; - - updatePage(pageInfo.page); - }, [error, country, updatePage, pageInfo.page, pageInfo.loading]); - - if (pageInfo.loading || error) { - return ( -
- - -
-
-
- {error && } - {!error && } -
-
-
-
-
-
- ); - } - - const players = pageInfo.players; - - return ( -
- - - {pageInfo.loading ? ( -
- -
- ) : ( -
-
- -

- You are viewing scores from {normalizedRegionName(country)} -

-
- - - - - - - - - - - - - - {players.map((player) => ( - - - - ))} - -
RankProfilePerformance PointsTotal PlaysTotal Ranked PlaysAvg Ranked Accuracy
- -
- {players.map((player) => ( -
- - - -
- ))} -
- - {/* Pagination */} -
-
- { - updatePage(page); - }} - /> -
-
-
- )} -
-
-
- ); + return ; } diff --git a/src/app/ranking/global/page.tsx b/src/app/ranking/global/page.tsx index 2a6666e..05e4bf7 100644 --- a/src/app/ranking/global/page.tsx +++ b/src/app/ranking/global/page.tsx @@ -1,169 +1,9 @@ -"use client"; - -import Card from "@/components/Card"; -import Container from "@/components/Container"; -import Error from "@/components/Error"; -import Pagination from "@/components/Pagination"; -import { Spinner } from "@/components/Spinner"; -import PlayerRanking from "@/components/player/PlayerRanking"; -import PlayerRankingMobile from "@/components/player/PlayerRankingMobile"; -import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; -import { ScoreSaberAPI } from "@/utils/scoresaber/api"; -import { GlobeAsiaAustraliaIcon } from "@heroicons/react/20/solid"; -import Link from "next/link"; -import { useRouter, useSearchParams } from "next/navigation"; -import { useCallback, useEffect, useState } from "react"; - -type PageInfo = { - loading: boolean; - page: number; - totalPages: number; - players: ScoresaberPlayer[]; -}; +import GlobalRanking from "@/components/player/GlobalRanking"; +import { getPageFromSearchQuery } from "@/utils/utils"; +import { headers } from "next/headers"; export default function RankingGlobal() { - const searchParams = useSearchParams(); - const router = useRouter(); + const page = getPageFromSearchQuery(headers()); - let page; - const pageString = searchParams.get("page"); - if (pageString == null) { - page = 1; - } else { - page = Number.parseInt(pageString) || 1; - } - - const [error, setError] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); - - const [pageInfo, setPageInfo] = useState({ - loading: true, - page: page, - totalPages: 1, - players: [], - }); - - const updatePage = useCallback( - (page: any) => { - console.log("Switching page to", page); - ScoreSaberAPI.fetchTopPlayers(page).then((response) => { - if (!response) { - setError(true); - setErrorMessage("No players found"); - setPageInfo({ ...pageInfo, loading: false }); - return; - } - setPageInfo({ - ...pageInfo, - players: response.players, - totalPages: response.pageInfo.totalPages, - loading: false, - page: page, - }); - if (page > 1) { - router.push(`/ranking/global?page=${page}`, { - scroll: false, - }); - } else { - router.push(`/ranking/global`, { - scroll: false, - }); - } - }); - }, - [pageInfo, router], - ); - - useEffect(() => { - if (!pageInfo.loading || error) return; - - updatePage(pageInfo.page); - }, [error, updatePage, pageInfo.page, pageInfo.loading]); - - if (pageInfo.loading || error) { - return ( -
- - -
-
-
- {error && } - {!error && } -
-
-
-
-
-
- ); - } - - const players = pageInfo.players; - - return ( -
- - - {pageInfo.loading ? ( -
- -
- ) : ( -
-
- -

You are viewing Global scores

-
- - - - - - - - - - - - - {players.map((player) => ( - - - - ))} - -
RankProfilePerformance PointsTotal PlaysTotal Ranked PlaysAvg Ranked Accuracy
- -
- {players.map((player) => ( -
- - - -
- ))} -
- - {/* Pagination */} -
-
- { - updatePage(page); - }} - /> -
-
-
- )} -
-
-
- ); + return ; } diff --git a/src/components/player/GlobalRanking.tsx b/src/components/player/GlobalRanking.tsx new file mode 100644 index 0000000..d0ae7c6 --- /dev/null +++ b/src/components/player/GlobalRanking.tsx @@ -0,0 +1,184 @@ +"use client"; + +import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; +import { ScoreSaberAPI } from "@/utils/scoresaber/api"; +import { normalizedRegionName } from "@/utils/utils"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; +import ReactCountryFlag from "react-country-flag"; +import Card from "../Card"; +import Container from "../Container"; +import Error from "../Error"; +import Pagination from "../Pagination"; +import { Spinner } from "../Spinner"; +import PlayerRanking from "./PlayerRanking"; +import PlayerRankingMobile from "./PlayerRankingMobile"; + +type PageInfo = { + loading: boolean; + page: number; + totalPages: number; + players: ScoresaberPlayer[]; +}; + +type GlobalRankingProps = { + page: number; + country?: string; +}; + +export default function GlobalRanking({ page, country }: GlobalRankingProps) { + const router = useRouter(); + + const [error, setError] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); + + const [pageInfo, setPageInfo] = useState({ + loading: true, + page: page, + totalPages: 1, + players: [], + }); + + const updatePage = useCallback( + (page: any) => { + console.log("Switching page to", page); + ScoreSaberAPI.fetchTopPlayers(page, country).then((response) => { + if (!response) { + setError(true); + setErrorMessage("No players found"); + setPageInfo({ ...pageInfo, loading: false }); + return; + } + setPageInfo({ + ...pageInfo, + players: response.players, + totalPages: response.pageInfo.totalPages, + loading: false, + page: page, + }); + const urlBase = country + ? `/ranking/country/${country}` + : "/ranking/global"; + if (page > 1) { + router.push(`${urlBase}?page=${page}`, { + scroll: false, + }); + } else { + router.push(`${urlBase}`, { + scroll: false, + }); + } + }); + }, + [country, pageInfo, router], + ); + + useEffect(() => { + if (!pageInfo.loading || error) return; + + updatePage(pageInfo.page); + }, [error, country, updatePage, pageInfo.page, pageInfo.loading]); + + if (pageInfo.loading || error) { + return ( +
+ + +
+
+
+ {error && } + {!error && } +
+
+
+
+
+
+ ); + } + + const players = pageInfo.players; + + return ( +
+ + + {pageInfo.loading ? ( +
+ +
+ ) : ( +
+
+ {country && ( + + )} +

+ You are viewing{" "} + {country + ? "scores from " + normalizedRegionName(country) + : "Global scores"} +

+
+ + + + + + + + + + + + + + {players.map((player) => ( + + + + ))} + +
RankProfilePerformance PointsTotal PlaysTotal Ranked PlaysAvg Ranked Accuracy
+ +
+ {players.map((player) => ( +
+ + + +
+ ))} +
+ + {/* Pagination */} +
+
+ { + updatePage(page); + }} + /> +
+
+
+ )} +
+
+
+ ); +} diff --git a/src/middleware.ts b/src/middleware.ts index 859b6a0..f782074 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -6,6 +6,15 @@ export function middleware(request: NextRequest) { if (request.nextUrl.pathname == "/") { return NextResponse.redirect(new URL("/search", request.url)); } + + const requestHeaders = new Headers(request.headers); + requestHeaders.set("x-url", request.url); + return NextResponse.next({ + request: { + // New request headers + headers: requestHeaders, + }, + }); } export const config = { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 8865076..0016d04 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,3 +1,5 @@ +import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers"; + let regionNames = new Intl.DisplayNames(["en"], { type: "region" }); export function isProduction() { @@ -13,3 +15,31 @@ export function isProduction() { export function normalizedRegionName(region: string) { return regionNames.of(region); } + +/** + * Gets the page number from the search query + * + * @param query the query to get the page from + * @param defaultPage the default page to return if the page is not found + * @returns the page from the query + */ +export function getPageFromSearchQuery( + headers: ReadonlyHeaders, + defaultPage = 1, +) { + const url = new URL(headers.get("x-url")!); + const searchParams = url.searchParams; + + let page; + const pageString = searchParams.get("page"); + if (pageString == null) { + page = defaultPage; + } else { + page = Number.parseInt(pageString); + } + if (Number.isNaN(page)) { + page = defaultPage; + } + + return page; +}