diff --git a/next.config.js b/next.config.js index d59a8a8..97fe8dd 100644 --- a/next.config.js +++ b/next.config.js @@ -33,37 +33,37 @@ module.exports = withBundleAnalyzer(nextConfig); // // Injected content via Sentry wizard below -const { withSentryConfig } = require("@sentry/nextjs"); +// const { withSentryConfig } = require("@sentry/nextjs"); -module.exports = withSentryConfig( - module.exports, - { - // For all available options, see: - // https://github.com/getsentry/sentry-webpack-plugin#options +// module.exports = withSentryConfig( +// module.exports, +// { +// // For all available options, see: +// // https://github.com/getsentry/sentry-webpack-plugin#options - // Suppresses source map uploading logs during build - silent: true, - org: "sentry", - project: "scoresaber-reloaded", - url: "https://sentry.fascinated.cc/", - }, - { - // For all available options, see: - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ +// // Suppresses source map uploading logs during build +// silent: true, +// org: "sentry", +// project: "scoresaber-reloaded", +// url: "https://sentry.fascinated.cc/", +// }, +// { +// // For all available options, see: +// // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ - // Upload a larger set of source maps for prettier stack traces (increases build time) - widenClientFileUpload: false, +// // Upload a larger set of source maps for prettier stack traces (increases build time) +// widenClientFileUpload: false, - // Transpiles SDK to be compatible with IE11 (increases bundle size) - transpileClientSDK: false, +// // Transpiles SDK to be compatible with IE11 (increases bundle size) +// transpileClientSDK: false, - // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load) - tunnelRoute: "/monitoring", +// // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load) +// tunnelRoute: "/monitoring", - // Hides source maps from generated client bundles - hideSourceMaps: true, +// // Hides source maps from generated client bundles +// hideSourceMaps: true, - // Automatically tree-shake Sentry logger statements to reduce bundle size - disableLogger: true, - }, -); +// // Automatically tree-shake Sentry logger statements to reduce bundle size +// disableLogger: true, +// }, +// ); diff --git a/package-lock.json b/package-lock.json index 8a79e07..cc9e9a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "react-dom": "^18", "react-toastify": "^9.1.3", "sharp": "^0.32.6", + "swr": "^2.2.4", "zustand": "^4.4.3" }, "devDependencies": { @@ -5417,6 +5418,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.4.tgz", + "integrity": "sha512-njiZ/4RiIhoOlAaLYDqwz5qH/KZXVilRLvomrx83HjzCWTfa+InyfAjv05PSFxnmLzZkNO9ZfvgoqzAaEI4sGQ==", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/tailwindcss": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", diff --git a/package.json b/package.json index 7d8bc96..97a5ac2 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "react-dom": "^18", "react-toastify": "^9.1.3", "sharp": "^0.32.6", + "swr": "^2.2.4", "zustand": "^4.4.3" }, "devDependencies": { diff --git a/src/app/player/[id]/page.tsx b/src/app/player/[id]/page.tsx index d7c0339..b83ed5d 100644 --- a/src/app/player/[id]/page.tsx +++ b/src/app/player/[id]/page.tsx @@ -1,4 +1,4 @@ -import PlayerPage from "@/components/player/PlayerPage"; +import PlayerPage from "@/components/player/Player"; import { ssrSettings } from "@/ssrSettings"; import { formatNumber } from "@/utils/number"; import { ScoreSaberAPI } from "@/utils/scoresaber/api"; diff --git a/src/app/ranking/country/[country]/[page]/page.tsx b/src/app/ranking/country/[country]/[page]/page.tsx new file mode 100644 index 0000000..d014377 --- /dev/null +++ b/src/app/ranking/country/[country]/[page]/page.tsx @@ -0,0 +1,44 @@ +import Card from "@/components/Card"; +import Container from "@/components/Container"; +import Error from "@/components/Error"; +import GlobalRanking from "@/components/player/GlobalRanking"; +import { ScoreSaberAPI } from "@/utils/scoresaber/api"; + +type RankingGlobalProps = { + params: { page: string; country: string }; +}; + +// Get data from API (server-sided) +async function getData(page: number, country: string) { + const pageData = await ScoreSaberAPI.fetchTopPlayers(page); + if (!pageData) { + return undefined; + } + return pageData; +} + +export default async function RankingGlobal({ + params: { page, country }, +}: RankingGlobalProps) { + const pageData = await getData(Number(page), country); + + return ( +
+ + + {pageData == undefined && ( + + )} + {pageData && ( + + )} + + +
+ ); +} diff --git a/src/app/ranking/country/[country]/page.tsx b/src/app/ranking/country/[country]/page.tsx deleted file mode 100644 index 10e84a8..0000000 --- a/src/app/ranking/country/[country]/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -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 page = getPageFromSearchQuery(headers()); - const country = params.country; - - return ; -} diff --git a/src/app/ranking/global/[page]/page.tsx b/src/app/ranking/global/[page]/page.tsx new file mode 100644 index 0000000..dcfa38d --- /dev/null +++ b/src/app/ranking/global/[page]/page.tsx @@ -0,0 +1,43 @@ +import Card from "@/components/Card"; +import Container from "@/components/Container"; +import Error from "@/components/Error"; +import GlobalRanking from "@/components/player/GlobalRanking"; +import { ScoreSaberAPI } from "@/utils/scoresaber/api"; + +type RankingGlobalProps = { + params: { page: string }; +}; + +// Get data from API (server-sided) +async function getData(page: number) { + const pageData = await ScoreSaberAPI.fetchTopPlayers(page); + if (!pageData || pageData.players.length == 0) { + return undefined; + } + return pageData; +} + +export default async function RankingGlobal({ + params: { page }, +}: RankingGlobalProps) { + const pageData = await getData(Number(page)); + + return ( +
+ + + {pageData == undefined && ( + + )} + {pageData && ( + + )} + + +
+ ); +} diff --git a/src/app/ranking/global/page.tsx b/src/app/ranking/global/page.tsx deleted file mode 100644 index 05e4bf7..0000000 --- a/src/app/ranking/global/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import GlobalRanking from "@/components/player/GlobalRanking"; -import { getPageFromSearchQuery } from "@/utils/utils"; -import { headers } from "next/headers"; - -export default function RankingGlobal() { - const page = getPageFromSearchQuery(headers()); - - return ; -} diff --git a/src/components/player/GlobalRanking.tsx b/src/components/player/GlobalRanking.tsx index 3b3c280..a8db632 100644 --- a/src/components/player/GlobalRanking.tsx +++ b/src/components/player/GlobalRanking.tsx @@ -1,186 +1,100 @@ "use client"; import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; -import { ScoreSaberAPI } from "@/utils/scoresaber/api"; import { normalizedRegionName } from "@/utils/utils"; import dynamic from "next/dynamic"; import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useCallback, useEffect, useState } from "react"; -import Card from "../Card"; -import Container from "../Container"; import Pagination from "../Pagination"; -import Spinner from "../Spinner"; import PlayerRanking from "./PlayerRanking"; import PlayerRankingMobile from "./PlayerRankingMobile"; const ReactCountryFlag = dynamic(() => import("react-country-flag")); -const Error = dynamic(() => import("@/components/Error")); - -type PageInfo = { - loading: boolean; - page: number; - totalPages: number; - players: ScoresaberPlayer[]; -}; type GlobalRankingProps = { page: number; + totalPages: number; country?: string; + players: ScoresaberPlayer[]; }; -export default function GlobalRanking({ page, country }: GlobalRankingProps) { +export default function GlobalRanking({ + page, + totalPages, + country, + players, +}: 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); - }} - /> -
-
-
+ <> +
+
+ {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 */} +
+
+ { + const urlBase = country + ? `/ranking/country/${country}` + : `/ranking/global`; + router.push(`${urlBase}/${page}`, { + scroll: false, + }); + }} + /> +
+
+ + ); } diff --git a/src/components/player/PlayerPage.tsx b/src/components/player/Player.tsx similarity index 100% rename from src/components/player/PlayerPage.tsx rename to src/components/player/Player.tsx diff --git a/src/components/player/PlayerInfo.tsx b/src/components/player/PlayerInfo.tsx index 762cf0e..d5fd2d4 100644 --- a/src/components/player/PlayerInfo.tsx +++ b/src/components/player/PlayerInfo.tsx @@ -6,7 +6,6 @@ import { calcPpBoundary, getAveragePp, getHighestPpPlay, - getTotalScores, } from "@/utils/scoresaber/scores"; import { GlobeAsiaAustraliaIcon, @@ -167,9 +166,7 @@ export default function PlayerInfo({ playerData }: PlayerInfoProps) {

#{formatNumber(playerData.rank)}

@@ -179,7 +176,7 @@ export default function PlayerInfo({ playerData }: PlayerInfoProps) {
diff --git a/src/components/player/PlayerRanking.tsx b/src/components/player/PlayerRanking.tsx index 1f89b1c..789bc74 100644 --- a/src/components/player/PlayerRanking.tsx +++ b/src/components/player/PlayerRanking.tsx @@ -1,3 +1,5 @@ +"use client"; + import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; import { useSettingsStore } from "@/store/settingsStore"; import { formatNumber } from "@/utils/number"; diff --git a/src/components/player/PlayerRankingMobile.tsx b/src/components/player/PlayerRankingMobile.tsx index 75e2dbe..3d16c60 100644 --- a/src/components/player/PlayerRankingMobile.tsx +++ b/src/components/player/PlayerRankingMobile.tsx @@ -1,3 +1,5 @@ +"use client"; + import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; import { useSettingsStore } from "@/store/settingsStore"; import { formatNumber } from "@/utils/number"; @@ -22,7 +24,7 @@ export default function PlayerRankingMobile({ return (
-

+

#{formatNumber(player.rank)}

{showCountryFlag && ( @@ -41,7 +43,7 @@ export default function PlayerRankingMobile({ > {player.name}

-

+