From fd2d14442805c0f342042f476f5e91df9f7deee7 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 26 Apr 2026 19:27:11 +0700 Subject: [PATCH] refactor: harden loto game per code review - Validate localStorage shape on load (typed parse, guarded set) - Fix isRowComplete to require at least one number per row - Split row-scan effect into bingo + waiting passes so updates to later rows are not skipped by an early return - Memoize per-row completeness; was recomputing 81 times per render - Replace clickable divs with real ); })} @@ -174,6 +192,8 @@ export default function PlayerBoard({ storagePrefix = "loto" }: PlayerBoardProps {toast && (
@@ -191,8 +211,15 @@ export default function PlayerBoard({ storagePrefix = "loto" }: PlayerBoardProps {showCongrats && (
setShowCongrats(false)} + onKeyDown={(e) => { + if (e.key === "Escape") setShowCongrats(false); + }} + tabIndex={-1} >
-

+

Kinh!

diff --git a/web/app/master/page.tsx b/web/app/master/page.tsx index 2aa0f5f..36c616f 100644 --- a/web/app/master/page.tsx +++ b/web/app/master/page.tsx @@ -58,7 +58,10 @@ function loadState(): MasterState | null { } } -const BOARD = buildBoard(); +const BOARD: ReadonlyArray> = Object.freeze( + buildBoard().map((row) => Object.freeze(row)) +); +const BOARD_FLAT: ReadonlyArray = Object.freeze(BOARD.flatMap((r) => r)); export default function MasterPage() { const [state, setState] = useState(null); @@ -179,9 +182,12 @@ export default function MasterPage() { {/* Master board 9x10 */} {state ? ( -

+
- {BOARD.flat().map((num, idx) => { + {BOARD_FLAT.map((num, idx) => { const hasNumber = num > 0; const isCalled = hasNumber && calledSet.has(num); diff --git a/web/next-env.d.ts b/web/next-env.d.ts index c4b7818..9edff1c 100644 --- a/web/next-env.d.ts +++ b/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/web/next.config.ts b/web/next.config.ts index 2462dd4..b496f8c 100644 --- a/web/next.config.ts +++ b/web/next.config.ts @@ -20,7 +20,9 @@ function codeserverConfig() { } const cs = isCodeserver ? codeserverConfig() : null; -const basePath = cs?.basePath ?? (isProd ? "/loto" : ""); +// NEXT_BASE_PATH wins so custom-domain / fork deploys don't have to edit code. +const basePath = + process.env.NEXT_BASE_PATH ?? cs?.basePath ?? (isProd ? "/loto" : ""); const nextConfig: NextConfig = { output: "export",