made some changes to the UI, removed leaderboards. updated gamesaves
This commit is contained in:
+72
-5
@@ -19,7 +19,12 @@ import {
|
||||
type AuthSession,
|
||||
type CharacterProfile,
|
||||
} from './profile'
|
||||
import { getGameMode, type GameMode } from './gameRepository'
|
||||
import {
|
||||
getCloudSyncStatus,
|
||||
getGameMode,
|
||||
syncCloudSave,
|
||||
type GameMode,
|
||||
} from './gameRepository'
|
||||
import { focusFirstControl } from './input.tsx'
|
||||
|
||||
type Screen =
|
||||
@@ -40,8 +45,8 @@ const MENU_ITEMS: Array<{
|
||||
glyph: string
|
||||
description: string
|
||||
}> = [
|
||||
{ screen: 'dungeons', label: 'Dungeons', glyph: 'D', description: 'Guide a five-player party through dangerous encounters.' },
|
||||
{ screen: 'raids', label: 'Raids', glyph: 'R', description: 'Guide a ten-player party through three-phase challenges.' },
|
||||
{ screen: 'dungeons', label: 'Dungeons', glyph: 'D', description: 'Guide a six-player party through dangerous encounters.' },
|
||||
{ screen: 'raids', label: 'Raids', glyph: 'R', description: 'Guide an eighteen-player party through three-phase challenges.' },
|
||||
{ screen: 'roguelike', label: 'Roguelike', glyph: 'L', description: 'Draft upgrades through escalating random encounters.' },
|
||||
{ screen: 'pvp', label: 'PvP', glyph: 'P', description: 'Race another healer through roguelike encounters with buffs and sabotage.' },
|
||||
{ screen: 'customize', label: 'Customize Character', glyph: 'C', description: 'Choose your class and prepare a six-ability loadout.' },
|
||||
@@ -49,6 +54,7 @@ const MENU_ITEMS: Array<{
|
||||
]
|
||||
|
||||
const LAST_DIFFICULTY_KEY = 'i-want-to-heal:last-difficulty'
|
||||
const SHOW_LEADERBOARDS = false
|
||||
|
||||
function activityInitials(name: string) {
|
||||
return name
|
||||
@@ -88,6 +94,8 @@ function App() {
|
||||
const [lootSort, setLootSort] = useState<'sequence' | 'boss'>('sequence')
|
||||
const [showLeaderboard, setShowLeaderboard] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [syncingCloud, setSyncingCloud] = useState(false)
|
||||
const [syncMessage, setSyncMessage] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
loadAuthSession()
|
||||
@@ -105,6 +113,17 @@ function App() {
|
||||
.finally(() => setAuthChecked(true))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const handleModeChange = (event: Event) => {
|
||||
const nextMode = (event as CustomEvent<GameMode>).detail
|
||||
setGameMode(nextMode)
|
||||
}
|
||||
window.addEventListener('chronicle:mode-changed', handleModeChange as EventListener)
|
||||
return () => {
|
||||
window.removeEventListener('chronicle:mode-changed', handleModeChange as EventListener)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (screen === 'combat') return
|
||||
window.requestAnimationFrame(() => {
|
||||
@@ -138,11 +157,27 @@ function App() {
|
||||
setProfile(null)
|
||||
setGameMode(getGameMode())
|
||||
setScreen('menu')
|
||||
setSyncMessage('')
|
||||
} catch (reason) {
|
||||
setError(reason instanceof Error ? reason.message : 'Unable to sign out.')
|
||||
}
|
||||
}
|
||||
|
||||
async function syncSaveNow() {
|
||||
setSyncingCloud(true)
|
||||
setSyncMessage('')
|
||||
try {
|
||||
const updated = await syncCloudSave()
|
||||
setProfile(updated)
|
||||
setGameMode(getGameMode())
|
||||
setSyncMessage('Cloud save updated.')
|
||||
} catch (reason) {
|
||||
setSyncMessage(reason instanceof Error ? reason.message : 'Unable to sync cloud save.')
|
||||
} finally {
|
||||
setSyncingCloud(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<main className="game-shell">
|
||||
@@ -253,6 +288,8 @@ function App() {
|
||||
{ part: 3, name: `${sectionName} 3`, encounterCount: 3, unlocked: completedSections >= 2 },
|
||||
]
|
||||
const lootChanceSlots = activity.contentType === 'raid' ? 8 : 5
|
||||
const cloudSync = getCloudSyncStatus()
|
||||
const canShowCloudSync = account.id !== -1 && cloudSync.available
|
||||
const lootPreviewEncounters = [...activity.encounters]
|
||||
.filter((encounter) => encounter.isBoss)
|
||||
.sort((a, b) => lootSort === 'boss'
|
||||
@@ -285,6 +322,28 @@ function App() {
|
||||
{screen === 'menu' && (
|
||||
<section className="menu-screen">
|
||||
<div className="main-menu-grid">
|
||||
{canShowCloudSync && (
|
||||
<div className="menu-card cloud-sync-card">
|
||||
<span>{cloudSync.dirty ? 'S' : 'C'}</span>
|
||||
<div>
|
||||
<strong>Cloud Save</strong>
|
||||
<small>
|
||||
{cloudSync.dirty
|
||||
? 'Local progress waiting. Upload when you want to refresh the server copy.'
|
||||
: 'Server copy matches this device.'}
|
||||
</small>
|
||||
{syncMessage && <small className="cloud-sync-message">{syncMessage}</small>}
|
||||
</div>
|
||||
<button
|
||||
className="text-button"
|
||||
disabled={syncingCloud || !cloudSync.dirty}
|
||||
onClick={syncSaveNow}
|
||||
type="button"
|
||||
>
|
||||
{syncingCloud ? 'Syncing...' : cloudSync.dirty ? 'Sync Save To Server' : 'Already Synced'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{MENU_ITEMS.map((item) => (
|
||||
<button
|
||||
className="menu-card"
|
||||
@@ -457,6 +516,7 @@ function App() {
|
||||
Start Match
|
||||
</button>
|
||||
</div>
|
||||
{SHOW_LEADERBOARDS && (
|
||||
<div className="leaderboard-section">
|
||||
<div className="equipment-heading toggle-heading">
|
||||
<div>
|
||||
@@ -488,6 +548,7 @@ function App() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
@@ -663,6 +724,7 @@ function App() {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{SHOW_LEADERBOARDS && (
|
||||
<div className="leaderboard-section">
|
||||
<div className="equipment-heading toggle-heading">
|
||||
<div>
|
||||
@@ -682,7 +744,9 @@ function App() {
|
||||
<p className="section-note">
|
||||
{gameMode === 'offline'
|
||||
? 'Offline runs are not submitted'
|
||||
: 'Lowest resource spent ranks first'}
|
||||
: canShowCloudSync
|
||||
? 'Manual save sync updates your cloud profile.'
|
||||
: 'Lowest resource spent ranks first'}
|
||||
</p>
|
||||
<div className="leaderboard-tabs">
|
||||
{([
|
||||
@@ -730,13 +794,16 @@ function App() {
|
||||
<div className="leaderboard-empty">
|
||||
{gameMode === 'offline'
|
||||
? 'Connect with an online character to compete in rankings.'
|
||||
: 'Complete this difficulty to claim the first ranking.'}
|
||||
: canShowCloudSync
|
||||
? 'No leaderboard entries yet.'
|
||||
: 'Complete this difficulty to claim the first ranking.'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user