Files
smart-rgb/frontend/pages/index/+Page.tsx
2025-10-11 14:52:54 -05:00

72 lines
2.1 KiB
TypeScript

import { useState, useEffect, lazy, Suspense } from "react";
import "./+Page.css";
import { MenuScreen } from "@/shared/components/MenuScreen";
import { useAnalytics } from "@/shared/analytics";
// Lazy load components that aren't needed immediately
const GameContainer = lazy(() => import("./GameContainer.client").then(m => ({ default: m.GameContainer })));
const AlphaWarningModal = lazy(() => import("@/shared/components/AlphaWarningModal"));
function App() {
const [showMenu, setShowMenu] = useState(true);
const [isHydrated, setIsHydrated] = useState(false);
const analytics = useAnalytics();
// Mark as hydrated after first render to prevent hydration mismatch
useEffect(() => {
setIsHydrated(true);
}, []);
// Prefetch components after initial render
useEffect(() => {
const timer = setTimeout(() => {
// Trigger chunk downloads without rendering
import("./GameContainer.client");
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]);
const handleStartSingleplayer = () => {
setShowMenu(false);
};
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);
});
}
};
// Don't render game components until hydration is complete
if (!isHydrated) {
return null;
}
return (
<>
{/* Menu Screen - pre-renderable, covers everything when visible */}
{showMenu && <MenuScreen onStartSingleplayer={handleStartSingleplayer} onExit={handleExit} />}
{/* Game Container - client-only, lazy loaded when game starts */}
{!showMenu && (
<Suspense fallback={null}>
<GameContainer onReturnToMenu={() => setShowMenu(true)} />
</Suspense>
)}
</>
);
}
export default App;