Files
i-want-to-heal/src/App.tsx
T
2026-07-05 22:48:56 -04:00

160 lines
4.6 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react'
import { AuthScreen } from './components/AuthScreen'
import IWantToHeal1App from './modes/iwt1/IWantToHeal1App'
import { IWantToHeal2App } from './modes/iwt2/IWantToHeal2App'
import { useGameAction } from './input'
import {
loadAuthSession,
type AuthSession,
} from './profile'
type GameVersion = 'iwt1' | 'iwt2'
const GAME_OPTIONS: Array<{
version: GameVersion
title: string
label: string
description: string
glyph: string
}> = [
{
version: 'iwt1',
title: 'I Want To Heal 1',
label: 'Classic Healer Runs',
description: 'Original dungeon, raid, roguelike, PvP, gear, talents, and collection progression.',
glyph: 'I',
},
{
version: 'iwt2',
title: 'I Want To Heal 2',
label: '2D Boss Arena',
description: 'Move through boss arenas with a visible party, analog movement, projectiles, and separate progression.',
glyph: 'II',
},
]
function App() {
const [selectedVersion, setSelectedVersion] = useState<GameVersion | null>(null)
const [selectedIndex, setSelectedIndex] = useState(0)
const [authSession, setAuthSession] = useState<AuthSession | null>(null)
const [authChecked, setAuthChecked] = useState(false)
const [serverMessage, setServerMessage] = useState('')
useEffect(() => {
let cancelled = false
loadAuthSession()
.then((session) => {
if (cancelled) return
setAuthSession(session.account && session.profile ? session : null)
})
.catch((reason: unknown) => {
if (cancelled) return
setServerMessage(
reason instanceof Error
? `${reason.message} Offline play is still available.`
: 'Unable to reach the server. Offline play is still available.',
)
})
.finally(() => {
if (!cancelled) setAuthChecked(true)
})
return () => {
cancelled = true
}
}, [])
const acceptSession = useCallback((session: AuthSession) => {
setAuthSession(session)
setSelectedVersion(null)
setServerMessage('')
}, [])
const clearAuthSession = useCallback(() => {
setAuthSession(null)
setSelectedVersion(null)
}, [])
useGameAction((action, device) => {
if (!authSession || selectedVersion || device !== 'controller') return
if (action === 'navigateLeft' || action === 'navigateUp') {
setSelectedIndex((current) => Math.max(0, current - 1))
} else if (action === 'navigateRight' || action === 'navigateDown') {
setSelectedIndex((current) => Math.min(GAME_OPTIONS.length - 1, current + 1))
} else if (action === 'confirm') {
setSelectedVersion(GAME_OPTIONS[selectedIndex].version)
}
})
if (!authChecked) {
return (
<main className="game-shell">
<section className="message-panel">
<p className="eyebrow">Opening Chronicle</p>
<h1>Loading...</h1>
</section>
</main>
)
}
if (!authSession) {
return (
<AuthScreen
onAuthenticated={acceptSession}
serverMessage={serverMessage}
/>
)
}
if (selectedVersion === 'iwt1') {
return (
<IWantToHeal1App
initialSession={authSession}
onAuthenticationCleared={clearAuthSession}
onBackToGameSelect={() => setSelectedVersion(null)}
/>
)
}
if (selectedVersion === 'iwt2') {
return (
<IWantToHeal2App
onlineBackupsAvailable={authSession.account?.id !== -1}
onBackToGameSelect={() => setSelectedVersion(null)}
/>
)
}
return (
<main className="game-shell game-version-shell">
<section className="game-version-screen" data-game-nav-active="true">
<div className="game-version-heading">
<p className="eyebrow">Select Game</p>
<h1>I Want To Heal</h1>
</div>
<div className="game-version-grid">
{GAME_OPTIONS.map((option, index) => (
<button
className={`game-version-card ${selectedIndex === index ? 'game-selected' : ''}`}
data-controller-nav="skip"
data-game-selected={selectedIndex === index ? 'true' : undefined}
key={option.version}
onClick={() => setSelectedVersion(option.version)}
onPointerDown={() => setSelectedIndex(index)}
type="button"
>
<span>{option.glyph}</span>
<div>
<strong>{option.title}</strong>
<small>{option.label}</small>
<p>{option.description}</p>
</div>
</button>
))}
</div>
</section>
</main>
)
}
export default App