import { useEffect, type ReactNode } from "react"; import { Route, Routes } from "react-router"; import { useApi } from "./api/context"; import { startClockTicker, syncClock } from "./clock/clock"; import { AwaySummary } from "./components/AwaySummary"; import { ToastFeed } from "./components/ToastFeed"; import { Toasts } from "./components/Toasts"; import { DealModal } from "./components/DealModal"; import { WelcomeGate } from "./components/Welcome"; import { SkillPointPrompt } from "./components/SkillPointPrompt"; import { formatCredits } from "./format"; import { tabAttention } from "./layout/attention"; import { Layout } from "./layout/Layout"; import { SCREENS } from "./routes"; import { Contracts } from "./screens/Contracts"; import { Fleet } from "./screens/Fleet"; import { Dashboard } from "./screens/Dashboard"; import { HQs } from "./screens/HQs"; import { Locations } from "./screens/Locations"; import { MapScreen } from "./screens/MapScreen"; import { Market } from "./screens/Market"; import { More } from "./screens/More"; import { Pilots } from "./screens/Pilots"; import { Logbook } from "./screens/Logbook"; import { Settings } from "./screens/Settings"; import { useMotionPreference } from "./state/preferences"; import { StartScreen } from "./screens/StartScreen"; import { usePlayerState, useRefetchOnArrival } from "./state/player"; import { RadioCallSheet, useRefetchOnRadio } from "./components/RadioCalls"; import { Button } from "./ui"; const SCREEN_COMPONENTS: Record<(typeof SCREENS)[number]["path"], ReactNode> = { "/": , "/contracts": , "/fleet": , "/more": , }; /** Reads the game clock (the engine's device clock, CR-014), then ticks it every second. */ function useGameClock() { const api = useApi(); useEffect(() => { void syncClock(api); return startClockTicker(); }, [api]); } /** The whole client. Router, API and query providers come from the caller. */ export function App() { useGameClock(); useMotionPreference(); const state = usePlayerState(); useRefetchOnArrival(state.data ?? null, state.refetch); useRefetchOnRadio(state.data ?? null, state.refetch); const retry = () => void state.refetch(); if (state.isPending) { return

Loading…

; } if (state.data === undefined) { // The first load failed: nothing to show yet. return (

Could not load the game.

); } if (state.data === null) return ; // A later refresh failed: keep showing the last state, with a way to retry. const banner = state.isError ? (
Couldn't refresh the game.
) : null; return ( <> } > {SCREENS.map((screen) => ( ))} {/* Screens without a tab of their own (decision 0.5.167). */} } /> } /> } /> } /> } /> {/* Settings opens from the header gear and More, not a tab (CR-011). */} } /> {/* The Logbook opens from "While you were away" and the Bridge (CR-025). */} } /> {/* What happens while the player watches (CR-028). */} ); }