Scoreboard ui stuff
This commit is contained in:
parent
f0d177feeb
commit
279dc7f2c3
12 changed files with 1558 additions and 77 deletions
133
hooks/useGame.ts
133
hooks/useGame.ts
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { Quiz, Player, GameState, GameRole, NetworkMessage, AnswerOption, Question, GenerateQuizOptions, ProcessedDocument, GameConfig, DEFAULT_GAME_CONFIG, PointsBreakdown } from '../types';
|
||||
import { generateQuiz } from '../services/geminiService';
|
||||
import { QUESTION_TIME, QUESTION_TIME_MS, PLAYER_COLORS, calculatePointsWithBreakdown, getPlayerRank } from '../constants';
|
||||
|
|
@ -35,6 +36,9 @@ const clearStoredSession = () => {
|
|||
};
|
||||
|
||||
export const useGame = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [role, setRole] = useState<GameRole>('HOST');
|
||||
const [gameState, setGameState] = useState<GameState>('LANDING');
|
||||
const [quiz, setQuiz] = useState<Quiz | null>(null);
|
||||
|
|
@ -87,6 +91,53 @@ export const useGame = () => {
|
|||
useEffect(() => { firstCorrectPlayerIdRef.current = firstCorrectPlayerId; }, [firstCorrectPlayerId]);
|
||||
useEffect(() => { currentCorrectShapeRef.current = currentCorrectShape; }, [currentCorrectShape]);
|
||||
|
||||
const isInitializingFromUrl = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitializingFromUrl.current) return;
|
||||
if (location.pathname === '/callback') return;
|
||||
|
||||
const getTargetPath = () => {
|
||||
switch (gameState) {
|
||||
case 'LANDING':
|
||||
if (gamePin && location.pathname.startsWith('/play/')) {
|
||||
return `/play/${gamePin}`;
|
||||
}
|
||||
return '/';
|
||||
case 'CREATING':
|
||||
case 'GENERATING':
|
||||
return '/create';
|
||||
case 'EDITING':
|
||||
return '/edit';
|
||||
case 'LOBBY':
|
||||
case 'COUNTDOWN':
|
||||
case 'QUESTION':
|
||||
case 'REVEAL':
|
||||
case 'SCOREBOARD':
|
||||
case 'PODIUM':
|
||||
case 'HOST_RECONNECTED':
|
||||
if (gamePin) {
|
||||
return role === 'HOST' ? `/host/${gamePin}` : `/play/${gamePin}`;
|
||||
}
|
||||
return '/';
|
||||
case 'DISCONNECTED':
|
||||
case 'WAITING_TO_REJOIN':
|
||||
if (gamePin) {
|
||||
return `/play/${gamePin}`;
|
||||
}
|
||||
return '/';
|
||||
default:
|
||||
return '/';
|
||||
}
|
||||
};
|
||||
|
||||
const targetPath = getTargetPath();
|
||||
if (location.pathname !== targetPath) {
|
||||
const useReplace = ['COUNTDOWN', 'QUESTION', 'REVEAL', 'SCOREBOARD'].includes(gameState);
|
||||
navigate(targetPath + location.search, { replace: useReplace });
|
||||
}
|
||||
}, [gameState, gamePin, role, navigate, location.pathname, location.search]);
|
||||
|
||||
const generateGamePin = () => Math.floor(Math.random() * 900000) + 100000 + "";
|
||||
|
||||
const generateRandomName = (): string => {
|
||||
|
|
@ -391,14 +442,69 @@ export const useGame = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
const session = getStoredSession();
|
||||
if (session) {
|
||||
if (session.role === 'HOST') {
|
||||
reconnectAsHost(session);
|
||||
} else {
|
||||
reconnectAsClient(session);
|
||||
const initializeFromUrl = async () => {
|
||||
const path = location.pathname;
|
||||
|
||||
if (path === '/callback') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const hostMatch = path.match(/^\/host\/(\d+)$/);
|
||||
const playMatch = path.match(/^\/play\/(\d+)$/);
|
||||
|
||||
if (hostMatch) {
|
||||
const pin = hostMatch[1];
|
||||
const session = getStoredSession();
|
||||
if (session && session.pin === pin && session.role === 'HOST') {
|
||||
isInitializingFromUrl.current = true;
|
||||
await reconnectAsHost(session);
|
||||
isInitializingFromUrl.current = false;
|
||||
} else {
|
||||
navigate('/', { replace: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (playMatch) {
|
||||
const pin = playMatch[1];
|
||||
const session = getStoredSession();
|
||||
if (session && session.pin === pin && session.role === 'CLIENT') {
|
||||
isInitializingFromUrl.current = true;
|
||||
await reconnectAsClient(session);
|
||||
isInitializingFromUrl.current = false;
|
||||
} else {
|
||||
setGamePin(pin);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (path === '/create') {
|
||||
isInitializingFromUrl.current = true;
|
||||
setGameState('CREATING');
|
||||
setRole('HOST');
|
||||
isInitializingFromUrl.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (path === '/edit') {
|
||||
const session = getStoredSession();
|
||||
if (!session) {
|
||||
navigate('/', { replace: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const session = getStoredSession();
|
||||
if (session) {
|
||||
if (session.role === 'HOST') {
|
||||
reconnectAsHost(session);
|
||||
} else {
|
||||
reconnectAsClient(session);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initializeFromUrl();
|
||||
|
||||
return () => {
|
||||
if (timerRef.current) clearInterval(timerRef.current);
|
||||
|
|
@ -465,6 +571,10 @@ export const useGame = () => {
|
|||
setGameState('CREATING');
|
||||
};
|
||||
|
||||
const cancelCreation = () => {
|
||||
setGameState('LANDING');
|
||||
};
|
||||
|
||||
const finalizeManualQuiz = (manualQuiz: Quiz, saveToLibrary: boolean = false) => {
|
||||
if (saveToLibrary) {
|
||||
setPendingQuizToSave({ quiz: manualQuiz, topic: '' });
|
||||
|
|
@ -940,6 +1050,7 @@ export const useGame = () => {
|
|||
if (peerRef.current) {
|
||||
peerRef.current.destroy();
|
||||
}
|
||||
setGamePin(null);
|
||||
setGameState('LANDING');
|
||||
setError(null);
|
||||
};
|
||||
|
|
@ -1175,7 +1286,11 @@ export const useGame = () => {
|
|||
if (timerRef.current) clearInterval(timerRef.current);
|
||||
if (syncTimerRef.current) clearInterval(syncTimerRef.current);
|
||||
if (peerRef.current) peerRef.current.destroy();
|
||||
window.location.reload();
|
||||
setGamePin(null);
|
||||
setQuiz(null);
|
||||
setPlayers([]);
|
||||
setGameState('LANDING');
|
||||
navigate('/', { replace: true });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -1187,7 +1302,7 @@ export const useGame = () => {
|
|||
return {
|
||||
role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, lastAnswerCorrect, currentCorrectShape, selectedOption, currentPlayerScore, currentStreak, currentPlayerId, gameConfig,
|
||||
pendingQuizToSave, dismissSavePrompt, sourceQuizId, isReconnecting, currentPlayerName,
|
||||
startQuizGen, startManualCreation, finalizeManualQuiz, loadSavedQuiz, joinGame, startGame: startHostGame, handleAnswer, nextQuestion, showScoreboard,
|
||||
startQuizGen, startManualCreation, cancelCreation, finalizeManualQuiz, loadSavedQuiz, joinGame, startGame: startHostGame, handleAnswer, nextQuestion, showScoreboard,
|
||||
updateQuizFromEditor, startGameFromEditor, backFromEditor, endGame, attemptReconnect, goHomeFromDisconnected, resumeGame
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue