68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
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 { formatCredits } from "./format";
|
|
import { Layout } from "./layout/Layout";
|
|
import { SCREENS } from "./routes";
|
|
import { Contracts } from "./screens/Contracts";
|
|
import { HQ } from "./screens/HQ";
|
|
import { Placeholder } from "./screens/Placeholder";
|
|
import { StartScreen } from "./screens/StartScreen";
|
|
import { usePlayerState, useRefetchOnArrival } from "./state/player";
|
|
|
|
/** Built screens by path; the rest show a placeholder. */
|
|
const SCREEN_COMPONENTS: Partial<Record<(typeof SCREENS)[number]["path"], ReactNode>> = {
|
|
"/": <HQ />,
|
|
"/contracts": <Contracts />,
|
|
};
|
|
|
|
/** Syncs the clock with the server once, then ticks it every second. */
|
|
function useServerClock() {
|
|
const api = useApi();
|
|
useEffect(() => {
|
|
syncClock(api).catch(() => {
|
|
// Offline or server down: keep the device clock until the next launch.
|
|
});
|
|
return startClockTicker();
|
|
}, [api]);
|
|
}
|
|
|
|
/** The whole client. Router, API and query providers come from the caller. */
|
|
export function App() {
|
|
useServerClock();
|
|
const state = usePlayerState();
|
|
useRefetchOnArrival(state.data ?? null, state.refetch);
|
|
|
|
if (state.isPending) {
|
|
return <p className="p-6 text-slate-400">Loading…</p>;
|
|
}
|
|
if (state.isError) {
|
|
return (
|
|
<p role="alert" className="p-6 text-red-400">
|
|
Could not reach the server.
|
|
</p>
|
|
);
|
|
}
|
|
if (state.data === null) return <StartScreen />;
|
|
|
|
return (
|
|
<>
|
|
<Routes>
|
|
<Route element={<Layout credits={formatCredits(state.data.company.credits)} />}>
|
|
{SCREENS.map((screen) => (
|
|
<Route
|
|
key={screen.path}
|
|
index={screen.path === "/"}
|
|
path={screen.path === "/" ? undefined : screen.path}
|
|
element={SCREEN_COMPONENTS[screen.path] ?? <Placeholder title={screen.title} />}
|
|
/>
|
|
))}
|
|
</Route>
|
|
</Routes>
|
|
<AwaySummary />
|
|
</>
|
|
);
|
|
}
|