From 8436cdbcdde511ab0670cc5a3c255290a6e174c8 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Thu, 4 Dec 2025 19:56:46 +0530 Subject: [PATCH] refactor(game): unify move handling using typed payloads and remove UI-driven handlers - Removed onCellClick from TicTacToeGameProps and migrated move sending inside TicTacToeGame - Updated TicTacToeGame to: - import TicTacToePayload - use movePayload() builder - send moves using handleMove() with matchId + sendMatchData - remove old matchId destructuring duplication - Updated BattleshipGame to: - import BattleshipPayload - use placePayload() and shootPayload() helpers - collapse place and shoot handlers into a single handleMove() - send typed payloads instead of raw objects - Updated App.tsx: - Removed handleCellClick and no longer pass onCellClick down - Created typed ticTacToeProps and battleshipProps without UI callbacks - Cleaned unused state and simplified board rendering - Use {...commonProps} to propagate shared game state - Updated props: - Removed TicTacToeGameProps.onCellClick - BattleshipGameProps continues to extend GameProps - Removed duplicate MatchDataModel definition from interfaces/models - Fixed imports to use revised models and payload types This refactor completes the transition from UI-triggered handlers to typed action payloads per game, significantly improving type safety, consistency, and separation of concerns. --- src/App.tsx | 10 ------ src/games/battleship/BattleshipGame.tsx | 41 +++++++++++-------------- src/games/battleship/models.ts | 15 +++++++++ src/games/battleship/utils.ts | 22 +++++++++++++ src/games/tictactoe/TicTacToeGame.tsx | 18 ++++++++--- src/games/tictactoe/models.ts | 11 +++++++ src/games/tictactoe/props.ts | 2 +- src/games/tictactoe/utils.ts | 12 ++++++++ src/interfaces/models.ts | 6 ---- 9 files changed, 92 insertions(+), 45 deletions(-) create mode 100644 src/games/battleship/models.ts create mode 100644 src/games/battleship/utils.ts create mode 100644 src/games/tictactoe/models.ts create mode 100644 src/games/tictactoe/utils.ts diff --git a/src/App.tsx b/src/App.tsx index 7d09888..8929fff 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,7 +35,6 @@ export default function App() { }; const ticTacToeProps: TicTacToeGameProps = { ...commonProps, - onCellClick: handleCellClick, }; const battleshipProps: BattleshipGameProps = { ...commonProps, @@ -104,15 +103,6 @@ export default function App() { onMatchData(onMatchDataCallback); }, [onMatchData]); - // ------------------------------------------ - // SEND A MOVE - // ------------------------------------------ - function handleCellClick(row: number, col: number) { - if (!matchId) return; - - sendMatchData(matchId, 1, { data: { row, col } }); - } - // --------------------------------------------------- // UI LAYOUT // --------------------------------------------------- diff --git a/src/games/battleship/BattleshipGame.tsx b/src/games/battleship/BattleshipGame.tsx index 78d151c..3b1aa29 100644 --- a/src/games/battleship/BattleshipGame.tsx +++ b/src/games/battleship/BattleshipGame.tsx @@ -5,6 +5,11 @@ import { useNakama } from "../../providers/NakamaProvider"; import PlacementGrid from "./placement/PlacementGrid"; import ShotGrid from "./battle/ShotGrid"; import { BattleshipGameProps } from "./props"; +import { BattleshipPayload } from "./models"; +import { + placePayload, + shootPayload, +} from "./utils"; const Fleet: Record = { carrier: 5, @@ -40,28 +45,10 @@ export default function BattleshipGame({ const nextShip = FLEET_ORDER[placed] || null; const nextShipSize = nextShip ? Fleet[nextShip] : null; - // ------------------- PLACE SHIP ------------------- - function handlePlace(ship: string, r: number, c: number, dir: "h" | "v") { - sendMatchData(matchId!, 1, { - action: "place", - data: { - ship: ship, - row: r, - col: c, - dir, - } - }); - } + function handleMove(matchPayload: BattleshipPayload) { + if (!matchId) return; - // ------------------- SHOOT ------------------- - function handleShoot(r: number, c: number) { - sendMatchData(matchId!, 1, { - action: "shoot", - data: { - row: r, - col: c, - } - }); + sendMatchData(matchId!, 1, matchPayload); } // ------------------- STATUS LABEL ------------------- @@ -90,7 +77,11 @@ export default function BattleshipGame({ shipBoard={myShips} shipName={nextShip} shipSize={nextShipSize} - onPlace={handlePlace} + onPlace={( + s,r,c,d + ) => handleMove( + placePayload(s,r,c,d) + )} /> )} @@ -103,7 +94,11 @@ export default function BattleshipGame({ grid={myShots} isMyTurn={isMyTurn} gameOver={!!gameOver} - onShoot={handleShoot} + onShoot={( + r,c + ) => handleMove( + shootPayload(r,c) + )} />

