import React from 'react'; import UsernameGate from './UsernameGate.jsx'; import Game from './Game.jsx'; import MobileGame from './MobileGame.jsx'; import Leaderboard from './Leaderboard.jsx'; import { api } from './api.js'; function detectMobile() { if (typeof window === 'undefined') return false; const ua = navigator.userAgent || ''; const isTouch = (navigator.maxTouchPoints || 0) > 0; const isPhoneUA = /Android|iPhone|iPod|Mobile/i.test(ua); const isSmall = window.innerWidth < 820; return isPhoneUA || (isTouch && isSmall); } export default function App() { const [username, setUsername] = React.useState(() => { try { return localStorage.getItem('egg-username') || ''; } catch { return ''; } }); const [mode, setMode] = React.useState(() => detectMobile() ? 'mobile' : 'desktop'); const [view, setView] = React.useState('play'); const [lastResult, setLastResult] = React.useState(null); // room hoisted here so Play Again reuses the same room (controller stays paired) const [roomId, setRoomId] = React.useState(null); const [lanIp, setLanIp] = React.useState(null); const [ipCandidates, setIpCandidates] = React.useState([]); React.useEffect(() => { const onResize = () => setMode(detectMobile() ? 'mobile' : 'desktop'); window.addEventListener('resize', onResize); return () => window.removeEventListener('resize', onResize); }, []); React.useEffect(() => { if (mode !== 'desktop' || roomId) return; let alive = true; (async () => { try { const r1 = await fetch(api('/api/game/room'), { method: 'POST' }).then((r) => r.json()); if (!alive) return; setRoomId(r1.roomId); if (import.meta.env.PROD) return; // in prod, controller URL = window.location.origin try { const r2 = await fetch(api('/api/game/local-ip')).then((r) => r.json()); if (!alive) return; setLanIp(r2.ip); setIpCandidates(Array.isArray(r2.candidates) ? r2.candidates : []); } catch {} } catch {} })(); return () => { alive = false; }; }, [mode, roomId]); const onPickUsername = (name) => { try { localStorage.setItem('egg-username', name); } catch {} setUsername(name); }; const onGameOver = (result) => { setLastResult(result); setView('board'); }; const playAgain = () => { setLastResult(null); setView('play'); }; const changeUser = () => { try { localStorage.removeItem('egg-username'); } catch {} setUsername(''); setLastResult(null); setView('play'); try { if (document.fullscreenElement) document.exitFullscreen(); } catch {} }; if (!username) { return ; } if (view === 'board') { return ( ); } return (
setView('board')} onChangeUser={changeUser} onToggleMode={() => setMode((m) => (m === 'mobile' ? 'desktop' : 'mobile'))} /> {mode === 'mobile' ? ( ) : ( )}
); } function TopBar({ username, mode, onSeeBoard, onChangeUser, onToggleMode }) { return (
EGG-CATCHER
▸ PLAYER {username}
); }