Initial commit
This commit is contained in:
commit
c87ebf0a74
22 changed files with 4973 additions and 0 deletions
156
components/RevealScreen.tsx
Normal file
156
components/RevealScreen.tsx
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Check, X, Flame, Trophy } from 'lucide-react';
|
||||
import { AnswerOption, Player, GameRole } from '../types';
|
||||
import { SHAPES, COLORS } from '../constants';
|
||||
import confetti from 'canvas-confetti';
|
||||
|
||||
interface RevealScreenProps {
|
||||
isCorrect: boolean;
|
||||
pointsEarned: number;
|
||||
newScore: number;
|
||||
streak: number;
|
||||
correctOption: AnswerOption;
|
||||
role: GameRole;
|
||||
}
|
||||
|
||||
export const RevealScreen: React.FC<RevealScreenProps> = ({
|
||||
isCorrect,
|
||||
pointsEarned,
|
||||
newScore,
|
||||
streak,
|
||||
correctOption,
|
||||
role
|
||||
}) => {
|
||||
const isHost = role === 'HOST';
|
||||
|
||||
// Trigger confetti for correct answers
|
||||
useEffect(() => {
|
||||
if (isCorrect && !isHost) {
|
||||
confetti({
|
||||
particleCount: 100,
|
||||
spread: 70,
|
||||
origin: { y: 0.6 },
|
||||
colors: ['#22c55e', '#ffffff', '#fbbf24']
|
||||
});
|
||||
}
|
||||
}, [isCorrect, isHost]);
|
||||
|
||||
// -- HOST VIEW --
|
||||
if (isHost) {
|
||||
const ShapeIcon = SHAPES[correctOption.shape];
|
||||
const colorClass = COLORS[correctOption.color];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-screen bg-gray-900 text-white p-8 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10"></div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="text-4xl font-bold uppercase tracking-widest mb-12 opacity-80"
|
||||
>
|
||||
The correct answer is
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ scale: 0, rotate: -10 }}
|
||||
animate={{ scale: 1, rotate: 0 }}
|
||||
transition={{ type: "spring", bounce: 0.5 }}
|
||||
className={`${colorClass} p-12 rounded-[3rem] shadow-[0_20px_0_rgba(0,0,0,0.3)] flex flex-col items-center max-w-4xl w-full border-8 border-white/20`}
|
||||
>
|
||||
<div className="bg-black/20 p-6 rounded-full mb-6">
|
||||
<ShapeIcon size={80} fill="currentColor" />
|
||||
</div>
|
||||
<h1 className="text-5xl md:text-7xl font-black font-display text-center drop-shadow-md leading-tight">
|
||||
{correctOption.text}
|
||||
</h1>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// -- CLIENT VIEW --
|
||||
const bgColor = isCorrect ? 'bg-[#22c55e]' : 'bg-[#ef4444]';
|
||||
const darkerColor = isCorrect ? 'bg-[#15803d]' : 'bg-[#b91c1c]';
|
||||
const ShapeIcon = SHAPES[correctOption.shape];
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col items-center justify-center h-screen ${bgColor} text-white p-6 relative overflow-hidden transition-colors duration-500`}>
|
||||
|
||||
{/* Dynamic Background Circles */}
|
||||
<motion.div
|
||||
animate={{ scale: [1, 1.2, 1], opacity: [0.1, 0.2, 0.1] }}
|
||||
transition={{ repeat: Infinity, duration: 4 }}
|
||||
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-6 rounded-full shadow-[0_10px_0_rgba(0,0,0,0.2)] mb-8">
|
||||
{isCorrect ? (
|
||||
<Check size={80} className="text-[#22c55e]" strokeWidth={4} />
|
||||
) : (
|
||||
<X size={80} className="text-[#ef4444]" strokeWidth={4} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h1 className="text-6xl md:text-8xl font-black font-display 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"
|
||||
>
|
||||
{isCorrect ? (
|
||||
<div className="bg-black/20 px-8 py-4 rounded-3xl backdrop-blur-sm border-4 border-white/30 flex items-center gap-4 mb-8">
|
||||
<span className="text-4xl font-black">+{pointsEarned}</span>
|
||||
<span className="font-bold uppercase opacity-80">Points</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-2xl font-bold opacity-90 mb-8 max-w-md text-center">
|
||||
Don't worry, you can catch up in the next round!
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Streak Indicator */}
|
||||
{streak > 1 && isCorrect && (
|
||||
<div className="flex items-center gap-2 text-yellow-200 font-black text-2xl animate-pulse">
|
||||
<Flame fill="currentColor" />
|
||||
<span>Answer Streak: {streak}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-8 bg-black/20 px-6 py-2 rounded-xl text-xl font-bold">
|
||||
Total Score: {newScore}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{!isCorrect && (
|
||||
<motion.div
|
||||
initial={{ y: 100 }}
|
||||
animate={{ y: 0 }}
|
||||
transition={{ delay: 0.5, type: 'spring' }}
|
||||
className="absolute bottom-0 left-0 right-0 bg-black/30 backdrop-blur-md p-6 pb-12"
|
||||
>
|
||||
<p className="text-center text-sm font-bold uppercase tracking-widest mb-4 opacity-70">The correct answer was</p>
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<div className={`${COLORS[correctOption.color]} p-3 rounded-xl shadow-lg`}>
|
||||
<ShapeIcon size={24} fill="currentColor" />
|
||||
</div>
|
||||
<span className="text-2xl font-black">{correctOption.text}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue