Fix host disconnect
This commit is contained in:
parent
2b3be6569f
commit
5d3c6320d9
2 changed files with 27 additions and 15 deletions
6
App.tsx
6
App.tsx
|
|
@ -104,7 +104,8 @@ function App() {
|
||||||
setPresenterPlayer,
|
setPresenterPlayer,
|
||||||
sendAdvance,
|
sendAdvance,
|
||||||
kickPlayer,
|
kickPlayer,
|
||||||
leaveGame
|
leaveGame,
|
||||||
|
connectedPlayerIds
|
||||||
} = useGame(defaultConfig);
|
} = useGame(defaultConfig);
|
||||||
|
|
||||||
const handleSaveQuiz = async () => {
|
const handleSaveQuiz = async () => {
|
||||||
|
|
@ -386,7 +387,8 @@ function App() {
|
||||||
quizTitle={quiz.title}
|
quizTitle={quiz.title}
|
||||||
currentQuestionIndex={currentQuestionIndex}
|
currentQuestionIndex={currentQuestionIndex}
|
||||||
totalQuestions={quiz.questions.length}
|
totalQuestions={quiz.questions.length}
|
||||||
playerCount={players.filter(p => p.id !== 'host').length}
|
players={players}
|
||||||
|
connectedPlayerIds={connectedPlayerIds}
|
||||||
onResume={resumeGame}
|
onResume={resumeGame}
|
||||||
onEndGame={endGame}
|
onEndGame={endGame}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
const [hostSecret, setHostSecret] = useState<string | null>(null);
|
const [hostSecret, setHostSecret] = useState<string | null>(null);
|
||||||
const [isReconnecting, setIsReconnecting] = useState(false);
|
const [isReconnecting, setIsReconnecting] = useState(false);
|
||||||
const [presenterId, setPresenterId] = useState<string | null>(null);
|
const [presenterId, setPresenterId] = useState<string | null>(null);
|
||||||
|
const [connectedPlayerIds, setConnectedPlayerIds] = useState<string[]>([]);
|
||||||
|
|
||||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
const syncTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
const syncTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
@ -213,11 +214,19 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't redirect away from game URLs during initial load - let initializeFromUrl handle it
|
||||||
|
const isGameUrl = /^\/host\/[A-Z0-9]+$/i.test(location.pathname) ||
|
||||||
|
/^\/play\/[A-Z0-9]+$/i.test(location.pathname);
|
||||||
|
|
||||||
switch (gameState) {
|
switch (gameState) {
|
||||||
case 'LANDING':
|
case 'LANDING':
|
||||||
if (gamePin && location.pathname.startsWith('/play/')) {
|
if (gamePin && location.pathname.startsWith('/play/')) {
|
||||||
return `/play/${gamePin}`;
|
return `/play/${gamePin}`;
|
||||||
}
|
}
|
||||||
|
// Don't navigate away from game URLs if we're in LANDING state (might be reconnecting)
|
||||||
|
if (isGameUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return '/';
|
return '/';
|
||||||
case 'CREATING':
|
case 'CREATING':
|
||||||
case 'GENERATING':
|
case 'GENERATING':
|
||||||
|
|
@ -236,6 +245,10 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
}
|
}
|
||||||
return '/';
|
return '/';
|
||||||
case 'DISCONNECTED':
|
case 'DISCONNECTED':
|
||||||
|
if (gamePin) {
|
||||||
|
return role === 'HOST' ? `/host/${gamePin}` : `/play/${gamePin}`;
|
||||||
|
}
|
||||||
|
return '/';
|
||||||
case 'WAITING_TO_REJOIN':
|
case 'WAITING_TO_REJOIN':
|
||||||
if (gamePin) {
|
if (gamePin) {
|
||||||
return `/play/${gamePin}`;
|
return `/play/${gamePin}`;
|
||||||
|
|
@ -411,6 +424,7 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
});
|
});
|
||||||
conn.on('close', () => {
|
conn.on('close', () => {
|
||||||
connectionsRef.current.delete(conn.peer);
|
connectionsRef.current.delete(conn.peer);
|
||||||
|
setConnectedPlayerIds(prev => prev.filter(id => id !== conn.peer));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -504,6 +518,7 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
setGamePin(session.pin);
|
setGamePin(session.pin);
|
||||||
setGameState('DISCONNECTED'); // Show loading state on disconnected screen
|
setGameState('DISCONNECTED'); // Show loading state on disconnected screen
|
||||||
setCurrentPlayerName("Host");
|
setCurrentPlayerName("Host");
|
||||||
|
setConnectedPlayerIds([]); // Reset connected players - they will reconnect
|
||||||
|
|
||||||
const hostData = await fetchHostSession(session.pin, session.hostSecret);
|
const hostData = await fetchHostSession(session.pin, session.hostSecret);
|
||||||
|
|
||||||
|
|
@ -546,24 +561,15 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
|
|
||||||
// Determine which state to restore to
|
// Determine which state to restore to
|
||||||
const savedState = hostData.gameState;
|
const savedState = hostData.gameState;
|
||||||
const restoredPlayers = hostData.players || [];
|
|
||||||
const allAnswered = restoredPlayers.length > 0 && restoredPlayers.every((p: Player) => p.lastAnswerCorrect !== null);
|
|
||||||
|
|
||||||
if (savedState === 'LOBBY') {
|
if (savedState === 'LOBBY') {
|
||||||
setGameState('LOBBY');
|
setGameState('LOBBY');
|
||||||
} else if (savedState === 'PODIUM') {
|
} else if (savedState === 'PODIUM') {
|
||||||
setGameState('PODIUM');
|
setGameState('PODIUM');
|
||||||
} else if (savedState === 'REVEAL') {
|
|
||||||
// Go directly to reveal screen - players may have already seen their results
|
|
||||||
setGameState('REVEAL');
|
|
||||||
} else if (savedState === 'SCOREBOARD') {
|
|
||||||
// Go directly to scoreboard
|
|
||||||
setGameState('SCOREBOARD');
|
|
||||||
} else if (savedState === 'QUESTION' && allAnswered) {
|
|
||||||
// All players answered while host was disconnected - go directly to reveal
|
|
||||||
setGameState('REVEAL');
|
|
||||||
} else {
|
} else {
|
||||||
// For QUESTION or COUNTDOWN states where not everyone answered, show HOST_RECONNECTED to let them resume
|
// For all mid-game states (QUESTION, COUNTDOWN, REVEAL, SCOREBOARD),
|
||||||
|
// show HOST_RECONNECTED first so host can wait for players to reconnect
|
||||||
|
// and then choose when to resume
|
||||||
setGameState('HOST_RECONNECTED');
|
setGameState('HOST_RECONNECTED');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -853,6 +859,7 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
const payload = data.payload as { name: string; reconnect?: boolean; previousId?: string };
|
const payload = data.payload as { name: string; reconnect?: boolean; previousId?: string };
|
||||||
|
|
||||||
connectionsRef.current.set(conn.peer, conn);
|
connectionsRef.current.set(conn.peer, conn);
|
||||||
|
setConnectedPlayerIds(prev => prev.includes(conn.peer) ? prev : [...prev, conn.peer]);
|
||||||
|
|
||||||
const existingByPreviousId = payload.previousId ? playersRef.current.find(p => p.id === payload.previousId) : null;
|
const existingByPreviousId = payload.previousId ? playersRef.current.find(p => p.id === payload.previousId) : null;
|
||||||
const existingByName = playersRef.current.find(p => p.name === payload.name && p.id !== 'host');
|
const existingByName = playersRef.current.find(p => p.name === payload.name && p.id !== 'host');
|
||||||
|
|
@ -899,6 +906,8 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
updatedPlayers = playersRef.current.map(p => p.id === reconnectedPlayer.id ? { ...p, id: conn.peer } : p);
|
updatedPlayers = playersRef.current.map(p => p.id === reconnectedPlayer.id ? { ...p, id: conn.peer } : p);
|
||||||
setPlayers(updatedPlayers);
|
setPlayers(updatedPlayers);
|
||||||
assignedName = reconnectedPlayer.name;
|
assignedName = reconnectedPlayer.name;
|
||||||
|
// Update connected player IDs - remove old ID, new ID already added above
|
||||||
|
setConnectedPlayerIds(prev => prev.filter(id => id !== reconnectedPlayer.id));
|
||||||
if (presenterIdRef.current === reconnectedPlayer.id) {
|
if (presenterIdRef.current === reconnectedPlayer.id) {
|
||||||
setPresenterId(conn.peer);
|
setPresenterId(conn.peer);
|
||||||
}
|
}
|
||||||
|
|
@ -1052,6 +1061,7 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
playersRef.current = updatedPlayers;
|
playersRef.current = updatedPlayers;
|
||||||
setPlayers(updatedPlayers);
|
setPlayers(updatedPlayers);
|
||||||
connectionsRef.current.delete(playerId);
|
connectionsRef.current.delete(playerId);
|
||||||
|
setConnectedPlayerIds(prev => prev.filter(id => id !== playerId));
|
||||||
|
|
||||||
if (presenterIdRef.current === playerId) {
|
if (presenterIdRef.current === playerId) {
|
||||||
const realPlayers = updatedPlayers.filter(p => p.id !== 'host');
|
const realPlayers = updatedPlayers.filter(p => p.id !== 'host');
|
||||||
|
|
@ -1718,7 +1728,7 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, lastAnswerCorrect, currentCorrectShape, selectedOption, currentPlayerScore, currentStreak, currentPlayerId, gameConfig,
|
role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, lastAnswerCorrect, currentCorrectShape, selectedOption, currentPlayerScore, currentStreak, currentPlayerId, gameConfig,
|
||||||
pendingQuizToSave, dismissSavePrompt, sourceQuizId, isReconnecting, currentPlayerName, presenterId,
|
pendingQuizToSave, dismissSavePrompt, sourceQuizId, isReconnecting, currentPlayerName, presenterId, connectedPlayerIds,
|
||||||
startQuizGen, startManualCreation, cancelCreation, 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, setPresenterPlayer, sendAdvance, kickPlayer, leaveGame
|
updateQuizFromEditor, startGameFromEditor, backFromEditor, endGame, attemptReconnect, goHomeFromDisconnected, resumeGame, setPresenterPlayer, sendAdvance, kickPlayer, leaveGame
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue