add navbar items and auto refreshing of player page data
This commit is contained in:
parent
19e10e883d
commit
a2401e6b57
@ -19,6 +19,7 @@
|
|||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.1",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
|
"@tanstack/react-query": "^5.55.4",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.8",
|
"dexie": "^4.0.8",
|
||||||
|
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@ -35,6 +35,9 @@ dependencies:
|
|||||||
'@radix-ui/react-tooltip':
|
'@radix-ui/react-tooltip':
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1)
|
version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1)
|
||||||
|
'@tanstack/react-query':
|
||||||
|
specifier: ^5.55.4
|
||||||
|
version: 5.55.4(react@18.3.1)
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.0
|
specifier: ^0.7.0
|
||||||
version: 0.7.0
|
version: 0.7.0
|
||||||
@ -872,6 +875,19 @@ packages:
|
|||||||
tslib: 2.7.0
|
tslib: 2.7.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tanstack/query-core@5.55.4:
|
||||||
|
resolution: {integrity: sha512-uoRqNnRfzOH4OMIoxj8E2+Us89UIGXfau981qYJWsNMkFS1GXR4UIyzUTVGq4N7SDLHgFPpo6IOazqUV5gkMZA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@tanstack/react-query@5.55.4(react@18.3.1):
|
||||||
|
resolution: {integrity: sha512-e3uX5XkLD9oTV66/VsVpkYz3Ds/ps/Yk+V5d89xthAbtNIKKBEm4FdNb9yISFzGEGezUzVO68qmfmiSrtScvsg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18 || ^19
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/query-core': 5.55.4
|
||||||
|
react: 18.3.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/json-schema@7.0.15:
|
/@types/json-schema@7.0.15:
|
||||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useLiveQuery } from "dexie-react-hooks";
|
|
||||||
import useDatabase from "../hooks/use-database";
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const database = useDatabase();
|
return <>home page</>;
|
||||||
const settings = useLiveQuery(() => database.getSettings());
|
|
||||||
return <>{settings?.playerId}</>;
|
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,10 @@
|
|||||||
import { scoresaberLeaderboard } from "@/app/common/leaderboard/impl/scoresaber";
|
import { scoresaberLeaderboard } from "@/app/common/leaderboard/impl/scoresaber";
|
||||||
import { ScoreSort } from "@/app/common/leaderboard/sort";
|
import { ScoreSort } from "@/app/common/leaderboard/sort";
|
||||||
import ScoreSaberPlayer from "@/app/common/leaderboard/types/scoresaber/scoresaber-player";
|
|
||||||
import { formatNumberWithCommas } from "@/app/common/number-utils";
|
import { formatNumberWithCommas } from "@/app/common/number-utils";
|
||||||
import CountryFlag from "@/app/components/country-flag";
|
import PlayerData from "@/app/components/player/player-data";
|
||||||
import ClaimProfile from "@/app/components/player/claim-profile";
|
|
||||||
import PlayerSubName from "@/app/components/player/player-sub-name";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/app/components/ui/avatar";
|
|
||||||
import { format } from "@formkit/tempo";
|
import { format } from "@formkit/tempo";
|
||||||
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
const playerSubNames = [
|
|
||||||
{
|
|
||||||
icon: () => {
|
|
||||||
return <GlobeAmericasIcon className="h-5 w-5" />;
|
|
||||||
},
|
|
||||||
render: (player: ScoreSaberPlayer) => {
|
|
||||||
return <p>#{formatNumberWithCommas(player.rank)}</p>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (player: ScoreSaberPlayer) => {
|
|
||||||
return <CountryFlag country={player.country} size={15} />;
|
|
||||||
},
|
|
||||||
render: (player: ScoreSaberPlayer) => {
|
|
||||||
return <p>#{formatNumberWithCommas(player.countryRank)}</p>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
render: (player: ScoreSaberPlayer) => {
|
|
||||||
return <p className="tex-pp">{formatNumberWithCommas(player.pp)}pp</p>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
params: {
|
params: {
|
||||||
@ -76,49 +48,13 @@ export default async function Search({ params: { slug } }: Props) {
|
|||||||
console.log("sort", sort);
|
console.log("sort", sort);
|
||||||
console.log("page", page);
|
console.log("page", page);
|
||||||
|
|
||||||
|
if (player == undefined) {
|
||||||
|
return redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full w-full">
|
<div className="flex flex-col h-full w-full">
|
||||||
{player === undefined && (
|
<PlayerData initalPlayerData={player} />
|
||||||
<div>
|
|
||||||
<p>idek mate</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{player !== undefined && (
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<div className="flex flex-col bg-secondary p-2 rounded-md">
|
|
||||||
<div className="flex gap-3 flex-col items-center text-center sm:flex-row sm:items-start sm:text-start relative select-none">
|
|
||||||
<Avatar className="w-32 h-32 pointer-events-none">
|
|
||||||
<AvatarImage src={player.profilePicture} />
|
|
||||||
<AvatarFallback>{player.name}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-2xl">{player.name}</p>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{playerSubNames.map((subName, index) => {
|
|
||||||
return (
|
|
||||||
<PlayerSubName icon={subName.icon && subName.icon(player)} key={index}>
|
|
||||||
{subName.render(player)}
|
|
||||||
</PlayerSubName>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{/* <PlayerSubName icon={<GlobeAmericasIcon className="size-6" />}>
|
|
||||||
<p>#{formatNumberWithCommas(player.rank)}</p>
|
|
||||||
</PlayerSubName>
|
|
||||||
<PlayerSubName icon={<GlobeAmericasIcon className="size-6" />}>
|
|
||||||
<p>#{formatNumberWithCommas(player.countryRank)}</p>
|
|
||||||
</PlayerSubName>
|
|
||||||
<PlayerSubName>
|
|
||||||
<p className="text-pp">{formatNumberWithCommas(player.pp)}pp</p>
|
|
||||||
</PlayerSubName> */}
|
|
||||||
</div>
|
|
||||||
<div className="absolute top-0 right-0">
|
|
||||||
<ClaimProfile playerId={id} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ class ScoreSaberLeaderboard extends Leaderboard {
|
|||||||
* @returns the players that match the query, or undefined if no players were found
|
* @returns the players that match the query, or undefined if no players were found
|
||||||
*/
|
*/
|
||||||
async searchPlayers(query: string, useProxy = true): Promise<ScoreSaberPlayerSearch | undefined> {
|
async searchPlayers(query: string, useProxy = true): Promise<ScoreSaberPlayerSearch | undefined> {
|
||||||
|
console.log(`SS API: Searching for players matching "${query}"...`);
|
||||||
try {
|
try {
|
||||||
const results = await ky
|
const results = await ky
|
||||||
.get((useProxy ? "https://proxy.fascinated.cc/" : "") + SEARCH_PLAYERS_ENDPOINT.replace("{query}", query))
|
.get((useProxy ? "https://proxy.fascinated.cc/" : "") + SEARCH_PLAYERS_ENDPOINT.replace("{query}", query))
|
||||||
@ -38,6 +39,7 @@ class ScoreSaberLeaderboard extends Leaderboard {
|
|||||||
* @returns the player that matches the ID, or undefined
|
* @returns the player that matches the ID, or undefined
|
||||||
*/
|
*/
|
||||||
async lookupPlayer(playerId: string, useProxy = true): Promise<ScoreSaberPlayer | undefined> {
|
async lookupPlayer(playerId: string, useProxy = true): Promise<ScoreSaberPlayer | undefined> {
|
||||||
|
console.log(`SS API: Looking up player "${playerId}"...`);
|
||||||
try {
|
try {
|
||||||
const results = await ky
|
const results = await ky
|
||||||
.get((useProxy ? "https://proxy.fascinated.cc/" : "") + LOOKUP_PLAYER_ENDPOINT.replace("{playerId}", playerId))
|
.get((useProxy ? "https://proxy.fascinated.cc/" : "") + LOOKUP_PLAYER_ENDPOINT.replace("{playerId}", playerId))
|
||||||
|
@ -6,6 +6,6 @@ type Props = {
|
|||||||
export default function CountryFlag({ country, size = 24 }: Props) {
|
export default function CountryFlag({ country, size = 24 }: Props) {
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img alt="Player Country" src={`/assets/flags/${country}.png`} className={`h-[${size}px] w-[${size * 2}px]`} />
|
<img alt="Player Country" src={`/assets/flags/${country}.png`} width={size * 2} height={size} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ export default function SearchPlayer() {
|
|||||||
{results?.map((player) => {
|
{results?.map((player) => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/player/${player.id}/top/1`}
|
href={`/player/${player.id}`}
|
||||||
key={player.id}
|
key={player.id}
|
||||||
className="bg-secondary p-2 rounded-md flex gap-2 items-center hover:brightness-75 transition-all transform-gpu"
|
className="bg-secondary p-2 rounded-md flex gap-2 items-center hover:brightness-75 transition-all transform-gpu"
|
||||||
>
|
>
|
||||||
|
11
src/app/components/navbar/navbar-button.tsx
Normal file
11
src/app/components/navbar/navbar-button.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NavbarButton({ children }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="px-2 gap-2 rounded-md hover:bg-blue-500 transform-gpu transition-all h-full flex items-center">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
|
import { HomeIcon } from "@heroicons/react/24/solid";
|
||||||
|
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import NavbarButton from "./navbar-button";
|
||||||
|
import ProfileButton from "./profile-button";
|
||||||
|
|
||||||
type NavbarItem = {
|
type NavbarItem = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -11,12 +15,12 @@ const items: NavbarItem[] = [
|
|||||||
{
|
{
|
||||||
name: "Home",
|
name: "Home",
|
||||||
link: "/",
|
link: "/",
|
||||||
icon: undefined, // Add your home icon here
|
icon: <HomeIcon className="h-5 w-5" />, // Add your home icon here
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Search",
|
name: "Search",
|
||||||
link: "/search",
|
link: "/search",
|
||||||
icon: undefined, // Add your search icon here
|
icon: <MagnifyingGlassIcon className="h-5 w-5" />, // Add your search icon here
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -31,20 +35,22 @@ const renderNavbarItem = (item: NavbarItem) => (
|
|||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full py-2">
|
<div className="w-full py-2">
|
||||||
<div className="px-2 h-10 rounded-md items-center flex justify-between bg-secondary">
|
<div className="h-10 rounded-md items-center flex justify-between bg-secondary">
|
||||||
{/* Left-aligned items */}
|
{/* Left-aligned items */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center h-full">
|
||||||
|
<ProfileButton />
|
||||||
|
|
||||||
{items.slice(0, -1).map((item, index) => (
|
{items.slice(0, -1).map((item, index) => (
|
||||||
<div key={index}>
|
<NavbarButton key={index}>
|
||||||
<Link href={item.link}>{renderNavbarItem(item)}</Link>
|
<Link href={item.link}>{renderNavbarItem(item)}</Link>
|
||||||
</div>
|
</NavbarButton>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right-aligned item */}
|
{/* Right-aligned item */}
|
||||||
<div>
|
<NavbarButton>
|
||||||
<Link href={items[items.length - 1].link}>{renderNavbarItem(items[items.length - 1])}</Link>
|
<Link href={items[items.length - 1].link}>{renderNavbarItem(items[items.length - 1])}</Link>
|
||||||
</div>
|
</NavbarButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
27
src/app/components/navbar/profile-button.tsx
Normal file
27
src/app/components/navbar/profile-button.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import useDatabase from "@/app/hooks/use-database";
|
||||||
|
import { useLiveQuery } from "dexie-react-hooks";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Avatar, AvatarImage } from "../ui/avatar";
|
||||||
|
import NavbarButton from "./navbar-button";
|
||||||
|
|
||||||
|
export default function ProfileButton() {
|
||||||
|
const database = useDatabase();
|
||||||
|
const settings = useLiveQuery(() => database.getSettings());
|
||||||
|
|
||||||
|
if (settings == undefined) {
|
||||||
|
return; // Settings hasn't loaded yet
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavbarButton>
|
||||||
|
<Link href={`/player/${settings.playerId}`} className="flex items-center gap-2">
|
||||||
|
<Avatar className="w-6 h-6">
|
||||||
|
<AvatarImage src={`https://cdn.scoresaber.com/avatars/${settings.playerId}.jpg`} />
|
||||||
|
</Avatar>
|
||||||
|
<p>You</p>
|
||||||
|
</Link>
|
||||||
|
</NavbarButton>
|
||||||
|
);
|
||||||
|
}
|
31
src/app/components/player/player-data.tsx
Normal file
31
src/app/components/player/player-data.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { scoresaberLeaderboard } from "@/app/common/leaderboard/impl/scoresaber";
|
||||||
|
import ScoreSaberPlayer from "@/app/common/leaderboard/types/scoresaber/scoresaber-player";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import PlayerHeader from "./player-header";
|
||||||
|
|
||||||
|
const REFRESH_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
initalPlayerData: ScoreSaberPlayer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PlayerData({ initalPlayerData }: Props) {
|
||||||
|
let player = initalPlayerData;
|
||||||
|
const { data, isLoading, isError } = useQuery({
|
||||||
|
queryKey: ["player", player.id],
|
||||||
|
queryFn: () => scoresaberLeaderboard.lookupPlayer(player.id),
|
||||||
|
refetchInterval: REFRESH_INTERVAL,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data && (!isLoading || !isError)) {
|
||||||
|
player = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<PlayerHeader player={player} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
62
src/app/components/player/player-header.tsx
Normal file
62
src/app/components/player/player-header.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import ScoreSaberPlayer from "@/app/common/leaderboard/types/scoresaber/scoresaber-player";
|
||||||
|
import { formatNumberWithCommas } from "@/app/common/number-utils";
|
||||||
|
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
|
||||||
|
import CountryFlag from "../country-flag";
|
||||||
|
import { Avatar, AvatarImage } from "../ui/avatar";
|
||||||
|
import ClaimProfile from "./claim-profile";
|
||||||
|
import PlayerSubName from "./player-sub-name";
|
||||||
|
|
||||||
|
const playerSubNames = [
|
||||||
|
{
|
||||||
|
icon: () => {
|
||||||
|
return <GlobeAmericasIcon className="h-5 w-5" />;
|
||||||
|
},
|
||||||
|
render: (player: ScoreSaberPlayer) => {
|
||||||
|
return <p>#{formatNumberWithCommas(player.rank)}</p>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (player: ScoreSaberPlayer) => {
|
||||||
|
return <CountryFlag country={player.country} size={15} />;
|
||||||
|
},
|
||||||
|
render: (player: ScoreSaberPlayer) => {
|
||||||
|
return <p>#{formatNumberWithCommas(player.countryRank)}</p>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: (player: ScoreSaberPlayer) => {
|
||||||
|
return <p className="tex-pp">{formatNumberWithCommas(player.pp)}pp</p>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
player: ScoreSaberPlayer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PlayerHeader({ player }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col bg-secondary p-2 rounded-md">
|
||||||
|
<div className="flex gap-3 flex-col items-center text-center sm:flex-row sm:items-start sm:text-start relative select-none">
|
||||||
|
<Avatar className="w-32 h-32 pointer-events-none">
|
||||||
|
<AvatarImage fetchPriority="high" src={player.profilePicture} />
|
||||||
|
</Avatar>
|
||||||
|
<div>
|
||||||
|
<p className="font-bold text-2xl">{player.name}</p>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{playerSubNames.map((subName, index) => {
|
||||||
|
return (
|
||||||
|
<PlayerSubName icon={subName.icon && subName.icon(player)} key={index}>
|
||||||
|
{subName.render(player)}
|
||||||
|
</PlayerSubName>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-0 right-0">
|
||||||
|
<ClaimProfile playerId={player.id} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
13
src/app/components/providers/query-provider.tsx
Normal file
13
src/app/components/providers/query-provider.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
export function QueryProvider({ children }: Props) {
|
||||||
|
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
|
||||||
|
}
|
@ -2,7 +2,8 @@ import type { Metadata } from "next";
|
|||||||
import localFont from "next/font/local";
|
import localFont from "next/font/local";
|
||||||
import BackgroundImage from "./components/background-image";
|
import BackgroundImage from "./components/background-image";
|
||||||
import DatabaseLoader from "./components/loaders/database-loader";
|
import DatabaseLoader from "./components/loaders/database-loader";
|
||||||
import NavBar from "./components/navbar";
|
import NavBar from "./components/navbar/navbar";
|
||||||
|
import { QueryProvider } from "./components/providers/query-provider";
|
||||||
import { ThemeProvider } from "./components/providers/theme-provider";
|
import { ThemeProvider } from "./components/providers/theme-provider";
|
||||||
import { Toaster } from "./components/ui/toaster";
|
import { Toaster } from "./components/ui/toaster";
|
||||||
import { TooltipProvider } from "./components/ui/tooltip";
|
import { TooltipProvider } from "./components/ui/tooltip";
|
||||||
@ -63,10 +64,12 @@ export default function RootLayout({
|
|||||||
<BackgroundImage />
|
<BackgroundImage />
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
||||||
|
<QueryProvider>
|
||||||
<main className="z-[9999] m-auto flex h-screen flex-col items-center md:max-w-[1200px]">
|
<main className="z-[9999] m-auto flex h-screen flex-col items-center md:max-w-[1200px]">
|
||||||
<NavBar />
|
<NavBar />
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
|
</QueryProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</DatabaseLoader>
|
</DatabaseLoader>
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import type { NextRequest } from "next/server";
|
|
||||||
import { NextResponse } from "next/server";
|
|
||||||
|
|
||||||
export function middleware(request: NextRequest) {
|
|
||||||
const { pathname } = request.nextUrl;
|
|
||||||
const cookies = request.cookies;
|
|
||||||
|
|
||||||
const playerIdCookie = cookies.get("playerId");
|
|
||||||
if (pathname == "/") {
|
|
||||||
if (playerIdCookie) {
|
|
||||||
return NextResponse.redirect(new URL(`/player/${playerIdCookie.value}`, request.url));
|
|
||||||
} else {
|
|
||||||
return NextResponse.redirect(new URL("/search", request.url));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
matcher: "/((?!api|_next/static|_next/image|favicon.ico).*)",
|
|
||||||
};
|
|
Reference in New Issue
Block a user