Initial commit

This commit is contained in:
Joey Yakimowich-Payne 2026-01-13 07:23:30 -07:00
commit c87ebf0a74
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
22 changed files with 4973 additions and 0 deletions

156
components/RevealScreen.tsx Normal file
View 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>
);
};