Add kick player and leave game functionality

- Host can kick players from lobby (removes from game, clears presenter if needed)
- Client can voluntarily leave game
- Fix browser-compatible base64 decoding for document upload (atob vs Buffer)
This commit is contained in:
Joey Yakimowich-Payne 2026-01-19 14:52:57 -07:00
commit 79820f5298
No known key found for this signature in database
GPG key ID: DDF6AF5B21B407D4
7 changed files with 1640 additions and 10 deletions

View file

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Player } from '../types';
import { motion, AnimatePresence } from 'framer-motion';
import { Sparkles, User, X, Link, Check, QrCode, Crown } from 'lucide-react';
import { Sparkles, User, X, Link, Check, QrCode, Crown, LogOut, UserX } from 'lucide-react';
import { QRCodeSVG } from 'qrcode.react';
import { PlayerAvatar } from './PlayerAvatar';
import toast from 'react-hot-toast';
@ -17,9 +17,11 @@ interface LobbyProps {
hostParticipates?: boolean;
presenterId?: string | null;
onSetPresenter?: (playerId: string | null) => void;
onKickPlayer?: (playerId: string) => void;
onLeaveGame?: () => void;
}
export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role, onStart, onEndGame, currentPlayerId, hostParticipates = false, presenterId, onSetPresenter }) => {
export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role, onStart, onEndGame, currentPlayerId, hostParticipates = false, presenterId, onSetPresenter, onKickPlayer, onLeaveGame }) => {
const isHost = role === 'HOST';
const hostPlayer = players.find(p => p.id === 'host');
const realPlayers = players.filter(p => p.id !== 'host');
@ -209,6 +211,18 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
{isPlayerPresenter && (
<span className="text-xs bg-yellow-400 text-yellow-900 px-2 py-0.5 rounded-full">PRESENTER</span>
)}
{onKickPlayer && (
<button
onClick={(e) => {
e.stopPropagation();
onKickPlayer(player.id);
}}
className="ml-1 p-1 rounded-full hover:bg-red-100 text-gray-400 hover:text-red-500 transition-colors"
title="Kick player"
>
<UserX size={16} />
</button>
)}
</motion.div>
);
})}
@ -272,6 +286,19 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
) : (
<p className="text-lg md:text-2xl font-bold opacity-80">Waiting for the host to start...</p>
)}
{onLeaveGame && (
<motion.button
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.3 }}
onClick={onLeaveGame}
className="mt-8 bg-white/20 hover:bg-white/30 text-white px-6 py-3 rounded-full font-bold flex items-center gap-2 transition-all active:scale-95"
>
<LogOut size={20} />
Leave Game
</motion.button>
)}
</div>
)}
</main>