cleanup and start work on player page
This commit is contained in:
parent
527d18a422
commit
b7ec3ebe0e
119
src/app/(pages)/player/[...slug]/page.tsx
Normal file
119
src/app/(pages)/player/[...slug]/page.tsx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { scoresaberLeaderboard } from "@/app/common/leaderboard/impl/scoresaber";
|
||||||
|
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 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 { GlobeAmericasIcon } from "@heroicons/react/24/solid";
|
||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
const playerSubNames = [
|
||||||
|
{
|
||||||
|
icon: <GlobeAmericasIcon className="h-5 w-5" />,
|
||||||
|
render: (player: ScoreSaberPlayer) => {
|
||||||
|
return <p>#{formatNumberWithCommas(player.rank)}</p>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <GlobeAmericasIcon className="h-5 w-5" />,
|
||||||
|
render: (player: ScoreSaberPlayer) => {
|
||||||
|
return <p>#{formatNumberWithCommas(player.countryRank)}</p>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: (player: ScoreSaberPlayer) => {
|
||||||
|
return <p className="tex-pp">{formatNumberWithCommas(player.pp)}pp</p>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
params: {
|
||||||
|
slug: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function generateMetadata({ params: { slug } }: Props): Promise<Metadata> {
|
||||||
|
const id = slug[0]; // The players id
|
||||||
|
const player = await scoresaberLeaderboard.lookupPlayer(id, false);
|
||||||
|
if (player === undefined) {
|
||||||
|
return {
|
||||||
|
title: `Unknown Player`,
|
||||||
|
openGraph: {
|
||||||
|
title: `Unknown Player`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: `${player.name}`,
|
||||||
|
openGraph: {
|
||||||
|
title: `ScoreSaber Reloaded - ${player.name}`,
|
||||||
|
description: `
|
||||||
|
PP: ${formatNumberWithCommas(player.pp)}pp
|
||||||
|
Rank: #${formatNumberWithCommas(player.rank)} (#${formatNumberWithCommas(player.countryRank)} ${player.country})
|
||||||
|
Joined ScoreSaber: ${format(player.firstSeen, { date: "medium", time: "short" })}
|
||||||
|
|
||||||
|
View the scores for ${player.name}!`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Search({ params: { slug } }: Props) {
|
||||||
|
const id = slug[0]; // The players id
|
||||||
|
const sort: ScoreSort = (slug[1] as ScoreSort) || "recent"; // The sorting method
|
||||||
|
const page = slug[2] || 1; // The page number
|
||||||
|
const player = await scoresaberLeaderboard.lookupPlayer(id, false);
|
||||||
|
|
||||||
|
console.log("id", id);
|
||||||
|
console.log("sort", sort);
|
||||||
|
console.log("page", page);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full w-full">
|
||||||
|
{player === undefined && (
|
||||||
|
<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} 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>
|
||||||
|
);
|
||||||
|
}
|
@ -1,80 +0,0 @@
|
|||||||
import { scoresaberLeaderboard } from "@/app/common/leaderboard/impl/scoresaber";
|
|
||||||
import { ScoreSort } from "@/app/common/leaderboard/sort";
|
|
||||||
import { formatNumberWithCommas } from "@/app/common/number-utils";
|
|
||||||
import ClaimProfile from "@/app/components/claim-profile";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/app/components/ui/avatar";
|
|
||||||
import { format } from "@formkit/tempo";
|
|
||||||
import { Metadata } from "next";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
params: {
|
|
||||||
id: string;
|
|
||||||
sort: ScoreSort;
|
|
||||||
page: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function generateMetadata({ params: { id } }: Props): Promise<Metadata> {
|
|
||||||
const player = await scoresaberLeaderboard.lookupPlayer(id, false);
|
|
||||||
if (player === undefined) {
|
|
||||||
return {
|
|
||||||
title: `Unknown Player`,
|
|
||||||
openGraph: {
|
|
||||||
title: `Unknown Player`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: `${player.name}`,
|
|
||||||
openGraph: {
|
|
||||||
title: `ScoreSaber Reloaded - ${player.name}`,
|
|
||||||
description: `
|
|
||||||
PP: ${formatNumberWithCommas(player.pp)}pp
|
|
||||||
Rank: #${formatNumberWithCommas(player.rank)} (#${formatNumberWithCommas(player.countryRank)} ${player.country})
|
|
||||||
Joined ScoreSaber: ${format(player.firstSeen, { date: "medium", time: "short" })}
|
|
||||||
|
|
||||||
View the scores for ${player.name}!`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Search({ params: { id, sort, page } }: Props) {
|
|
||||||
const player = await scoresaberLeaderboard.lookupPlayer(id, false);
|
|
||||||
console.log("id", id);
|
|
||||||
console.log("sort", sort);
|
|
||||||
console.log("page", page);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full w-full">
|
|
||||||
{player === undefined && (
|
|
||||||
<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">
|
|
||||||
<Avatar className="w-32 h-32">
|
|
||||||
<AvatarImage src={player.profilePicture} />
|
|
||||||
<AvatarFallback>{player.name}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<div>
|
|
||||||
<p className="font-bold text-2xl">{player.name}</p>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<p className="text-xl text-gray-400">#{formatNumberWithCommas(player.rank)}</p>
|
|
||||||
<p className="text-xl text-gray-400">#{formatNumberWithCommas(player.countryRank)}</p>
|
|
||||||
<p className="text-xl text-blue-400">{formatNumberWithCommas(player.pp)}pp</p>
|
|
||||||
</div>
|
|
||||||
<div className="absolute top-0 right-0">
|
|
||||||
<ClaimProfile playerId={id} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -30,7 +30,7 @@ const renderNavbarItem = (item: NavbarItem) => (
|
|||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
return (
|
return (
|
||||||
<div className="p-2">
|
<div className="w-full py-2">
|
||||||
<div className="px-2 h-10 rounded-md items-center flex justify-between bg-secondary">
|
<div className="px-2 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 gap-2">
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
import { CheckIcon } from "@heroicons/react/24/solid";
|
import { CheckIcon } from "@heroicons/react/24/solid";
|
||||||
import { useLiveQuery } from "dexie-react-hooks";
|
import { useLiveQuery } from "dexie-react-hooks";
|
||||||
import { setPlayerIdCookie } from "../common/website-utils";
|
import { setPlayerIdCookie } from "../../common/website-utils";
|
||||||
import useDatabase from "../hooks/use-database";
|
import useDatabase from "../../hooks/use-database";
|
||||||
import { useToast } from "../hooks/use-toast";
|
import { useToast } from "../../hooks/use-toast";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/**
|
/**
|
13
src/app/components/player/player-sub-name.tsx
Normal file
13
src/app/components/player/player-sub-name.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
type Props = {
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PlayerSubName({ icon, children }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
{icon}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -63,8 +63,10 @@ export default function RootLayout({
|
|||||||
<BackgroundImage />
|
<BackgroundImage />
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
||||||
|
<main className="z-[9999] m-auto flex h-screen min-h-full flex-col items-center opacity-90 md:max-w-[1200px]">
|
||||||
<NavBar />
|
<NavBar />
|
||||||
<main className="m-2 z-50">{children}</main>
|
{children}
|
||||||
|
</main>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</DatabaseLoader>
|
</DatabaseLoader>
|
||||||
|
@ -8,7 +8,7 @@ export function middleware(request: NextRequest) {
|
|||||||
const playerIdCookie = cookies.get("playerId");
|
const playerIdCookie = cookies.get("playerId");
|
||||||
if (pathname == "/") {
|
if (pathname == "/") {
|
||||||
if (playerIdCookie) {
|
if (playerIdCookie) {
|
||||||
return NextResponse.redirect(new URL(`/player/${playerIdCookie.value}/top/1`, request.url));
|
return NextResponse.redirect(new URL(`/player/${playerIdCookie.value}`, request.url));
|
||||||
} else {
|
} else {
|
||||||
return NextResponse.redirect(new URL("/search", request.url));
|
return NextResponse.redirect(new URL("/search", request.url));
|
||||||
}
|
}
|
||||||
|
@ -10,53 +10,54 @@ const config: Config = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
background: 'hsl(var(--background))',
|
pp: "text-purple-400",
|
||||||
foreground: 'hsl(var(--foreground))',
|
background: "hsl(var(--background))",
|
||||||
|
foreground: "hsl(var(--foreground))",
|
||||||
card: {
|
card: {
|
||||||
DEFAULT: 'hsl(var(--card))',
|
DEFAULT: "hsl(var(--card))",
|
||||||
foreground: 'hsl(var(--card-foreground))'
|
foreground: "hsl(var(--card-foreground))",
|
||||||
},
|
},
|
||||||
popover: {
|
popover: {
|
||||||
DEFAULT: 'hsl(var(--popover))',
|
DEFAULT: "hsl(var(--popover))",
|
||||||
foreground: 'hsl(var(--popover-foreground))'
|
foreground: "hsl(var(--popover-foreground))",
|
||||||
},
|
},
|
||||||
primary: {
|
primary: {
|
||||||
DEFAULT: 'hsl(var(--primary))',
|
DEFAULT: "hsl(var(--primary))",
|
||||||
foreground: 'hsl(var(--primary-foreground))'
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
DEFAULT: 'hsl(var(--secondary))',
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
foreground: 'hsl(var(--secondary-foreground))'
|
foreground: "hsl(var(--secondary-foreground))",
|
||||||
},
|
},
|
||||||
muted: {
|
muted: {
|
||||||
DEFAULT: 'hsl(var(--muted))',
|
DEFAULT: "hsl(var(--muted))",
|
||||||
foreground: 'hsl(var(--muted-foreground))'
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
},
|
},
|
||||||
accent: {
|
accent: {
|
||||||
DEFAULT: 'hsl(var(--accent))',
|
DEFAULT: "hsl(var(--accent))",
|
||||||
foreground: 'hsl(var(--accent-foreground))'
|
foreground: "hsl(var(--accent-foreground))",
|
||||||
},
|
},
|
||||||
destructive: {
|
destructive: {
|
||||||
DEFAULT: 'hsl(var(--destructive))',
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
foreground: 'hsl(var(--destructive-foreground))'
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
},
|
},
|
||||||
border: 'hsl(var(--border))',
|
border: "hsl(var(--border))",
|
||||||
input: 'hsl(var(--input))',
|
input: "hsl(var(--input))",
|
||||||
ring: 'hsl(var(--ring))',
|
ring: "hsl(var(--ring))",
|
||||||
chart: {
|
chart: {
|
||||||
'1': 'hsl(var(--chart-1))',
|
"1": "hsl(var(--chart-1))",
|
||||||
'2': 'hsl(var(--chart-2))',
|
"2": "hsl(var(--chart-2))",
|
||||||
'3': 'hsl(var(--chart-3))',
|
"3": "hsl(var(--chart-3))",
|
||||||
'4': 'hsl(var(--chart-4))',
|
"4": "hsl(var(--chart-4))",
|
||||||
'5': 'hsl(var(--chart-5))'
|
"5": "hsl(var(--chart-5))",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
lg: 'var(--radius)',
|
lg: "var(--radius)",
|
||||||
md: 'calc(var(--radius) - 2px)',
|
md: "calc(var(--radius) - 2px)",
|
||||||
sm: 'calc(var(--radius) - 4px)'
|
sm: "calc(var(--radius) - 4px)",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("tailwindcss-animate")],
|
plugins: [require("tailwindcss-animate")],
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user