const { useState, useEffect, useRef } = React; function wsUrl() { return `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}`; } const COLORS = { A: 'answer-a', B: 'answer-b', C: 'answer-c', D: 'answer-d' }; 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 timerRef = useRef(null); const wsRef = useRef(null); useEffect(() => { const code = location.hash.replace('#', ''); const token = prompt_alternative(); if (!code || !token) { setPhase('need-config'); return; } const ws = new WebSocket(wsUrl()); wsRef.current = ws; 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': setPhase('error'); break; } }; return () => ws.close(); }, []); // Timer 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]); // Simple token retrieval (read from URL param or sessionStorage) function prompt_alternative() { const params = new URLSearchParams(location.search); let t = params.get('token'); if (!t) { try { t = sessionStorage.getItem('qa_token'); } catch (e) { } } return t; } if (phase === 'need-config') { return (
Öffne diesen Link aus dem Admin-Panel oder füge ?token=PASSWORT#CODE zur URL hinzu.
{session?.setName}
{session?.players?.length || 0} Teilnehmer
Richtig: {result?.correctAnswer?.join(', ')}