Your Ships

diff --git a/src/games/battleship/models.ts b/src/games/battleship/models.ts new file mode 100644 index 0000000..e26d0b0 --- /dev/null +++ b/src/games/battleship/models.ts @@ -0,0 +1,15 @@ +import { + MatchDataModel, +} from '../../interfaces/models' + +export interface BattleshipPayload { + action: "place" | "shoot"; // extend as needed + data: { + ship?: string; // only for placement + row: number; + col: number; + dir?: "h" | "v"; + }; +} + +export type BattleshipMatchDataModel = MatchDataModel; diff --git a/src/games/battleship/utils.ts b/src/games/battleship/utils.ts new file mode 100644 index 0000000..a0b488e --- /dev/null +++ b/src/games/battleship/utils.ts @@ -0,0 +1,22 @@ +import { + BattleshipPayload +} from "./models"; + +export function placePayload( + ship: string, + row: number, + col: number, + dir: "h" | "v" +): BattleshipPayload { + return { + action: "place", + data: { ship, row, col, dir } + }; +} + +export function shootPayload(row: number, col: number): BattleshipPayload { + return { + action: "shoot", + data: { row, col } + }; +} diff --git a/src/games/tictactoe/TicTacToeGame.tsx b/src/games/tictactoe/TicTacToeGame.tsx index 0caab84..b75485e 100644 --- a/src/games/tictactoe/TicTacToeGame.tsx +++ b/src/games/tictactoe/TicTacToeGame.tsx @@ -4,6 +4,8 @@ import { useNakama } from "../../providers/NakamaProvider"; import getHaiku from "../../utils/haikus"; import { TicTacToeGameProps } from "./props"; +import { TicTacToePayload } from "./models"; +import { movePayload } from "./utils"; export default function TicTacToeGame({ boards, @@ -12,13 +14,11 @@ export default function TicTacToeGame({ gameOver, players, myUserId, - onCellClick, }: TicTacToeGameProps) { + const { sendMatchData, matchId } = useNakama(); + const myIndex = players.findIndex(p => p.user_id === myUserId); const gameReady = players.length === 2; - const { - matchId - } = useNakama(); const mySymbol = myIndex !== null && players[myIndex] @@ -67,6 +67,12 @@ export default function TicTacToeGame({ return () => clearTimeout(timer); }, [haikuIndex]); + function handleMove(matchPayload: TicTacToePayload) { + if (!matchId) return; + + sendMatchData(matchId!, 1, matchPayload); + } + return ( <> {matchId && ( @@ -132,7 +138,9 @@ export default function TicTacToeGame({ : {} } whileTap={!disabled ? { scale: 0.85 } : {}} - onClick={() => !disabled && onCellClick(rIdx, cIdx)} + onClick={() => !disabled && handleMove( + movePayload(rIdx, cIdx) + )} style={{ width: "80px", height: "80px", diff --git a/src/games/tictactoe/models.ts b/src/games/tictactoe/models.ts new file mode 100644 index 0000000..6713b1b --- /dev/null +++ b/src/games/tictactoe/models.ts @@ -0,0 +1,11 @@ +import { + MatchDataModel, +} from '../../interfaces/models' + +export interface TicTacToePayload { + data: { + row: number; + col: number; + }; +} +export type TicTacToeMatchDataModel = MatchDataModel; diff --git a/src/games/tictactoe/props.ts b/src/games/tictactoe/props.ts index 547fa00..7c0a263 100644 --- a/src/games/tictactoe/props.ts +++ b/src/games/tictactoe/props.ts @@ -4,5 +4,5 @@ import { export interface TicTacToeGameProps extends GameProps { - onCellClick: (row: number, col: number) => void; + // metadata: Record; } diff --git a/src/games/tictactoe/utils.ts b/src/games/tictactoe/utils.ts new file mode 100644 index 0000000..af889f6 --- /dev/null +++ b/src/games/tictactoe/utils.ts @@ -0,0 +1,12 @@ +import { + TicTacToePayload +} from "./models"; + +export function movePayload( + row: number, + col: number, +): TicTacToePayload { + return { + data: { row, col } + }; +} diff --git a/src/interfaces/models.ts b/src/interfaces/models.ts index f29644b..fad620d 100644 --- a/src/interfaces/models.ts +++ b/src/interfaces/models.ts @@ -19,9 +19,3 @@ export interface GameMetadataModel { game: string; mode: string; } - -export interface MatchDataModel { - opCode: number; - data: T; - userId: string | null; -}