diff --git a/src/components/ChessGame/ChessGame.tsx b/src/components/ChessGame/ChessGame.tsx index 3309e614..db069a88 100644 --- a/src/components/ChessGame/ChessGame.tsx +++ b/src/components/ChessGame/ChessGame.tsx @@ -22,8 +22,8 @@ const ChessGame = ({ id, type }: ChessGameType) => { moves, playing, customSquares, - gameFen, - setGameFen, + // gameFen, + // setGameFen, onPieceDrop, undoMove, startGame, @@ -65,7 +65,7 @@ const ChessGame = ({ id, type }: ChessGameType) => { id={id} onPieceDrop={onPieceDrop} // boardOrientation="black" TODO: make the bot play 1st, or just ignore, bot ALWAYS 'black'. - position={gameFen} + position={game.fen()} customBoardStyle={{ borderRadius: "8px", boxShadow: "0 2px 10px rgba(0, 0, 0, 0.5)", @@ -81,7 +81,7 @@ const ChessGame = ({ id, type }: ChessGameType) => {
{playing ? ( ) : ( diff --git a/src/hooks/useChessSocket.tsx b/src/hooks/useChessSocket.tsx index 1c2d473c..3a1ecd03 100644 --- a/src/hooks/useChessSocket.tsx +++ b/src/hooks/useChessSocket.tsx @@ -3,6 +3,7 @@ import React from "react"; import { Chess, Move, Square } from "chess.js"; import { io, Socket } from "socket.io-client"; import { CustomSquares, ShortMove } from "@/types"; +import { useLocalStorage } from "./useLocalStorage"; export type ChessType = "random" | "computer" | "minimax"; @@ -65,6 +66,22 @@ const useChessSocket = ({ type, id }: Props) => { } }, [disconnectSocket, setConnectionStatus, socket]); + React.useEffect(() => { + // Auto open socket connection on page load + connectSocket(); + const savedMoves = localStorage.getItem(`@stockchess/useLocalStorage/ComputerChessBoard-moves`) + if (savedMoves) { + const parsedSavedMoves = JSON.parse(savedMoves) as Move[]; + const newGame = new Chess(); + try { + parsedSavedMoves.forEach((move) => newGame.move(move)); + } catch (error) { + console.log("[!!!] Error in initializing saved game: ", error); + } + setGame(newGame); + } + }, []); + // Send latest move over socket const sendMove = (move: string) => { console.log("MOVE: ", move); @@ -93,9 +110,17 @@ const useChessSocket = ({ type, id }: Props) => { // End of socket const [game, setGame] = useState(new Chess()); - const [playing, setPlaying] = useState(false); - const [moves, setMoves] = useState([]); - const [gameFen, setGameFen] = useState(game.fen()); + // const [playing, setPlaying] = useState(false); + const [playing, setPlaying] = useLocalStorage({ + name: `${id}-is-playing`, + defaultValue: false, + }); + const [moves, setMoves] = useLocalStorage({ + name: `${id}-moves`, + defaultValue: [], + }); + // const [moves, setMoves] = useState([]); + // const [gameFen, setGameFen] = useState("start"); const [currentTimeout, setCurrentTimeout] = useState(); const [customSquares, updateCustomSquares] = useReducer(squareReducer, { check: {}, @@ -107,9 +132,9 @@ const useChessSocket = ({ type, id }: Props) => { const result = gameCopy.move(move); if (result) { - setMoves((prevMoves) => [result, ...prevMoves]); + setMoves(gameCopy.history({verbose: true})); setGame(gameCopy); - setGameFen(gameCopy.fen()); + // setGameFen(gameCopy.fen()); let kingSquare = undefined; if (game.inCheck()) { @@ -156,7 +181,7 @@ const useChessSocket = ({ type, id }: Props) => { const gameCopy = game; gameCopy.reset(); setGame(gameCopy); - setGameFen(gameCopy.fen()); + // setGameFen(gameCopy.fen()); cleanOldGame(); setMoves([]); @@ -166,7 +191,8 @@ const useChessSocket = ({ type, id }: Props) => { }; const startGame = () => { - connectSocket(); + // NOTE: I think it's better to init socket on page load + // connectSocket(); resetGame(); setPlaying(true); }; @@ -183,8 +209,7 @@ const useChessSocket = ({ type, id }: Props) => { movesCopy.shift(); setGame(gameCopy); - setGameFen(gameCopy.fen()); - setMoves(movesCopy); + // setGameFen(gameCopy.fen()); updateCustomSquares({ check: undefined }); // Reset style }; @@ -195,8 +220,9 @@ const useChessSocket = ({ type, id }: Props) => { customSquares, - gameFen, - setGameFen, + // NOTE: Since FEN can be easily extract from `games`, we dont need to maintain it + // gameFen, + // setGameFen, onPieceDrop, undoMove, diff --git a/src/hooks/useLocalStorage.tsx b/src/hooks/useLocalStorage.tsx new file mode 100644 index 00000000..c39aaf47 --- /dev/null +++ b/src/hooks/useLocalStorage.tsx @@ -0,0 +1,55 @@ +import { useState, useEffect } from 'react'; + +type Args = { + name: string; + defaultValue: T; +}; + +export function useLocalStorage(args: Args): [T, (arg: T) => void, () => void] { + const { defaultValue, name } = args; + const [value, setValue] = useState(undefined); + const persistenceKey = `@stockchess/useLocalStorage/${name}`; + + useEffect(function didMount() { + if (isLocalStorageAvailable()) { + const persistedState = localStorage.getItem(persistenceKey); + if (persistedState) { + setValue(JSON.parse(persistedState)); + } else { + // setValue(defaultValue); + // setValue() + } + } + }, []); + + useEffect( + function persistOnChange() { + if (isLocalStorageAvailable() && value !== undefined) + localStorage.setItem(persistenceKey, JSON.stringify(value)); + }, + [value] + ); + + function removeValue() { + if (isLocalStorageAvailable()) localStorage.removeItem(persistenceKey); + } + + return [value ?? defaultValue, setValue, removeValue]; +} + +export function isLocalStorageAvailable(): boolean { + try { + if (!window.localStorage || localStorage === null || typeof localStorage === 'undefined') { + return false; + } + + localStorage.setItem('localStorage:test', 'value'); + if (localStorage.getItem('localStorage:test') !== 'value') { + return false; + } + localStorage.removeItem('localStorage:test'); + return true; + } catch { + return false; + } +}