Fix host screen

This commit is contained in:
Joey Yakimowich-Payne 2026-01-25 08:05:57 -07:00
commit 5242f8d1f3
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
3 changed files with 44 additions and 8 deletions

View file

@ -299,6 +299,7 @@ function App() {
onNext={showScoreboard} onNext={showScoreboard}
isPresenter={currentPlayerId === presenterId} isPresenter={currentPlayerId === presenterId}
onPresenterAdvance={() => sendAdvance('SCOREBOARD')} onPresenterAdvance={() => sendAdvance('SCOREBOARD')}
hostParticipates={gameConfig.hostParticipates}
/> />
) : currentPlayerName ? ( ) : currentPlayerName ? (
<WaitingToRejoin <WaitingToRejoin

View file

@ -37,6 +37,7 @@ interface RevealScreenProps {
onNext?: () => void; onNext?: () => void;
isPresenter?: boolean; isPresenter?: boolean;
onPresenterAdvance?: () => void; onPresenterAdvance?: () => void;
hostParticipates?: boolean;
} }
export const RevealScreen: React.FC<RevealScreenProps> = ({ export const RevealScreen: React.FC<RevealScreenProps> = ({
@ -49,10 +50,13 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
role, role,
onNext, onNext,
isPresenter = false, isPresenter = false,
onPresenterAdvance onPresenterAdvance,
hostParticipates = false
}) => { }) => {
const isHost = role === 'HOST'; const isHost = role === 'HOST';
const canAdvance = isHost || isPresenter; const canAdvance = isHost || isPresenter;
// When host is participating, show client-style feedback view instead of presentation view
const showPlayerFeedback = !isHost || hostParticipates;
// Trigger confetti for correct answers // Trigger confetti for correct answers
useEffect(() => { useEffect(() => {
@ -66,7 +70,8 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
} }
}, [isCorrect, isHost]); }, [isCorrect, isHost]);
if (isHost) { // Host spectating (not participating) - show presentation view with big answer card
if (isHost && !hostParticipates) {
const ShapeIcon = SHAPES[correctOption.shape]; const ShapeIcon = SHAPES[correctOption.shape];
const colorClass = COLORS[correctOption.color]; const colorClass = COLORS[correctOption.color];
@ -196,7 +201,7 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
</motion.div> </motion.div>
{!isCorrect && ( {!isCorrect && (
<div className={`absolute bottom-0 left-0 right-0 overflow-hidden z-20 ${isPresenter ? 'pb-20' : ''}`}> <div className={`absolute bottom-0 left-0 right-0 overflow-hidden z-20 ${(isHost || isPresenter) ? 'pb-20' : ''}`}>
<motion.div <motion.div
initial={{ y: "100%" }} initial={{ y: "100%" }}
animate={{ y: 0 }} animate={{ y: 0 }}
@ -229,16 +234,31 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
</div> </div>
)} )}
{isPresenter && onPresenterAdvance && ( {/* Host participating gets the continue button */}
{isHost && onNext && (
<motion.button
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 }}
onClick={onNext}
className="fixed bottom-6 left-1/2 -translate-x-1/2 bg-white text-gray-900 px-6 md:px-8 py-3 md:py-4 rounded-xl md:rounded-2xl text-base md:text-xl font-black shadow-[0_6px_0_rgba(0,0,0,0.3)] active:shadow-none active:translate-y-[6px] transition-all flex items-center gap-2 hover:bg-gray-100 z-30 cursor-pointer"
>
Continue to Scoreboard
<ChevronRight className="w-5 h-5 md:w-7 md:h-7" strokeWidth={3} />
</motion.button>
)}
{/* Presenter (non-host) gets the continue button */}
{!isHost && isPresenter && onPresenterAdvance && (
<motion.button <motion.button
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 }} transition={{ delay: 0.5 }}
onClick={onPresenterAdvance} onClick={onPresenterAdvance}
className="fixed bottom-6 left-1/2 -translate-x-1/2 bg-white text-gray-900 px-8 py-4 rounded-2xl text-xl font-black shadow-[0_6px_0_rgba(0,0,0,0.3)] active:shadow-none active:translate-y-[6px] transition-all flex items-center gap-2 hover:bg-gray-100 z-30 cursor-pointer" className="fixed bottom-6 left-1/2 -translate-x-1/2 bg-white text-gray-900 px-6 md:px-8 py-3 md:py-4 rounded-xl md:rounded-2xl text-base md:text-xl font-black shadow-[0_6px_0_rgba(0,0,0,0.3)] active:shadow-none active:translate-y-[6px] transition-all flex items-center gap-2 hover:bg-gray-100 z-30 cursor-pointer"
> >
Continue to Scoreboard Continue to Scoreboard
<ChevronRight size={28} strokeWidth={3} /> <ChevronRight className="w-5 h-5 md:w-7 md:h-7" strokeWidth={3} />
</motion.button> </motion.button>
)} )}
</div> </div>

View file

@ -544,11 +544,26 @@ export const useGame = (defaultGameConfig?: GameConfig) => {
await updateHostPeerId(session.pin, session.hostSecret!, peerId); await updateHostPeerId(session.pin, session.hostSecret!, peerId);
} }
if (hostData.gameState === 'LOBBY') { // Determine which state to restore to
const savedState = hostData.gameState;
const restoredPlayers = hostData.players || [];
const allAnswered = restoredPlayers.length > 0 && restoredPlayers.every((p: Player) => p.lastAnswerCorrect !== null);
if (savedState === 'LOBBY') {
setGameState('LOBBY'); setGameState('LOBBY');
} else if (hostData.gameState === '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
setGameState('HOST_RECONNECTED'); setGameState('HOST_RECONNECTED');
} }