feat : improve game loading flow

This commit is contained in:
GuillaumeSD
2025-05-09 01:53:34 +02:00
parent 5dc1c4f485
commit 34fdde282c
4 changed files with 137 additions and 114 deletions

View File

@@ -1,7 +1,6 @@
import { useLocalStorage } from "@/hooks/useLocalStorage";
import { getLichessUserRecentGames } from "@/lib/lichess";
import { capitalize } from "@/lib/helpers";
import { LichessGame } from "@/types/lichess";
import {
CircularProgress,
FormControl,
@@ -10,43 +9,32 @@ import {
ListItemText,
TextField,
} from "@mui/material";
import { useEffect, useState } from "react";
import { useSetAtom } from "jotai";
import { boardOrientationAtom } from "../analysis/states";
import { useDebounce } from "@/hooks/useDebounce";
import { useQuery } from "@tanstack/react-query";
interface Props {
onSelect: (pgn: string) => void;
}
export default function LichessInput({ onSelect }: Props) {
const [requestCount, setRequestCount] = useState(0);
const [lichessUsername, setLichessUsername] = useLocalStorage(
"lichess-username",
""
);
const debouncedUsername = useDebounce(lichessUsername, 200);
const setBoardOrientation = useSetAtom(boardOrientationAtom);
const [games, setGames] = useState<LichessGame[]>([]);
useEffect(() => {
if (!lichessUsername) {
setGames([]);
return;
}
const timeout = setTimeout(
async () => {
const games = await getLichessUserRecentGames(lichessUsername);
setGames(games);
},
requestCount === 0 ? 0 : 500
);
setRequestCount((prev) => prev + 1);
return () => {
clearTimeout(timeout);
};
}, [lichessUsername]); // eslint-disable-line react-hooks/exhaustive-deps
const {
data: games,
isFetching,
isError,
} = useQuery({
queryKey: ["LichessUserGames", debouncedUsername],
enabled: !!debouncedUsername,
queryFn: () => getLichessUserRecentGames(debouncedUsername ?? ""),
});
return (
<>
@@ -68,40 +56,50 @@ export default function LichessInput({ onSelect }: Props) {
minHeight={100}
size={12}
>
{games.map((game) => (
<ListItemButton
onClick={() => {
setBoardOrientation(
lichessUsername.toLowerCase() !==
game.players.black.user?.name.toLowerCase()
);
onSelect(game.pgn);
}}
style={{ width: 350, maxWidth: 350 }}
key={game.id}
>
<ListItemText
primary={`${
capitalize(game.players.white.user?.name || "white") ||
"White"
} (${game.players?.white?.rating || "?"}) vs ${
capitalize(game.players.black.user?.name || "black") ||
"Black"
} (${game.players?.black?.rating || "?"})`}
secondary={`${capitalize(game.speed)} played at ${new Date(
game.lastMoveAt
)
.toLocaleString()
.slice(0, -3)}`}
slotProps={{
primary: { noWrap: true },
secondary: { noWrap: true },
{isFetching ? (
<CircularProgress />
) : isError ? (
<span style={{ color: "salmon" }}>
User not found. Please check your username.
</span>
) : !games?.length ? (
<span style={{ color: "salmon" }}>
No games found. Please check your username.
</span>
) : (
games.map((game) => (
<ListItemButton
onClick={() => {
setBoardOrientation(
lichessUsername.toLowerCase() !==
game.players.black.user?.name.toLowerCase()
);
onSelect(game.pgn);
}}
/>
</ListItemButton>
))}
{games.length === 0 && <CircularProgress />}
style={{ width: 350, maxWidth: 350 }}
key={game.id}
>
<ListItemText
primary={`${
capitalize(game.players.white.user?.name || "white") ||
"White"
} (${game.players?.white?.rating || "?"}) vs ${
capitalize(game.players.black.user?.name || "black") ||
"Black"
} (${game.players?.black?.rating || "?"})`}
secondary={`${capitalize(game.speed)} played at ${new Date(
game.lastMoveAt
)
.toLocaleString()
.slice(0, -3)}`}
slotProps={{
primary: { noWrap: true },
secondary: { noWrap: true },
}}
/>
</ListItemButton>
))
)}
</Grid>
)}
</>