small structure changes for less redundancy

This commit is contained in:
Nathaniel Tampus
2023-03-12 13:29:06 +08:00
parent fb43d2d9c7
commit e2a0f361cf
5 changed files with 183 additions and 192 deletions

View File

@@ -1,20 +1,26 @@
"use client"; "use client";
// TODO: restructure // TODO: restructure
import { Chessboard } from "react-chessboard";
import { IconCopy } from "@tabler/icons-react"; import { IconCopy } from "@tabler/icons-react";
import { useState, useEffect, useContext, useReducer, useRef } from "react";
import type { KeyboardEvent, FormEvent } from "react";
//import Image from "next/image";
import type { Game } from "@chessu/types";
import type { Message } from "@/types";
import { io } from "socket.io-client"; import type { FormEvent, KeyboardEvent } from "react";
import { API_URL } from "@/config";
import { SessionContext } from "@/context/session"; import { SessionContext } from "@/context/session";
import { useContext, useEffect, useReducer, useRef, useState } from "react";
import type { Message } from "@/types";
import type { Game } from "@chessu/types";
import type { Move, Square } from "chess.js";
import { Chess } from "chess.js"; import { Chess } from "chess.js";
import type { Square, Move } from "chess.js"; import { Chessboard } from "react-chessboard";
import { initSocket, lobbyReducer, squareReducer } from "./handlers";
import { API_URL } from "@/config";
import { io } from "socket.io-client";
import { lobbyReducer, squareReducer } from "./reducers";
import { initSocket } from "./socketEvents";
import { syncPgn, syncSide } from "./utils";
const socket = io(API_URL, { withCredentials: true, autoConnect: false }); const socket = io(API_URL, { withCredentials: true, autoConnect: false });
@@ -52,45 +58,10 @@ export default function GamePage({ initialLobby }: { initialLobby: Game }) {
handleResize(); handleResize();
if (lobby.pgn && lobby.actualGame.pgn() !== lobby.pgn) { if (lobby.pgn && lobby.actualGame.pgn() !== lobby.pgn) {
lobby.actualGame.loadPgn(lobby.pgn as string); syncPgn(lobby.pgn, lobby, { updateCustomSquares });
const lastMove = lobby.actualGame.history({ verbose: true }).pop();
let lastMoveSquares = undefined;
let kingSquare = undefined;
if (lastMove) {
lastMoveSquares = {
[lastMove.from]: { background: "rgba(255, 255, 0, 0.4)" },
[lastMove.to]: { background: "rgba(255, 255, 0, 0.4)" }
};
}
if (lobby.actualGame.inCheck()) {
const kingPos = lobby.actualGame.board().reduce((acc, row, index) => {
const squareIndex = row.findIndex(
(square) => square && square.type === "k" && square.color === lobby.actualGame.turn()
);
return squareIndex >= 0 ? `${String.fromCharCode(squareIndex + 97)}${8 - index}` : acc;
}, "");
kingSquare = {
[kingPos]: {
background: "radial-gradient(red, rgba(255,0,0,.4), transparent 70%)",
borderRadius: "50%"
}
};
}
updateCustomSquares({
lastMove: lastMoveSquares,
check: kingSquare
});
} }
if (lobby.black?.id === session?.user?.id) { syncSide(session.user, undefined, lobby, { updateLobby });
if (lobby.side !== "b") updateLobby({ type: "setSide", payload: "b" });
} else if (lobby.white?.id === session?.user?.id) {
if (lobby.side !== "w") updateLobby({ type: "setSide", payload: "w" });
} else if (lobby.side !== "s") {
updateLobby({ type: "setSide", payload: "s" });
}
initSocket(session.user, socket, lobby, { initSocket(session.user, socket, lobby, {
updateLobby, updateLobby,

View File

@@ -1,145 +0,0 @@
import type { Dispatch } from "react";
import type { Action, Lobby, Message, CustomSquares } from "@/types";
import { Chess } from "chess.js";
import type { Game, User } from "@chessu/types";
import type { Socket } from "socket.io-client";
export function lobbyReducer(lobby: Lobby, action: Action): Lobby {
switch (action.type) {
case "updateLobby":
return { ...lobby, ...action.payload };
case "setSide":
return { ...lobby, side: action.payload };
case "setGame":
return { ...lobby, actualGame: action.payload };
default:
throw new Error("Invalid action type");
}
}
export function squareReducer(squares: CustomSquares, action: Partial<CustomSquares>) {
return { ...squares, ...action };
}
export function initSocket(
user: User,
socket: Socket,
lobby: Lobby,
actions: {
updateLobby: Dispatch<Action>;
addMessage: Function;
updateCustomSquares: Dispatch<Partial<CustomSquares>>;
makeMove: Function;
}
) {
socket.on("connect", () => {
console.log("connected!");
socket.emit("joinLobby", lobby.code);
});
socket.on("disconnect", () => {
console.log("disconnected!");
});
// TODO: handle disconnect
socket.on("chat", (message: Message) => {
actions.addMessage(message);
});
socket.on("receivedLatestGame", (latestGame: Game) => {
if (latestGame.pgn && latestGame.pgn !== lobby.actualGame.pgn()) {
lobby.actualGame.loadPgn(latestGame.pgn as string);
const lastMove = lobby.actualGame.history({ verbose: true }).pop();
let lastMoveSquares = undefined;
let kingSquare = undefined;
if (lastMove) {
lastMoveSquares = {
[lastMove.from]: { background: "rgba(255, 255, 0, 0.4)" },
[lastMove.to]: { background: "rgba(255, 255, 0, 0.4)" }
};
}
if (lobby.actualGame.inCheck()) {
const kingPos = lobby.actualGame.board().reduce((acc, row, index) => {
const squareIndex = row.findIndex(
(square) =>
square &&
square.type === "k" &&
square.color === lobby.actualGame.turn()
);
return squareIndex >= 0
? `${String.fromCharCode(squareIndex + 97)}${8 - index}`
: acc;
}, "");
kingSquare = {
[kingPos]: {
background: "radial-gradient(red, rgba(255,0,0,.4), transparent 70%)",
borderRadius: "50%"
}
};
}
actions.updateCustomSquares({
lastMove: lastMoveSquares,
check: kingSquare
});
}
actions.updateLobby({ type: "updateLobby", payload: latestGame });
if (latestGame.black?.id === user?.id) {
if (lobby.side !== "b") actions.updateLobby({ type: "setSide", payload: "b" });
} else if (latestGame.white?.id === user?.id) {
if (lobby.side !== "w") actions.updateLobby({ type: "setSide", payload: "w" });
} else if (lobby.side !== "s") {
actions.updateLobby({ type: "setSide", payload: "s" });
}
});
socket.on("receivedMove", (m: { from: string; to: string; promotion?: string }) => {
const success = actions.makeMove(m);
if (!success) {
socket.emit("getLatestGame");
}
});
socket.on("userJoinedAsPlayer", ({ name, side }: { name: string; side: "white" | "black" }) => {
actions.addMessage({
author: { name: "server" },
message: `${name} is now playing as ${side}.`
});
});
socket.on(
"gameOver",
({
reason,
winnerName,
winnerSide
}: {
reason: string;
winnerName?: string;
winnerSide?: string;
}) => {
const m = {
author: { name: "server" }
} as Message;
if (reason === "checkmate") {
m.message = `${winnerName} (${winnerSide}) has won by checkmate.`;
} else {
let message = "The game has ended in a draw";
if (reason === "repetition") {
message = message.concat(" due to threefold repetition");
} else if (reason === "insufficient") {
message = message.concat(" due to insufficient material");
} else if (reason === "stalemate") {
message = "The game has been drawn due to stalemate";
}
m.message = message.concat(".");
}
actions.addMessage(m);
}
);
}

View File

@@ -0,0 +1,21 @@
import type { Action, CustomSquares, Lobby } from "@/types";
export function lobbyReducer(lobby: Lobby, action: Action): Lobby {
switch (action.type) {
case "updateLobby":
return { ...lobby, ...action.payload };
case "setSide":
return { ...lobby, side: action.payload };
case "setGame":
return { ...lobby, actualGame: action.payload };
default:
throw new Error("Invalid action type");
}
}
export function squareReducer(squares: CustomSquares, action: Partial<CustomSquares>) {
return { ...squares, ...action };
}

View File

@@ -0,0 +1,85 @@
import type { Action, CustomSquares, Lobby, Message } from "@/types";
import type { Game, User } from "@chessu/types";
import type { Dispatch } from "react";
import type { Socket } from "socket.io-client";
import { syncPgn, syncSide } from "./utils";
export function initSocket(
user: User,
socket: Socket,
lobby: Lobby,
actions: {
updateLobby: Dispatch<Action>;
addMessage: Function;
updateCustomSquares: Dispatch<Partial<CustomSquares>>;
makeMove: Function;
}
) {
socket.on("connect", () => {
console.log("connected!");
socket.emit("joinLobby", lobby.code);
});
socket.on("disconnect", () => {
console.log("disconnected!");
});
// TODO: handle disconnect
socket.on("chat", (message: Message) => {
actions.addMessage(message);
});
socket.on("receivedLatestGame", (latestGame: Game) => {
if (latestGame.pgn && latestGame.pgn !== lobby.actualGame.pgn()) {
syncPgn(latestGame.pgn, lobby, actions);
}
actions.updateLobby({ type: "updateLobby", payload: latestGame });
syncSide(user, latestGame, lobby, actions);
});
socket.on("receivedMove", (m: { from: string; to: string; promotion?: string }) => {
const success = actions.makeMove(m);
if (!success) {
socket.emit("getLatestGame");
}
});
socket.on("userJoinedAsPlayer", ({ name, side }: { name: string; side: "white" | "black" }) => {
actions.addMessage({
author: { name: "server" },
message: `${name} is now playing as ${side}.`
});
});
socket.on(
"gameOver",
({
reason,
winnerName,
winnerSide
}: {
reason: string;
winnerName?: string;
winnerSide?: string;
}) => {
const m = {
author: { name: "server" }
} as Message;
if (reason === "checkmate") {
m.message = `${winnerName} (${winnerSide}) has won by checkmate.`;
} else {
let message = "The game has ended in a draw";
if (reason === "repetition") {
message = message.concat(" due to threefold repetition");
} else if (reason === "insufficient") {
message = message.concat(" due to insufficient material");
} else if (reason === "stalemate") {
message = "The game has been drawn due to stalemate";
}
m.message = message.concat(".");
}
actions.addMessage(m);
}
);
}

View File

@@ -0,0 +1,59 @@
import type { Action, CustomSquares, Lobby } from "@/types";
import type { Game, User } from "@chessu/types";
import type { Dispatch } from "react";
export const syncPgn = (
latestPgn: string,
lobby: Lobby,
actions: {
updateCustomSquares: Dispatch<Partial<CustomSquares>>;
}
) => {
lobby.actualGame.loadPgn(latestPgn as string);
const lastMove = lobby.actualGame.history({ verbose: true }).pop();
let lastMoveSquares = undefined;
let kingSquare = undefined;
if (lastMove) {
lastMoveSquares = {
[lastMove.from]: { background: "rgba(255, 255, 0, 0.4)" },
[lastMove.to]: { background: "rgba(255, 255, 0, 0.4)" }
};
}
if (lobby.actualGame.inCheck()) {
const kingPos = lobby.actualGame.board().reduce((acc, row, index) => {
const squareIndex = row.findIndex(
(square) =>
square && square.type === "k" && square.color === lobby.actualGame.turn()
);
return squareIndex >= 0 ? `${String.fromCharCode(squareIndex + 97)}${8 - index}` : acc;
}, "");
kingSquare = {
[kingPos]: {
background: "radial-gradient(red, rgba(255,0,0,.4), transparent 70%)",
borderRadius: "50%"
}
};
}
actions.updateCustomSquares({
lastMove: lastMoveSquares,
check: kingSquare
});
};
export const syncSide = (
user: User,
game: Game | undefined,
lobby: Lobby,
actions: { updateLobby: Dispatch<Action> }
) => {
if (!game) game = lobby;
if (game.black?.id === user?.id) {
if (lobby.side !== "b") actions.updateLobby({ type: "setSide", payload: "b" });
} else if (game.white?.id === user?.id) {
if (lobby.side !== "w") actions.updateLobby({ type: "setSide", payload: "w" });
} else if (lobby.side !== "s") {
actions.updateLobby({ type: "setSide", payload: "s" });
}
};