import { useState, useEffect } from 'react' import { generateToken, loadState, saveState } from './api.js' import { createState } from './gameState.js' import Ledger from './screens/Ledger.jsx' import Map from './screens/Map.jsx' import './App.css' const TOKEN_KEY = 'otivm_token' export default function App() { const [state, setState] = useState(null) const [token, setToken] = useState(null) const [loading, setLoading] = useState(true) const [screen, setScreen] = useState('ledger') useEffect(() => { async function bootstrap() { let tok = localStorage.getItem(TOKEN_KEY) if (!tok) { tok = generateToken() localStorage.setItem(TOKEN_KEY, tok) } setToken(tok) const saved = await loadState(tok) if (saved) { setState(saved) } else { const fresh = createState(tok) setState(fresh) await saveState(tok, fresh) } setLoading(false) } bootstrap() }, []) async function onStateChange(newState) { setState(newState) await saveState(token, newState) } // Session abandonment — forward-looking lifecycle handler. // Does not delete the old save. Appends a terminal event so the record // is complete. The old save becomes a historical artefact on disk. // In the Simulator this event will have social and ecological consequences // for the clan — a Constructor who stops participating leaves a gap. // For now: mark abandoned, generate new token, bootstrap fresh in-place. async function onNewGame() { if (state && token) { const abandoned = { ...state, events: [ ...(state.events || []), { type: 'session_abandoned', route_id: null, timestamp_utc: new Date().toISOString() }, ], } await saveState(token, abandoned) } const newTok = generateToken() localStorage.setItem(TOKEN_KEY, newTok) setToken(newTok) const fresh = createState(newTok) setState(fresh) await saveState(newTok, fresh) setScreen('ledger') } if (loading) { return (
Consulting the ledger...
) } return (
) }