const { useState, useEffect, useRef } = React; function wsUrl() { return `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}`; } function PresentApp() { const [phase, setPhase] = useState('connect'); const [session, setSession] = useState(null); const [question, setQuestion] = useState(null); const [qIndex, setQIndex] = useState(0); const [qTotal, setQTotal] = useState(0); const [timeLimit, setTimeLimit] = useState(0); const [timeLeft, setTimeLeft] = useState(0); const [answerCount, setAnswerCount] = useState(0); const [result, setResult] = useState(null); const [leaderboard, setLeaderboard] = useState([]); const [error, setError] = useState(''); const timerRef = useRef(null); useEffect(() => { const code = location.hash.replace('#', ''); const params = new URLSearchParams(location.search); // Accept both ?pt= (present token) and ?token= (legacy/admin password) const token = params.get('pt') || params.get('token'); if (!code || !token) { setPhase('need-config'); return; } const ws = new WebSocket(wsUrl()); ws.onopen = () => { ws.send(JSON.stringify({ type: 'present-join', token, code })); }; ws.onmessage = (e) => { const msg = JSON.parse(e.data); switch (msg.type) { case 'present-joined': setSession(msg.session); setLeaderboard(msg.session.leaderboard || []); setPhase('waiting'); break; case 'player-joined': case 'player-left': setSession((s) => s ? { ...s, players: msg.players, playerCount: msg.playerCount } : s); break; case 'game-start': setQTotal(msg.totalQuestions); break; case 'question': setQuestion(msg.question); setQIndex(msg.index); setQTotal(msg.total); setTimeLimit(msg.timeLimit); setTimeLeft(msg.timeLimit); setAnswerCount(0); setResult(null); setPhase('question'); break; case 'answer-count': setAnswerCount(msg.count); break; case 'time-up': clearInterval(timerRef.current); break; case 'question-result': setResult(msg); setLeaderboard(msg.leaderboard); setPhase('result'); clearInterval(timerRef.current); break; case 'game-end': setLeaderboard(msg.leaderboard); setPhase('final'); clearInterval(timerRef.current); break; case 'error': setError(msg.message); setPhase('error'); break; } }; ws.onerror = () => setPhase('error'); return () => ws.close(); }, []); useEffect(() => { clearInterval(timerRef.current); if (phase === 'question' && timeLimit > 0) { const start = Date.now(); timerRef.current = setInterval(() => { setTimeLeft(Math.max(0, timeLimit - (Date.now() - start) / 1000)); }, 100); } }, [phase, timeLimit]); if (phase === 'need-config') { return (
Öffne diesen Link aus dem Admin-Panel.
❌ Verbindungsfehler
{error &&{error}
}{session?.setName}
{session?.players?.length || 0} Teilnehmer