import React from 'react'; import { api } from './api.js'; const ROUND_MS = 60_000; function send(roomId, action) { fetch(api('/api/game/control'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ roomId, action }), }).catch(() => {}); } function isLandscape() { if (typeof window === 'undefined') return true; return window.matchMedia('(orientation: landscape)').matches; } export default function Controller() { const params = new URLSearchParams(window.location.search); const roomId = (params.get('room') || '').toUpperCase(); const [paired, setPaired] = React.useState(false); const [score, setScore] = React.useState(0); const [timeLeft, setTimeLeft] = React.useState(null); const [running, setRunning] = React.useState(false); const [landscape, setLandscape] = React.useState(isLandscape()); const [armed, setArmed] = React.useState(false); React.useEffect(() => { if (!roomId) return; const es = new EventSource(api(`/api/game/controller-events/${roomId}`)); es.addEventListener('ready', (e) => { try { setPaired(JSON.parse(e.data).paired); } catch {} }); es.addEventListener('paired', () => setPaired(true)); es.addEventListener('state', (e) => { try { const s = JSON.parse(e.data); if (typeof s.score === 'number') setScore(s.score); if (typeof s.timeLeft === 'number') setTimeLeft(s.timeLeft); if (typeof s.running === 'boolean') setRunning(s.running); } catch {} }); es.onerror = () => {}; return () => es.close(); }, [roomId]); React.useEffect(() => { const prev = document.body.style.overscrollBehavior; document.body.style.overscrollBehavior = 'none'; document.body.classList.add('controller-body'); return () => { document.body.style.overscrollBehavior = prev; document.body.classList.remove('controller-body'); }; }, []); React.useEffect(() => { const mq = window.matchMedia('(orientation: landscape)'); const update = () => setLandscape(mq.matches); mq.addEventListener?.('change', update); window.addEventListener('resize', update); return () => { mq.removeEventListener?.('change', update); window.removeEventListener('resize', update); }; }, []); const arm = React.useCallback(async () => { if (armed) return; setArmed(true); try { await document.documentElement.requestFullscreen?.(); } catch {} try { await screen.orientation?.lock?.('landscape'); } catch {} }, [armed]); if (!roomId) { return (
Open this page by scanning the QR code shown on the laptop.