Show host and early stop

This commit is contained in:
Joey Yakimowich-Payne 2026-01-15 20:26:22 -07:00
commit eee3e7e47b
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
3 changed files with 34 additions and 6 deletions

View file

@ -182,6 +182,7 @@ function App() {
onStart={startGame} onStart={startGame}
onEndGame={role === 'HOST' ? endGame : undefined} onEndGame={role === 'HOST' ? endGame : undefined}
currentPlayerId={currentPlayerId} currentPlayerId={currentPlayerId}
hostParticipates={gameConfig.hostParticipates}
/> />
{auth.isAuthenticated && pendingQuizToSave && ( {auth.isAuthenticated && pendingQuizToSave && (
<SaveQuizPrompt <SaveQuizPrompt

View file

@ -12,10 +12,12 @@ interface LobbyProps {
onStart: () => void; onStart: () => void;
onEndGame?: () => void; onEndGame?: () => void;
currentPlayerId?: string | null; currentPlayerId?: string | null;
hostParticipates?: boolean;
} }
export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role, onStart, onEndGame, currentPlayerId }) => { export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role, onStart, onEndGame, currentPlayerId, hostParticipates = false }) => {
const isHost = role === 'HOST'; const isHost = role === 'HOST';
const hostPlayer = players.find(p => p.id === 'host');
const realPlayers = players.filter(p => p.id !== 'host'); const realPlayers = players.filter(p => p.id !== 'host');
const currentPlayer = currentPlayerId ? players.find(p => p.id === currentPlayerId) : null; const currentPlayer = currentPlayerId ? players.find(p => p.id === currentPlayerId) : null;
@ -48,7 +50,7 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
<> <>
<div className="flex flex-wrap gap-3 md:gap-4 justify-center w-full max-w-6xl pb-24 md:pb-28 overflow-y-auto content-start"> <div className="flex flex-wrap gap-3 md:gap-4 justify-center w-full max-w-6xl pb-24 md:pb-28 overflow-y-auto content-start">
<AnimatePresence> <AnimatePresence>
{realPlayers.length === 0 && ( {realPlayers.length === 0 && !hostParticipates && (
<div className="flex flex-col items-center opacity-60 mt-8 md:mt-12"> <div className="flex flex-col items-center opacity-60 mt-8 md:mt-12">
<div className="bg-white/10 p-4 md:p-6 rounded-full mb-4 animate-bounce"> <div className="bg-white/10 p-4 md:p-6 rounded-full mb-4 animate-bounce">
<Sparkles size={36} className="md:w-12 md:h-12" /> <Sparkles size={36} className="md:w-12 md:h-12" />
@ -56,6 +58,19 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
<div className="text-xl md:text-3xl font-bold font-display text-center px-4">Waiting for players to join...</div> <div className="text-xl md:text-3xl font-bold font-display text-center px-4">Waiting for players to join...</div>
</div> </div>
)} )}
{hostParticipates && hostPlayer && (
<motion.div
key="host"
initial={{ scale: 0, rotate: -10 }}
animate={{ scale: 1, rotate: 0 }}
exit={{ scale: 0, opacity: 0 }}
className="bg-yellow-400 text-black px-4 md:px-6 py-2 md:py-3 rounded-full font-black text-base md:text-xl shadow-[0_4px_0_rgba(0,0,0,0.2)] flex items-center gap-2 md:gap-3 border-b-4 border-yellow-500"
>
<PlayerAvatar seed={hostPlayer.avatarSeed} size={20} />
{hostPlayer.name}
<span className="text-xs bg-black/20 px-2 py-0.5 rounded-full">HOST</span>
</motion.div>
)}
{realPlayers.map((player) => ( {realPlayers.map((player) => (
<motion.div <motion.div
key={player.id} key={player.id}

View file

@ -850,12 +850,18 @@ export const useGame = () => {
}); });
const newScore = Math.max(0, currentPlayer.score + breakdown.total); const newScore = Math.max(0, currentPlayer.score + breakdown.total);
setPlayers(prev => prev.map(p => { const updatedPlayers = playersRef.current.map(p => {
if (p.id !== playerId) return p; if (p.id !== playerId) return p;
return { ...p, score: newScore, previousScore: p.score, streak: newStreak, lastAnswerCorrect: isCorrect, selectedShape, pointsBreakdown: breakdown }; return { ...p, score: newScore, previousScore: p.score, streak: newStreak, lastAnswerCorrect: isCorrect, selectedShape, pointsBreakdown: breakdown };
})); });
setPlayers(updatedPlayers);
conn.send({ type: 'RESULT', payload: { isCorrect, scoreAdded: breakdown.total, newScore, breakdown } }); conn.send({ type: 'RESULT', payload: { isCorrect, scoreAdded: breakdown.total, newScore, breakdown } });
const allAnswered = updatedPlayers.every(p => p.lastAnswerCorrect !== null);
if (allAnswered && gameStateRef.current === 'QUESTION') {
endQuestion();
}
} }
}; };
@ -1369,10 +1375,16 @@ export const useGame = () => {
setCurrentPlayerScore(newScore); setCurrentPlayerScore(newScore);
setCurrentStreak(newStreak); setCurrentStreak(newStreak);
setPlayers(prev => prev.map(p => { const updatedPlayers = playersRef.current.map(p => {
if (p.id !== 'host') return p; if (p.id !== 'host') return p;
return { ...p, score: newScore, previousScore: p.score, streak: newStreak, lastAnswerCorrect: isCorrect, selectedShape: option.shape, pointsBreakdown: breakdown }; return { ...p, score: newScore, previousScore: p.score, streak: newStreak, lastAnswerCorrect: isCorrect, selectedShape: option.shape, pointsBreakdown: breakdown };
})); });
setPlayers(updatedPlayers);
const allAnswered = updatedPlayers.every(p => p.lastAnswerCorrect !== null);
if (allAnswered && gameStateRef.current === 'QUESTION') {
endQuestion();
}
} else { } else {
const option = arg as AnswerOption; const option = arg as AnswerOption;
setSelectedOption(option); setSelectedOption(option);