test embed per player page
All checks were successful
deploy / deploy (push) Successful in 56s

This commit is contained in:
Lee 2023-10-23 06:40:38 +01:00
parent 1001e68e1a
commit 26c097d29f
2 changed files with 133 additions and 95 deletions

View File

@ -1,102 +1,34 @@
"use client";
import Card from "@/components/Card";
import Container from "@/components/Container";
import Error from "@/components/Error";
import { Spinner } from "@/components/Spinner";
import PlayerInfo from "@/components/player/PlayerInfo";
import Scores from "@/components/player/Scores";
import { ScoresaberPlayer } from "@/schemas/scoresaber/player";
import { useSettingsStore } from "@/store/settingsStore";
import { SortType, SortTypes } from "@/types/SortTypes";
import PlayerPage from "@/components/player/PlayerPage";
import { ScoreSaberAPI } from "@/utils/scoresaber/api";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
import { Metadata } from "next";
type PlayerInfo = {
loading: boolean;
player: ScoresaberPlayer | undefined;
type Props = {
params: { id: string };
};
const DEFAULT_SORT_TYPE = SortTypes.top;
export default function Player({ params }: { params: { id: string } }) {
const searchParams = useSearchParams();
const [mounted, setMounted] = useState(false);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const [player, setPlayer] = useState<PlayerInfo>({
loading: true,
player: undefined,
});
let page;
const pageString = searchParams.get("page");
if (pageString == null) {
page = 1;
} else {
page = Number.parseInt(pageString) || 1;
export async function generateMetadata({
params: { id },
}: Props): Promise<Metadata> {
const player = await ScoreSaberAPI.fetchPlayerData(id);
if (!player) {
return {
title: "Player not found",
};
}
let sortType: SortType;
const sortTypeString = searchParams.get("sort");
if (sortTypeString == null) {
sortType =
useSettingsStore.getState().lastUsedSortType || DEFAULT_SORT_TYPE;
} else {
sortType = SortTypes[sortTypeString] || DEFAULT_SORT_TYPE;
}
useEffect(() => {
setMounted(true);
if (error || !player.loading) {
return;
}
if (mounted == true) {
return;
}
ScoreSaberAPI.fetchPlayerData(params.id).then((playerResponse) => {
if (!playerResponse) {
setError(true);
setErrorMessage("Failed to fetch player. Is the ID correct?");
setPlayer({ ...player, loading: false });
return;
}
setPlayer({ ...player, player: playerResponse, loading: false });
});
}, [error, mounted, params.id, player]);
if (player.loading || error || !player.player) {
return (
<main>
<Container>
<Card className="mt-2">
<div className="p-3 text-center">
<div role="status">
<div className="flex flex-col items-center justify-center gap-2">
{error && <Error errorMessage={errorMessage} />}
{!error && <Spinner />}
</div>
</div>
</div>
</Card>
</Container>
</main>
);
}
const playerData = player.player;
return (
<main>
<Container>
<PlayerInfo playerData={playerData} />
<Scores playerData={playerData} page={page} sortType={sortType} />
</Container>
</main>
);
return {
title: `${player.name} - Scoresaber Leaderboards`,
description: `View ${player.name}'s scores, top plays, and more.,&#x0A;test`,
openGraph: {
images: [
{
url: player.profilePicture,
},
],
},
};
}
export default function Player({ params: { id } }: Props) {
return <PlayerPage id={id} />;
}

View File

@ -0,0 +1,106 @@
"use client";
import Card from "@/components/Card";
import Container from "@/components/Container";
import Error from "@/components/Error";
import { Spinner } from "@/components/Spinner";
import PlayerInfo from "@/components/player/PlayerInfo";
import Scores from "@/components/player/Scores";
import { ScoresaberPlayer } from "@/schemas/scoresaber/player";
import { useSettingsStore } from "@/store/settingsStore";
import { SortType, SortTypes } from "@/types/SortTypes";
import { ScoreSaberAPI } from "@/utils/scoresaber/api";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";
type PlayerInfo = {
loading: boolean;
player: ScoresaberPlayer | undefined;
};
type PlayerPageProps = {
id: string;
};
const DEFAULT_SORT_TYPE = SortTypes.top;
export default function PlayerPage({ id }: PlayerPageProps) {
const searchParams = useSearchParams();
const [mounted, setMounted] = useState(false);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const [player, setPlayer] = useState<PlayerInfo>({
loading: true,
player: undefined,
});
let page;
const pageString = searchParams.get("page");
if (pageString == null) {
page = 1;
} else {
page = Number.parseInt(pageString) || 1;
}
let sortType: SortType;
const sortTypeString = searchParams.get("sort");
if (sortTypeString == null) {
sortType =
useSettingsStore.getState().lastUsedSortType || DEFAULT_SORT_TYPE;
} else {
sortType = SortTypes[sortTypeString] || DEFAULT_SORT_TYPE;
}
useEffect(() => {
setMounted(true);
if (error || !player.loading) {
return;
}
if (mounted == true) {
return;
}
ScoreSaberAPI.fetchPlayerData(id).then((playerResponse) => {
if (!playerResponse) {
setError(true);
setErrorMessage("Failed to fetch player. Is the ID correct?");
setPlayer({ ...player, loading: false });
return;
}
setPlayer({ ...player, player: playerResponse, loading: false });
});
}, [error, mounted, id, player]);
if (player.loading || error || !player.player) {
return (
<main>
<Container>
<Card className="mt-2">
<div className="p-3 text-center">
<div role="status">
<div className="flex flex-col items-center justify-center gap-2">
{error && <Error errorMessage={errorMessage} />}
{!error && <Spinner />}
</div>
</div>
</div>
</Card>
</Container>
</main>
);
}
const playerData = player.player;
return (
<main>
<Container>
<PlayerInfo playerData={playerData} />
<Scores playerData={playerData} page={page} sortType={sortType} />
</Container>
</main>
);
}