Add accounts, presets, live sheet panel and equipment tiles
- Login with registration code, sessions, rate limiting, per-user characters - Share characters with other users (read-only), admin CLI - Rules presets with a Presets tab, applied/pending changes with diff in a side sheet - Equipment as tiles with amounts, starting kit removed - Rename nav tab to "Character", add "New character" button - Stop tracking _to_delete/ Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
+36
-7
@@ -3,6 +3,9 @@ import Creator from './Creator.jsx'
|
||||
import Rules from './Rules.jsx'
|
||||
import Characters from './Characters.jsx'
|
||||
import Presets from './Presets.jsx'
|
||||
import Login from './Login.jsx'
|
||||
import Account from './Account.jsx'
|
||||
import { claimLegacy } from './legacy.js'
|
||||
|
||||
const useHash = () => {
|
||||
const [h, setH] = useState(window.location.hash.slice(1) || 'creator')
|
||||
@@ -16,16 +19,40 @@ const useHash = () => {
|
||||
|
||||
export default function App() {
|
||||
const hash = useHash()
|
||||
const [auth, setAuth] = useState(null) // null = checking, {user, registration_open}
|
||||
const [rules, setRules] = useState(null)
|
||||
const [error, setError] = useState('')
|
||||
// Used to load a saved character into the creator
|
||||
const [loadRequest, setLoadRequest] = useState(null)
|
||||
const [charName, setCharName] = useState('') // name of the character being worked on, shown in the nav
|
||||
|
||||
// Ask the server who we are; any later 401 (session expired) sends us back to the login
|
||||
useEffect(() => {
|
||||
fetch('/api/auth/me').then((r) => r.json()).then(setAuth).catch(() => setError('Could not reach the server.'))
|
||||
const out = () => { setAuth((a) => ({ user: null, registration_open: a?.registration_open })); setRules(null) }
|
||||
window.addEventListener('aw-unauthorized', out)
|
||||
return () => window.removeEventListener('aw-unauthorized', out)
|
||||
}, [])
|
||||
|
||||
const username = auth?.user?.username
|
||||
const loadRules = () => fetch('/api/rules').then((r) => r.json()).then(setRules).catch(() => setError('Could not load the rules.'))
|
||||
useEffect(() => { loadRules() }, [])
|
||||
useEffect(() => {
|
||||
if (!username) return
|
||||
claimLegacy().finally(loadRules)
|
||||
}, [username]) // eslint-disable-line
|
||||
|
||||
const logout = async () => {
|
||||
await fetch('/api/auth/logout', { method: 'POST' }).catch(() => {})
|
||||
window.location.hash = 'creator'
|
||||
setAuth({ user: null, registration_open: auth.registration_open }); setRules(null)
|
||||
}
|
||||
|
||||
if (error) return <main><p className="warn">{error}</p></main>
|
||||
if (!auth) return <main><p className="muted">Loading …</p></main>
|
||||
if (!auth.user) return <main><Login registrationOpen={auth.registration_open} onDone={(u) => setAuth({ ...auth, user: u })} /></main>
|
||||
|
||||
const page = hash.split('/')[0]
|
||||
const nav = [['creator', 'Create character'], ['rules', 'Rules'], ['presets', 'Presets'], ['characters', 'My characters']]
|
||||
const nav = [['creator', charName ? `Character: ${charName}` : 'Character'], ['rules', 'Rules'], ['presets', 'Presets'], ['characters', 'My characters']]
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -36,19 +63,21 @@ export default function App() {
|
||||
</a>
|
||||
<nav>
|
||||
{nav.map(([id, label]) => (
|
||||
<a key={id} href={`#${id}`} className={page === id ? 'active' : ''}>{label}</a>
|
||||
<a key={id} href={`#${id}`} className={`${page === id ? 'active' : ''} ${id === 'creator' ? 'navchar' : ''}`}>{label}</a>
|
||||
))}
|
||||
<a href="#account" className={page === 'account' ? 'active' : ''} title="Account">{username}</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
{error && <p className="warn">{error}</p>}
|
||||
{!rules && !error && <p className="muted">Loading rules …</p>}
|
||||
{rules && page === 'creator' && <Creator rules={rules} loadRequest={loadRequest} clearLoad={() => setLoadRequest(null)} />}
|
||||
{!rules && <p className="muted">Loading rules …</p>}
|
||||
{rules && page === 'creator' && <Creator rules={rules} user={username} loadRequest={loadRequest} clearLoad={() => setLoadRequest(null)} onName={setCharName} />}
|
||||
{rules && page === 'rules' && <Rules rules={rules} section={hash.split('/')[1]} />}
|
||||
{rules && page === 'presets' && <Presets rules={rules} reload={loadRules} />}
|
||||
{rules && page === 'characters' && (
|
||||
<Characters onOpen={(entry) => { setLoadRequest(entry); window.location.hash = 'creator' }} />
|
||||
<Characters onOpen={(entry) => { setLoadRequest(entry); window.location.hash = 'creator' }}
|
||||
onNew={() => { setLoadRequest({ new: true }); window.location.hash = 'creator' }} />
|
||||
)}
|
||||
{page === 'account' && <Account user={username} onLogout={logout} />}
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user