Files
otivm/src/App.jsx

124 lines
3.2 KiB
JavaScript

import { useState, useEffect } from 'react'
import { generateToken, loadState, saveState } from './api.js'
import { createState } from './gameState.js'
import { BACKGROUNDS } from './constants.js'
import Shell from './components/Shell.jsx'
import Actor from './screens/Actor.jsx'
import Forum from './screens/Forum.jsx'
import Map from './screens/Map.jsx'
import './App.css'
import contextsJson from './config/contexts.json'
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 [context, setContext] = useState('actor')
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)
setContext(saved.background_id && saved.background_id !== 'unknown' ? 'forum' : 'actor')
} else {
const fresh = createState(tok)
setState(fresh)
await saveState(tok, fresh)
setContext('actor')
}
setLoading(false)
}
bootstrap()
}, [])
async function onStateChange(newState) {
setState(newState)
await saveState(token, newState)
}
async function onSelectBackground(backgroundId) {
const bg = BACKGROUNDS.find(b => b.id === backgroundId)
if (!bg) return
const updated = { ...state, background_id: backgroundId, den: bg.starting_den }
setState(updated)
await saveState(token, updated)
setContext('forum')
}
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)
setContext('actor')
}
if (loading) {
return <div className="otivm-loading"><span>Consulting the ledger...</span></div>
}
const activeCtx = contextsJson.find(c => c.id === context) || contextsJson[0]
const layout = activeCtx.layout
const subitems = activeCtx.subitems || []
return (
<Shell
contexts={contextsJson}
activeId={context}
onContext={setContext}
subitems={subitems}
layout={layout}
token={token}
onNewGame={onNewGame}
ctxName={activeCtx.name}
ctxSubtitle={activeCtx.subtitle}
>
{/* ACTOR */}
{context === 'actor' && (
<Actor
state={state}
onSelectBackground={onSelectBackground}
layout={layout}
/>
)}
{/* FORUM */}
{context === 'forum' && (
<Forum
state={state}
onStateChange={onStateChange}
onNewGame={onNewGame}
layout={layout}
/>
)}
{/* MAP */}
{context === 'map' && (
<Map state={state} layout={layout} />
)}
</Shell>
)
}