Fix screen issues
This commit is contained in:
parent
6f0dc86c82
commit
2a8811e9dc
2 changed files with 180 additions and 132 deletions
|
|
@ -254,38 +254,85 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
|
|||
</motion.div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center flex-1 text-center p-4 md:p-8">
|
||||
<motion.div
|
||||
initial={{ scale: 0.5 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: 'spring', bounce: 0.6 }}
|
||||
className={`bg-white p-6 md:p-8 rounded-2xl md:rounded-[2rem] shadow-[0_10px_0_rgba(0,0,0,0.1)] mb-4 md:mb-8 relative ${isPresenter ? 'ring-4 ring-yellow-400' : ''}`}
|
||||
>
|
||||
{isPresenter && (
|
||||
<div className="absolute -top-3 -right-3 bg-yellow-400 p-2 rounded-full shadow-lg">
|
||||
<Crown size={24} className="text-yellow-900" />
|
||||
<div className="flex flex-col items-center flex-1 text-center p-4 md:p-8 overflow-hidden">
|
||||
<div className="flex flex-col items-center shrink-0">
|
||||
<motion.div
|
||||
initial={{ scale: 0.5 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ type: 'spring', bounce: 0.6 }}
|
||||
className={`bg-white p-5 md:p-8 rounded-2xl md:rounded-[2rem] shadow-[0_10px_0_rgba(0,0,0,0.1)] mb-3 md:mb-6 relative ${isPresenter ? 'ring-4 ring-yellow-400' : ''}`}
|
||||
>
|
||||
{isPresenter && (
|
||||
<div className="absolute -top-3 -right-3 bg-yellow-400 p-2 rounded-full shadow-lg">
|
||||
<Crown size={24} className="text-yellow-900" />
|
||||
</div>
|
||||
)}
|
||||
{currentPlayer ? (
|
||||
<PlayerAvatar seed={currentPlayer.avatarSeed} size={48} className="md:w-20 md:h-20" />
|
||||
) : (
|
||||
<User size={48} strokeWidth={2.5} className="text-theme-primary md:w-20 md:h-20" />
|
||||
)}
|
||||
</motion.div>
|
||||
<h2 className="text-2xl md:text-5xl font-black mb-1 md:mb-4 font-display">
|
||||
{currentPlayer?.name || "You're in!"}
|
||||
</h2>
|
||||
{isPresenter ? (
|
||||
<div className="flex flex-col items-center gap-1 md:gap-2">
|
||||
<span className="bg-yellow-400 text-yellow-900 px-3 md:px-4 py-1 rounded-full font-bold text-xs md:text-sm flex items-center gap-2">
|
||||
<Crown size={14} className="md:w-4 md:h-4" />
|
||||
You are the Presenter
|
||||
</span>
|
||||
<p className="text-sm md:text-xl font-bold opacity-80">You can advance screens during the game</p>
|
||||
</div>
|
||||
)}
|
||||
{currentPlayer ? (
|
||||
<PlayerAvatar seed={currentPlayer.avatarSeed} size={60} className="md:w-20 md:h-20" />
|
||||
) : (
|
||||
<User size={60} strokeWidth={2.5} className="text-theme-primary md:w-20 md:h-20" />
|
||||
<p className="text-sm md:text-2xl font-bold opacity-80">Waiting for the host to start...</p>
|
||||
)}
|
||||
</motion.div>
|
||||
<h2 className="text-3xl md:text-5xl font-black mb-2 md:mb-4 font-display">
|
||||
{currentPlayer?.name || "You're in!"}
|
||||
</h2>
|
||||
{isPresenter ? (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<span className="bg-yellow-400 text-yellow-900 px-4 py-1 rounded-full font-bold text-sm flex items-center gap-2">
|
||||
<Crown size={16} />
|
||||
You are the Presenter
|
||||
</span>
|
||||
<p className="text-lg md:text-xl font-bold opacity-80">You can advance screens during the game</p>
|
||||
</div>
|
||||
|
||||
{/* Player list */}
|
||||
<div className="mt-4 md:mt-6 flex-1 min-h-0 w-full max-w-md overflow-hidden flex flex-col">
|
||||
<div className="text-xs md:text-sm font-bold uppercase tracking-widest opacity-60 mb-2 md:mb-3">
|
||||
{players.length} Player{players.length !== 1 ? 's' : ''} Connected
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-lg md:text-2xl font-bold opacity-80">Waiting for the host to start...</p>
|
||||
)}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="flex flex-wrap gap-2 justify-center pb-20">
|
||||
<AnimatePresence>
|
||||
{players.map((player) => {
|
||||
const isCurrentPlayer = player.id === currentPlayerId;
|
||||
const isPlayerHost = player.id === 'host';
|
||||
const isPlayerPresenter = player.id === presenterId;
|
||||
return (
|
||||
<motion.div
|
||||
key={player.id}
|
||||
initial={{ scale: 0, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0, opacity: 0 }}
|
||||
className={`px-3 py-1.5 rounded-full font-bold text-sm flex items-center gap-2 ${
|
||||
isCurrentPlayer
|
||||
? 'bg-white text-theme-primary'
|
||||
: isPlayerHost
|
||||
? 'bg-yellow-400 text-black'
|
||||
: 'bg-white/20 text-white'
|
||||
}`}
|
||||
>
|
||||
{isPlayerPresenter && !isPlayerHost && (
|
||||
<Crown size={12} className="text-yellow-400" />
|
||||
)}
|
||||
<PlayerAvatar seed={player.avatarSeed} size={16} />
|
||||
<span className="max-w-[100px] truncate">{player.name}</span>
|
||||
{isPlayerHost && (
|
||||
<span className="text-[10px] bg-black/20 px-1.5 py-0.5 rounded-full">HOST</span>
|
||||
)}
|
||||
{isCurrentPlayer && !isPlayerHost && (
|
||||
<span className="text-[10px] bg-theme-primary/20 px-1.5 py-0.5 rounded-full">YOU</span>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{onLeaveGame && (
|
||||
<motion.button
|
||||
|
|
@ -293,7 +340,7 @@ export const Lobby: React.FC<LobbyProps> = ({ quizTitle, players, gamePin, role,
|
|||
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"
|
||||
className="fixed bottom-4 md:bottom-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
|
||||
|
|
|
|||
|
|
@ -129,11 +129,12 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
|
|||
|
||||
// -- CLIENT VIEW --
|
||||
const bgColor = isCorrect ? 'bg-[#22c55e]' : 'bg-[#ef4444]';
|
||||
const darkerColor = isCorrect ? 'bg-[#15803d]' : 'bg-[#b91c1c]';
|
||||
const ShapeIcon = SHAPES[correctOption.shape];
|
||||
const showContinueButton = (isHost && onNext) || (!isHost && isPresenter && onPresenterAdvance);
|
||||
const handleContinue = isHost ? onNext : onPresenterAdvance;
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col items-center justify-center h-screen ${bgColor} text-white p-4 md:p-6 relative overflow-hidden transition-colors duration-500`}>
|
||||
<div className={`flex flex-col h-screen ${bgColor} text-white relative overflow-hidden transition-colors duration-500`}>
|
||||
|
||||
{/* Dynamic Background Circles */}
|
||||
<motion.div
|
||||
|
|
@ -142,119 +143,119 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
|
|||
className="absolute w-[800px] h-[800px] bg-white rounded-full blur-3xl opacity-10 pointer-events-none"
|
||||
/>
|
||||
|
||||
<motion.div
|
||||
initial={{ scale: 0.5, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ type: "spring", bounce: 0.6 }}
|
||||
className="flex flex-col items-center z-10"
|
||||
>
|
||||
<div className="bg-white p-4 md:p-6 rounded-full shadow-[0_6px_0_rgba(0,0,0,0.2)] md:shadow-[0_10px_0_rgba(0,0,0,0.2)] mb-4 md:mb-8">
|
||||
{isCorrect ? (
|
||||
<Check className="w-12 h-12 md:w-20 md:h-20 text-[#22c55e]" strokeWidth={4} />
|
||||
) : (
|
||||
<X className="w-12 h-12 md:w-20 md:h-20 text-[#ef4444]" strokeWidth={4} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl md:text-8xl font-black font-display mb-2 md:mb-4 drop-shadow-lg text-center">
|
||||
{isCorrect ? "Correct!" : "Incorrect"}
|
||||
</h1>
|
||||
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="flex flex-col items-center"
|
||||
{/* Main content - takes available space above bottom sheet */}
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-4 md:p-6 z-10">
|
||||
<motion.div
|
||||
initial={{ scale: 0.5, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ type: "spring", bounce: 0.6 }}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
{isCorrect ? (
|
||||
<div className="flex flex-col items-center gap-2 md:gap-4 mb-4 md:mb-8">
|
||||
<div className="bg-black/20 px-4 md:px-8 py-2 md:py-4 rounded-2xl md:rounded-3xl backdrop-blur-sm border-2 md:border-4 border-white/30 flex items-center gap-2 md:gap-4">
|
||||
<span className="text-2xl md:text-4xl font-black">+{pointsEarned}</span>
|
||||
<span className="text-sm md:text-base font-bold uppercase opacity-80">Points</span>
|
||||
</div>
|
||||
{correctOption.reason && (
|
||||
<p className="text-center text-sm md:text-lg opacity-90 max-w-md bg-black/10 px-3 md:px-4 py-2 rounded-xl">
|
||||
{correctOption.reason}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-base md:text-2xl font-bold opacity-90 mb-4 md:mb-8 max-w-xs md:max-w-md text-center px-4">
|
||||
Don't worry, you can catch up in the next round!
|
||||
</div>
|
||||
)}
|
||||
<div className="bg-white p-3 md:p-6 rounded-full shadow-[0_6px_0_rgba(0,0,0,0.2)] md:shadow-[0_10px_0_rgba(0,0,0,0.2)] mb-3 md:mb-8">
|
||||
{isCorrect ? (
|
||||
<Check className="w-10 h-10 md:w-20 md:h-20 text-[#22c55e]" strokeWidth={4} />
|
||||
) : (
|
||||
<X className="w-10 h-10 md:w-20 md:h-20 text-[#ef4444]" strokeWidth={4} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Streak Indicator */}
|
||||
{streak > 1 && isCorrect && (
|
||||
<div className="flex items-center gap-2 text-yellow-200 font-black text-lg md:text-2xl animate-pulse">
|
||||
<Flame className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" />
|
||||
<span>Answer Streak: {streak}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-4 md:mt-8 bg-black/20 px-4 md:px-6 py-2 rounded-xl text-base md:text-xl font-bold">
|
||||
Total Score: <AnimatedCounter from={newScore - (pointsEarned || 0)} to={newScore} />
|
||||
</div>
|
||||
</motion.div>
|
||||
<h1 className="text-3xl md:text-8xl font-black font-display mb-1 md:mb-4 drop-shadow-lg text-center">
|
||||
{isCorrect ? "Correct!" : "Incorrect"}
|
||||
</h1>
|
||||
|
||||
|
||||
</motion.div>
|
||||
|
||||
{!isCorrect && (
|
||||
<div className={`absolute bottom-0 left-0 right-0 overflow-hidden z-20 ${(isHost || isPresenter) ? 'pb-16 md:pb-20' : ''}`}>
|
||||
<motion.div
|
||||
initial={{ y: "100%" }}
|
||||
animate={{ y: 0 }}
|
||||
transition={{ delay: 0.5, type: 'spring' }}
|
||||
className="bg-black/30 backdrop-blur-md p-4 md:p-6 pb-8 md:pb-12"
|
||||
>
|
||||
{selectedOption && selectedOption.reason && (
|
||||
<div className="mb-3 md:mb-4 pb-3 md:pb-4 border-b border-white/20">
|
||||
<p className="text-center text-xs md:text-sm font-bold uppercase tracking-widest mb-1 md:mb-2 opacity-70">Why your answer was wrong</p>
|
||||
<p className="text-center text-xs md:text-sm opacity-90 max-w-md mx-auto">
|
||||
{selectedOption.reason}
|
||||
</p>
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
{isCorrect ? (
|
||||
<div className="flex flex-col items-center gap-2 md:gap-4 mb-2 md:mb-8">
|
||||
<div className="bg-black/20 px-4 md:px-8 py-2 md:py-4 rounded-2xl md:rounded-3xl backdrop-blur-sm border-2 md:border-4 border-white/30 flex items-center gap-2 md:gap-4">
|
||||
<span className="text-2xl md:text-4xl font-black">+{pointsEarned}</span>
|
||||
<span className="text-sm md:text-base font-bold uppercase opacity-80">Points</span>
|
||||
</div>
|
||||
{correctOption.reason && (
|
||||
<p className="text-center text-sm md:text-lg opacity-90 max-w-md bg-black/10 px-3 md:px-4 py-2 rounded-xl">
|
||||
{correctOption.reason}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-center text-xs md:text-sm font-bold uppercase tracking-widest mb-2 md:mb-4 opacity-70">The correct answer was</p>
|
||||
<div className="flex flex-col items-center gap-2 md:gap-3">
|
||||
<div className="flex items-center gap-3 md:gap-4">
|
||||
<div className={`${COLORS[correctOption.color]} p-2 md:p-3 rounded-lg md:rounded-xl shadow-lg`}>
|
||||
<ShapeIcon className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" />
|
||||
</div>
|
||||
<span className="text-xl md:text-2xl font-black">{correctOption.text}</span>
|
||||
</div>
|
||||
{correctOption.reason && (
|
||||
<p className="text-center text-xs md:text-sm opacity-80 max-w-md mt-1 md:mt-2">
|
||||
{correctOption.reason}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
) : (
|
||||
<div className="text-sm md:text-2xl font-bold opacity-90 mb-2 md:mb-8 max-w-xs md:max-w-md text-center px-4">
|
||||
Don't worry, you'll get the next one!
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 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"
|
||||
{/* Streak Indicator */}
|
||||
{streak > 1 && isCorrect && (
|
||||
<div className="flex items-center gap-2 text-yellow-200 font-black text-lg md:text-2xl animate-pulse">
|
||||
<Flame className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" />
|
||||
<span>Answer Streak: {streak}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-2 md:mt-8 bg-black/20 px-4 md:px-6 py-1.5 md:py-2 rounded-xl text-sm md:text-xl font-bold">
|
||||
Total Score: <AnimatedCounter from={newScore - (pointsEarned || 0)} to={newScore} />
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Bottom sheet for incorrect answers - includes button inside */}
|
||||
{!isCorrect && (
|
||||
<motion.div
|
||||
initial={{ y: "100%" }}
|
||||
animate={{ y: 0 }}
|
||||
transition={{ delay: 0.5, type: 'spring' }}
|
||||
className="bg-black/30 backdrop-blur-md p-4 md:p-6 z-20 shrink-0"
|
||||
>
|
||||
Continue to Scoreboard
|
||||
<ChevronRight className="w-5 h-5 md:w-7 md:h-7" strokeWidth={3} />
|
||||
</motion.button>
|
||||
{selectedOption && selectedOption.reason && (
|
||||
<div className="mb-3 md:mb-4 pb-3 md:pb-4 border-b border-white/20">
|
||||
<p className="text-center text-xs md:text-sm font-bold uppercase tracking-widest mb-1 md:mb-2 opacity-70">Why your answer was wrong</p>
|
||||
<p className="text-center text-xs md:text-sm opacity-90 max-w-md mx-auto">
|
||||
{selectedOption.reason}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-center text-xs md:text-sm font-bold uppercase tracking-widest mb-2 md:mb-3 opacity-70">The correct answer was</p>
|
||||
<div className="flex flex-col items-center gap-2 md:gap-3">
|
||||
<div className="flex items-center gap-3 md:gap-4">
|
||||
<div className={`${COLORS[correctOption.color]} p-2 md:p-3 rounded-lg md:rounded-xl shadow-lg`}>
|
||||
<ShapeIcon className="w-5 h-5 md:w-6 md:h-6" fill="currentColor" />
|
||||
</div>
|
||||
<span className="text-lg md:text-2xl font-black">{correctOption.text}</span>
|
||||
</div>
|
||||
{correctOption.reason && (
|
||||
<p className="text-center text-xs md:text-sm opacity-80 max-w-md">
|
||||
{correctOption.reason}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Continue button inside bottom sheet for incorrect answers */}
|
||||
{showContinueButton && (
|
||||
<motion.button
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.7 }}
|
||||
onClick={handleContinue}
|
||||
className="mt-4 w-full bg-gray-900 text-white px-6 py-3 md:py-4 rounded-xl md:rounded-2xl text-base md:text-xl font-black shadow-[0_4px_0_rgba(0,0,0,0.5)] active:shadow-none active:translate-y-[4px] transition-all flex items-center justify-center gap-2 cursor-pointer"
|
||||
>
|
||||
Continue to Scoreboard
|
||||
<ChevronRight className="w-5 h-5 md:w-6 md:h-6" strokeWidth={3} />
|
||||
</motion.button>
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Presenter (non-host) gets the continue button */}
|
||||
{!isHost && isPresenter && onPresenterAdvance && (
|
||||
{/* Continue button for correct answers - fixed at bottom */}
|
||||
{isCorrect && showContinueButton && (
|
||||
<motion.button
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
onClick={onPresenterAdvance}
|
||||
onClick={handleContinue}
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue