feat(chess): add localStorage auto-save and restore (P3.14)
This commit is contained in:
parent
3fdeb5822d
commit
a75b6f041b
4 changed files with 43 additions and 2 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { useEffect } from 'react'
|
||||
import { Routes, Route, useNavigate } from 'react-router-dom'
|
||||
import { GameView } from '../ui/GameView'
|
||||
import { RulesView } from '../ui/RulesView'
|
||||
|
|
@ -5,11 +6,23 @@ import { SavePanel } from '../ui/SavePanel'
|
|||
import { ImportExport } from '../ui/ImportExport'
|
||||
import { useChessEngine } from '../hooks/useChessEngine'
|
||||
import { ChessEngine } from '../engine'
|
||||
import { loadAutoSave } from '../persist/autosave.js'
|
||||
import type { AttrKey, FactValue, EntityId } from '@paratype/rete'
|
||||
|
||||
export function App() {
|
||||
const chessState = useChessEngine()
|
||||
|
||||
|
||||
// Restore autosave on mount — [] deps is intentional (run once, avoid infinite loop)
|
||||
useEffect(() => {
|
||||
const saved = loadAutoSave();
|
||||
if (!saved) return;
|
||||
const newEngine = new ChessEngine();
|
||||
const oldFacts = newEngine.session.allFacts();
|
||||
for (const f of oldFacts) newEngine.session.retract(f.id, f.attr as AttrKey);
|
||||
for (const f of saved) newEngine.session.insert(f.id as EntityId, f.attr as AttrKey, f.value as FactValue);
|
||||
chessState.loadEngine(newEngine);
|
||||
}, []); // intentional: run once on mount, chessState ref is stable
|
||||
|
||||
return (
|
||||
<div data-testid="app-root">
|
||||
<Routes>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useState, useRef, useCallback } from 'react';
|
||||
import { ChessEngine, type GameResult } from '../engine';
|
||||
import type { PieceType } from '../schema';
|
||||
import { saveAutoSave } from '../persist/autosave.js';
|
||||
|
||||
export function useChessEngine() {
|
||||
const engineRef = useRef<ChessEngine | null>(null);
|
||||
|
|
@ -19,6 +20,7 @@ export function useChessEngine() {
|
|||
const loadEngine = useCallback((newEngine: ChessEngine) => {
|
||||
engineRef.current = newEngine;
|
||||
moveHistoryRef.current = [];
|
||||
saveAutoSave(newEngine.session.allFacts());
|
||||
setTick(t => t + 1);
|
||||
}, []);
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ export function useChessEngine() {
|
|||
if (move) {
|
||||
moveHistoryRef.current.push({ from, to, promoteTo });
|
||||
const result = engine.applyMove(move, promoteTo);
|
||||
saveAutoSave(engine.session.allFacts());
|
||||
setTick(t => t + 1); // trigger re-render
|
||||
return result;
|
||||
}
|
||||
|
|
@ -60,6 +63,7 @@ export function useChessEngine() {
|
|||
}
|
||||
}
|
||||
engineRef.current = newEngine;
|
||||
saveAutoSave(newEngine.session.allFacts());
|
||||
setTick(t => t + 1);
|
||||
}
|
||||
}, []);
|
||||
|
|
|
|||
23
packages/chess/src/persist/autosave.ts
Normal file
23
packages/chess/src/persist/autosave.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { exportGame, importGame } from './io.js';
|
||||
|
||||
const AUTOSAVE_KEY = 'paratype-chess:v1:autosave';
|
||||
|
||||
export function saveAutoSave(facts: Array<{ id: number; attr: string; value: unknown }>): void {
|
||||
try {
|
||||
localStorage.setItem(AUTOSAVE_KEY, exportGame(facts));
|
||||
} catch { /* ignore quota errors */ }
|
||||
}
|
||||
|
||||
export function loadAutoSave(): Array<{ id: number; attr: string; value: unknown }> | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(AUTOSAVE_KEY);
|
||||
if (!raw) return null;
|
||||
return importGame(raw);
|
||||
} catch {
|
||||
return null; // silently discard invalid/stale/version-mismatched data
|
||||
}
|
||||
}
|
||||
|
||||
export function clearAutoSave(): void {
|
||||
localStorage.removeItem(AUTOSAVE_KEY);
|
||||
}
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*", "tests/**/*"]
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue