import { useState, useEffect, lazy, Suspense } from "react"; import "./+Page.css"; import { Attacks } from "@/shared/components/Attacks"; import { AttackControls } from "@/shared/components/AttackControls"; import { Leaderboard } from "@/shared/components/Leaderboard"; import { MenuScreen } from "@/shared/components/MenuScreen"; import { GameMenu } from "@/shared/components/GameMenu"; import { SpawnPhaseOverlay } from "@/shared/components/SpawnPhaseOverlay"; import { GameCanvas } from "@/shared/components/GameCanvas"; import { useGameAPI } from "@/shared/api/GameAPIContext"; import { useAnalytics } from "@/shared/analytics"; import type { GameOutcome, LeaderboardSnapshot } from "@/shared/api/types"; import type { GameRenderer } from "@/shared/render/GameRenderer"; // Lazy load conditional components that aren't needed immediately const GameEndOverlay = lazy(() => import("@/shared/components/GameEndOverlay")); const AlphaWarningModal = lazy(() => import("@/shared/components/AlphaWarningModal")); function App() { const [showMenu, setShowMenu] = useState(true); const [gameStarted, setGameStarted] = useState(false); const [gameOutcome, setGameOutcome] = useState(null); const [spawnPhaseActive, setSpawnPhaseActive] = useState(false); const [spawnCountdown, setSpawnCountdown] = useState<{ startedAtMs: number; durationSecs: number; } | null>(null); const [initialGameState, setInitialGameState] = useState(null); const [initialLeaderboard, setInitialLeaderboard] = useState(null); const [renderer, setRenderer] = useState(null); const [highlightedNation, setHighlightedNation] = useState(null); const api = useGameAPI(); const analytics = useAnalytics(); // Prefetch conditional components after initial render useEffect(() => { const timer = setTimeout(() => { // Trigger chunk downloads without rendering import("@/shared/components/GameEndOverlay"); import("@/shared/components/AlphaWarningModal"); }, 2000); return () => clearTimeout(timer); }, []); // Track app started on mount useEffect(() => { if (!analytics) return; analytics.track("app_started", { platform: __DESKTOP__ ? "desktop" : "browser", }); }, [analytics]); // Check for existing game state on mount (for reload recovery) useEffect(() => { if (!api || typeof api.getGameState !== "function") return; api.getGameState().then((state) => { // Only recover if we actually have render data (indicates a running game) if (state && state.render_init) { console.log("Recovered game state after reload:", state); setInitialGameState(state.render_init); setInitialLeaderboard(state.leaderboard_snapshot); setGameStarted(true); setShowMenu(false); } }); }, [api]); // Subscribe to spawn phase events useEffect(() => { if (!api) return; const unsubUpdate = api.onSpawnPhaseUpdate((update) => { setSpawnPhaseActive(true); setSpawnCountdown(update.countdown); }); const unsubEnd = api.onSpawnPhaseEnded(() => { setSpawnPhaseActive(false); setSpawnCountdown(null); }); return () => { unsubUpdate(); unsubEnd(); }; }, [api]); // Subscribe to game end events useEffect(() => { if (!gameStarted || !api) return; const unsubscribe = api.onGameEnded((outcome) => { console.log("Game outcome received:", outcome); setGameOutcome(outcome); setSpawnPhaseActive(false); // Hide spawn overlay on game end // Track game ended if (analytics) { analytics.track("game_ended", { outcome: outcome.toString().toLowerCase(), }); } }); return () => unsubscribe(); }, [gameStarted, api, analytics]); // Track renderer initialization with GPU info useEffect(() => { if (renderer && analytics) { const rendererInfo = renderer.getRendererInfo(); analytics.track("renderer_initialized", rendererInfo); } }, [renderer, analytics]); // Sync highlighted nation with renderer useEffect(() => { if (renderer) { renderer.setHighlightedNation(highlightedNation); } }, [highlightedNation, renderer]); const handleStartSingleplayer = () => { setShowMenu(false); setGameStarted(true); // Start the game in the backend if (api) { api.startGame(); } // Track game started if (analytics) { analytics.track("game_started", { mode: "singleplayer", }); } }; const handleExit = async () => { if (__DESKTOP__) { const { invoke } = await import("@tauri-apps/api/core"); await invoke("request_exit").catch((err) => { console.error("Failed to request exit:", err); }); } }; return ( <> {/* Game Canvas - always rendered at root, hidden under menu initially */} {/* Menu Screen - covers everything when visible */} {/* Game UI - only visible when game is started */} {gameStarted && (
{/* Spawn Phase Overlay */} { if (api) { api.quitGame(); } setGameOutcome(null); setGameStarted(false); setShowMenu(true); }} onSettings={() => { // TODO: Implement settings }} /> {/* Game End Overlay */} {gameOutcome && ( setGameOutcome(null)} onExit={() => { if (api) { api.quitGame(); } setGameOutcome(null); setGameStarted(false); setShowMenu(true); }} /> )}
)} ); } export default App;