From 98b4bf92c64b12b79ba7eaf9a8d8d4923e31937b Mon Sep 17 00:00:00 2001 From: ale Date: Fri, 5 Dec 2025 23:40:41 +0100 Subject: [PATCH] refactor cve Signed-off-by: ale --- app/api/socket/route.ts | 7 ++++-- components/GameBoard.tsx | 47 +++++++++++++++++++++------------------- components/Lobby.tsx | 4 ++-- lib/gameLogic.ts | 2 +- lib/socket-server.ts | 2 +- package.json | 6 ++--- tsconfig.json | 16 ++++++++++---- 7 files changed, 49 insertions(+), 35 deletions(-) diff --git a/app/api/socket/route.ts b/app/api/socket/route.ts index a371555..a01a907 100644 --- a/app/api/socket/route.ts +++ b/app/api/socket/route.ts @@ -57,12 +57,14 @@ function startGame(roomId: string): GameState { } export async function GET(req: NextRequest) { - // @ts-ignore + // @ts-expect-error - NextRequest extended with socket server const res = req.res || req.nextUrl; + // eslint-disable-next-line @typescript-eslint/no-explicit-any if (!(res as any).socket?.server?.io) { console.log('Initializing Socket.IO server...'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const httpServer: HTTPServer = (res as any).socket.server; const io = new SocketIOServer(httpServer, { path: '/api/socket', @@ -167,7 +169,7 @@ export async function GET(req: NextRequest) { gameRooms.set(roomId, newGameState); io.to(roomId).emit('game-state-updated', newGameState); - } catch (error) { + } catch { socket.emit('invalid-move', 'Invalid move'); } }); @@ -220,6 +222,7 @@ export async function GET(req: NextRequest) { }); }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any (res as any).socket.server.io = io; } diff --git a/components/GameBoard.tsx b/components/GameBoard.tsx index ae74d5c..680aa74 100644 --- a/components/GameBoard.tsx +++ b/components/GameBoard.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useRef, useEffect, useState } from 'react'; +import React, { useRef, useEffect, useState, useMemo } from 'react'; import { PlacedTile, Position } from '@/lib/types'; interface GameBoardProps { @@ -17,7 +17,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = const [isDragging, setIsDragging] = useState(false); const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); const [canvasSize, setCanvasSize] = useState({ width, height }); - const [zoom, setZoom] = useState(1); + const initialZoom = 1; // Handle responsive canvas sizing useEffect(() => { @@ -37,8 +37,8 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = }, [width, height]); // Auto-zoom and auto-center to fit all tiles - useEffect(() => { - if (placedTiles.length === 0) return; + const autoFitValues = useMemo(() => { + if (placedTiles.length === 0) return null; // Calculate bounding box of all tiles let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; @@ -65,7 +65,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = const contentHeight = maxY - minY; // Calculate zoom to fit all tiles in viewport - const isMobile = window.innerWidth < 640; + const isMobile = typeof window !== 'undefined' && window.innerWidth < 640; const zoomX = canvasSize.width / contentWidth; const zoomY = canvasSize.height / contentHeight; @@ -83,10 +83,13 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = const newOffsetX = canvasSize.width / 2 - centerX * newZoom; const newOffsetY = canvasSize.height / 2 - centerY * newZoom; - setZoom(newZoom); - setOffset({ x: newOffsetX, y: newOffsetY }); + return { zoom: newZoom, offset: { x: newOffsetX, y: newOffsetY } }; }, [placedTiles, canvasSize.width, canvasSize.height]); + // Derive effective zoom and offset values + const effectiveZoom = autoFitValues?.zoom ?? initialZoom; + const effectiveOffset = autoFitValues?.offset ?? offset; + useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; @@ -118,27 +121,27 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = } // Draw placed tiles - placedTiles.forEach((placedTile, index) => { + placedTiles.forEach((placedTile) => { const { tile, position, orientation } = placedTile; const tileWidth = orientation === 'horizontal' ? 60 : 30; const tileHeight = orientation === 'horizontal' ? 30 : 60; - const x = position.x * zoom + offset.x; - const y = position.y * zoom + offset.y; - const scaledWidth = tileWidth * zoom; - const scaledHeight = tileHeight * zoom; + const x = position.x * effectiveZoom + effectiveOffset.x; + const y = position.y * effectiveZoom + effectiveOffset.y; + const scaledWidth = tileWidth * effectiveZoom; + const scaledHeight = tileHeight * effectiveZoom; // Draw tile background with shadow ctx.shadowColor = 'rgba(0, 0, 0, 0.2)'; - ctx.shadowBlur = 5 * zoom; - ctx.shadowOffsetX = 2 * zoom; - ctx.shadowOffsetY = 2 * zoom; + ctx.shadowBlur = 5 * effectiveZoom; + ctx.shadowOffsetX = 2 * effectiveZoom; + ctx.shadowOffsetY = 2 * effectiveZoom; ctx.fillStyle = '#ffffff'; ctx.strokeStyle = '#1f2937'; - ctx.lineWidth = 2 * zoom; + ctx.lineWidth = 2 * effectiveZoom; - const radius = 4 * zoom; + const radius = 4 * effectiveZoom; ctx.beginPath(); ctx.moveTo(x + radius, y); ctx.lineTo(x + scaledWidth - radius, y); @@ -158,7 +161,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = // Draw center divider ctx.strokeStyle = '#6b7280'; - ctx.lineWidth = 1 * zoom; + ctx.lineWidth = 1 * effectiveZoom; ctx.beginPath(); if (orientation === 'horizontal') { ctx.moveTo(x + scaledWidth / 2, y); @@ -170,7 +173,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = ctx.stroke(); // Draw dots - const dotRadius = Math.max(2, 2.5 * zoom); + const dotRadius = Math.max(2, 2.5 * effectiveZoom); ctx.fillStyle = '#1f2937'; const drawDots = (value: number, dotX: number, dotY: number, size: number) => { @@ -186,7 +189,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = const leftX = x + scaledWidth / 4; const rightX = x + (scaledWidth * 3) / 4; const centerY = y + scaledHeight / 2; - const dotAreaSize = (scaledWidth / 2 - 6 * zoom); + const dotAreaSize = (scaledWidth / 2 - 6 * effectiveZoom); drawDots(tile.left, leftX, centerY, dotAreaSize); drawDots(tile.right, rightX, centerY, dotAreaSize); @@ -194,14 +197,14 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className = const topY = y + scaledHeight / 4; const bottomY = y + (scaledHeight * 3) / 4; const centerX = x + scaledWidth / 2; - const dotAreaSize = (scaledHeight / 2 - 6 * zoom); + const dotAreaSize = (scaledHeight / 2 - 6 * effectiveZoom); drawDots(tile.left, centerX, topY, dotAreaSize); drawDots(tile.right, centerX, bottomY, dotAreaSize); } }); - }, [placedTiles, offset, zoom, canvasSize.width, canvasSize.height]); + }, [placedTiles, effectiveOffset, effectiveZoom, canvasSize.width, canvasSize.height]); const handleMouseDown = (e: React.MouseEvent) => { setIsDragging(true); diff --git a/components/Lobby.tsx b/components/Lobby.tsx index 43835c3..a79f691 100644 --- a/components/Lobby.tsx +++ b/components/Lobby.tsx @@ -7,10 +7,10 @@ interface LobbyProps { onCreateRoom: (playerName: string) => void; onJoinRoom: (roomId: string, playerName: string) => void; onStartAI: (playerName: string) => void; - roomId: string | null; + roomId?: string | null; } -export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProps) { +export function Lobby({ onCreateRoom, onJoinRoom, onStartAI }: LobbyProps) { const [playerName, setPlayerName] = useState(''); const [joinRoomId, setJoinRoomId] = useState(''); const [mode, setMode] = useState<'menu' | 'create' | 'join' | 'ai'>('menu'); diff --git a/lib/gameLogic.ts b/lib/gameLogic.ts index 1ffad2c..578f5d1 100644 --- a/lib/gameLogic.ts +++ b/lib/gameLogic.ts @@ -151,7 +151,7 @@ export function calculateTilePosition( const lastTile = side === 'right' ? board[board.length - 1] : board[0]; let position: Position; let orientation: 'horizontal' | 'vertical' = 'horizontal'; - let rotation = 0; + const rotation = 0; if (side === 'right') { const offset = lastTile.orientation === 'horizontal' ? tileWidth : tileHeight; diff --git a/lib/socket-server.ts b/lib/socket-server.ts index 6ca8fde..db675f8 100644 --- a/lib/socket-server.ts +++ b/lib/socket-server.ts @@ -163,7 +163,7 @@ io.on('connection', (socket) => { gameRooms.set(roomId, newGameState); io.to(roomId).emit('game-state-updated', newGameState); - } catch (error) { + } catch { socket.emit('invalid-move', 'Invalid move'); } }); diff --git a/package.json b/package.json index 4f6d4ca..795a1ec 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "framer-motion": "^12.23.24", - "next": "16.0.1", - "react": "19.2.0", - "react-dom": "19.2.0", + "next": "15.4.8", + "react": "19.1.2", + "react-dom": "19.1.2", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", "zustand": "^5.0.8" diff --git a/tsconfig.json b/tsconfig.json index 3a13f90..2627c60 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { @@ -19,7 +23,9 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, "include": [ @@ -30,5 +36,7 @@ ".next/dev/types/**/*.ts", "**/*.mts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] }