mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 01:46:57 +00:00
set and clear game state using reducers - removed the need for global state management
This commit is contained in:
@@ -1,31 +0,0 @@
|
|||||||
import LichessPgnViewer from "lichess-pgn-viewer";
|
|
||||||
import type PgnViewer from "lichess-pgn-viewer/pgnViewer";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
pgn: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PgnViewerComponent = ({ pgn }: IProps) => {
|
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
const viewerRef = useRef<PgnViewer | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!containerRef.current) return;
|
|
||||||
|
|
||||||
viewerRef.current = LichessPgnViewer(containerRef.current, {
|
|
||||||
pgn,
|
|
||||||
scrollToMove: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (containerRef.current) {
|
|
||||||
containerRef.current.innerHTML = "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [pgn]);
|
|
||||||
|
|
||||||
return <div ref={containerRef} className="lpv-board" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PgnViewerComponent;
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import LichessPgnViewer from "lichess-pgn-viewer";
|
||||||
|
import type PgnViewerType from "lichess-pgn-viewer/pgnViewer";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
gamePgn: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PgnViewer = ({ gamePgn }: IProps) => {
|
||||||
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const viewerRef = useRef<PgnViewerType | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const element: HTMLElement | null = document.querySelector(".lpv-board");
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
viewerRef.current = LichessPgnViewer(element, {
|
||||||
|
pgn: gamePgn,
|
||||||
|
scrollToMove: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
containerRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [gamePgn]);
|
||||||
|
|
||||||
|
return <div ref={containerRef} className="lpv-board" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PgnViewer;
|
||||||
@@ -1,7 +1,81 @@
|
|||||||
|
import { type ChangeEvent, useReducer, useRef } from "react";
|
||||||
import Chessboard from "#/pages/Chessboard.tsx";
|
import Chessboard from "#/pages/Chessboard.tsx";
|
||||||
|
import { gameReducer, initialGameState } from "#/reducers/gameReducer.ts";
|
||||||
|
|
||||||
const ChessboardContainer = () => {
|
const ChessboardContainer = () => {
|
||||||
return <Chessboard />;
|
const [gameState, gameDispatch] = useReducer(gameReducer, initialGameState);
|
||||||
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const handleSavePgn = () => {
|
||||||
|
console.log("Save PGN");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSavePdf = () => {
|
||||||
|
console.log("Save PDF");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearGame = () => {
|
||||||
|
if (fileInputRef.current) {
|
||||||
|
fileInputRef.current.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
gameDispatch({
|
||||||
|
type: "CLEAR_GAME",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLoadPgn = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const selectedFile = e.target.files?.[0];
|
||||||
|
|
||||||
|
if (selectedFile?.name.endsWith(".pgn")) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
const pgnData = event.target?.result;
|
||||||
|
let pgnString = "";
|
||||||
|
|
||||||
|
if (pgnData) {
|
||||||
|
if (typeof pgnData === "string") {
|
||||||
|
pgnString = pgnData;
|
||||||
|
} else {
|
||||||
|
pgnString = new TextDecoder().decode(pgnData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pgnString) {
|
||||||
|
gameDispatch({
|
||||||
|
type: "SET_GAME",
|
||||||
|
payload: { pgn: pgnString, headers: "" },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
gameDispatch({
|
||||||
|
type: "CLEAR_GAME",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(selectedFile);
|
||||||
|
} else {
|
||||||
|
if (fileInputRef.current) {
|
||||||
|
fileInputRef.current.value = "";
|
||||||
|
}
|
||||||
|
e.target.files = null;
|
||||||
|
gameDispatch({
|
||||||
|
type: "CLEAR_GAME",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Chessboard
|
||||||
|
gameState={gameState}
|
||||||
|
handleLoadPgn={handleLoadPgn}
|
||||||
|
handleClearGame={handleClearGame}
|
||||||
|
handleSavePgn={handleSavePgn}
|
||||||
|
handleSavePdf={handleSavePdf}
|
||||||
|
fileInputRef={fileInputRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ChessboardContainer;
|
export default ChessboardContainer;
|
||||||
|
|||||||
@@ -1,16 +1,65 @@
|
|||||||
|
import type { ChangeEvent, RefObject } from "react";
|
||||||
import LichessButton from "#/components/LichessButton.tsx";
|
import LichessButton from "#/components/LichessButton.tsx";
|
||||||
import PgnViewerComponent from "#/components/LichessPgnViewerComponent.tsx";
|
import PgnViewer from "#/components/PgnViewer.tsx";
|
||||||
import Section from "#/components/Section.tsx";
|
import Section from "#/components/Section.tsx";
|
||||||
|
import type { IGameState } from "#/reducers/gameReducer.ts";
|
||||||
|
|
||||||
const Chessboard = () => {
|
interface IProps {
|
||||||
|
gameState: IGameState;
|
||||||
|
handleLoadPgn: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
handleClearGame: () => void;
|
||||||
|
handleSavePgn: () => void;
|
||||||
|
handleSavePdf: () => void;
|
||||||
|
fileInputRef: RefObject<HTMLInputElement | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Chessboard = ({
|
||||||
|
gameState,
|
||||||
|
handleLoadPgn,
|
||||||
|
handleClearGame,
|
||||||
|
handleSavePgn,
|
||||||
|
handleSavePdf,
|
||||||
|
fileInputRef,
|
||||||
|
}: IProps) => {
|
||||||
return (
|
return (
|
||||||
<main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]">
|
<main className="px-4 pb-8 place-self-center min-h-[calc(100vh-226px)]">
|
||||||
<Section title="Convert PGN to PDF">
|
<Section title="Convert PGN to PDF">
|
||||||
<LichessButton /> or{" "}
|
<LichessButton /> or{" "}
|
||||||
<input type="file" id="file-input" className="file-input max-w-xs" />
|
<input
|
||||||
|
type="file"
|
||||||
|
id="file-input"
|
||||||
|
ref={fileInputRef}
|
||||||
|
className="file-input max-w-xs"
|
||||||
|
onChange={handleLoadPgn}
|
||||||
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<PgnViewerComponent pgn="" />
|
<div className="flex gap-4">
|
||||||
|
<button
|
||||||
|
className="cursor-pointer"
|
||||||
|
type="button"
|
||||||
|
onClick={handleClearGame}
|
||||||
|
>
|
||||||
|
Clear Game
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="cursor-pointer"
|
||||||
|
type="button"
|
||||||
|
onClick={handleSavePgn}
|
||||||
|
>
|
||||||
|
Save PGN
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="cursor-pointer"
|
||||||
|
type="button"
|
||||||
|
onClick={handleSavePdf}
|
||||||
|
>
|
||||||
|
Save PDF
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<PgnViewer gamePgn={gameState.pgn || ""} />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
interface IDiagrams {
|
||||||
|
ply: number;
|
||||||
|
fen: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGameState {
|
||||||
|
pgn: string;
|
||||||
|
headers: string;
|
||||||
|
diagrams: IDiagrams[];
|
||||||
|
diagramClock: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameAction =
|
||||||
|
| { type: "SET_GAME"; payload: { pgn: string; headers: "" } }
|
||||||
|
| { type: "CLEAR_GAME" };
|
||||||
|
|
||||||
|
export const initialGameState = {
|
||||||
|
pgn: "",
|
||||||
|
headers: "",
|
||||||
|
diagrams: [],
|
||||||
|
diagramClock: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const gameReducer = (state: IGameState, action: GameAction) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case "SET_GAME":
|
||||||
|
return {
|
||||||
|
pgn: action.payload.pgn,
|
||||||
|
headers: "",
|
||||||
|
diagrams: [],
|
||||||
|
diagramClock: false,
|
||||||
|
};
|
||||||
|
case "CLEAR_GAME":
|
||||||
|
return initialGameState;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user