diff --git a/App.tsx b/App.tsx index 8c2c9fc..f1cb188 100644 --- a/App.tsx +++ b/App.tsx @@ -53,7 +53,9 @@ function App() { lastPointsEarned, nextQuestion, currentCorrectShape, - selectedOption + selectedOption, + currentPlayerScore, + currentStreak } = useGame(); const currentQ = quiz?.questions[currentQuestionIndex]; @@ -123,8 +125,8 @@ function App() { 0} pointsEarned={lastPointsEarned || 0} - newScore={0} - streak={0} + newScore={currentPlayerScore} + streak={currentStreak} correctOption={correctOpt} selectedOption={selectedOption} role={role} diff --git a/components/RevealScreen.tsx b/components/RevealScreen.tsx index 99f0f7f..a501c68 100644 --- a/components/RevealScreen.tsx +++ b/components/RevealScreen.tsx @@ -1,10 +1,31 @@ -import React, { useEffect } from 'react'; -import { motion } from 'framer-motion'; +import React, { useEffect, useState } from 'react'; +import { motion, useSpring, useTransform } 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'; +const AnimatedCounter: React.FC<{ from: number; to: number }> = ({ from, to }) => { + const springValue = useSpring(from, { + stiffness: 50, + damping: 20, + }); + const [displayValue, setDisplayValue] = useState(from); + + useEffect(() => { + springValue.set(to); + }, [to, springValue]); + + useEffect(() => { + const unsubscribe = springValue.on('change', (latest) => { + setDisplayValue(Math.round(Number(latest))); + }); + return unsubscribe; + }, [springValue]); + + return {displayValue}; +}; + interface RevealScreenProps { isCorrect: boolean; pointsEarned: number; @@ -148,7 +169,7 @@ export const RevealScreen: React.FC = ({ )}
- Total Score: {newScore} + Total Score:
diff --git a/hooks/useGame.ts b/hooks/useGame.ts index 4a12e3d..6dca234 100644 --- a/hooks/useGame.ts +++ b/hooks/useGame.ts @@ -17,6 +17,8 @@ export const useGame = () => { const [currentCorrectShape, setCurrentCorrectShape] = useState(null); const [lastPointsEarned, setLastPointsEarned] = useState(null); const [selectedOption, setSelectedOption] = useState(null); + const [currentPlayerScore, setCurrentPlayerScore] = useState(0); + const [currentStreak, setCurrentStreak] = useState(0); const timerRef = useRef | null>(null); const peerRef = useRef(null); @@ -302,6 +304,12 @@ export const useGame = () => { if (data.type === 'RESULT') { setLastPointsEarned(data.payload.scoreAdded); + setCurrentPlayerScore(data.payload.newScore); + if (data.payload.isCorrect) { + setCurrentStreak(prev => prev + 1); + } else { + setCurrentStreak(0); + } } if (data.type === 'TIME_UP') { @@ -329,9 +337,15 @@ export const useGame = () => { const points = isCorrect ? Math.round(POINTS_PER_QUESTION * (timeLeftRef.current / QUESTION_TIME)) : 0; setLastPointsEarned(points); + const hostPlayer = playersRef.current.find(p => p.id === 'host'); + const newScore = (hostPlayer?.score || 0) + points; + const newStreak = isCorrect ? (hostPlayer?.streak || 0) + 1 : 0; + setCurrentPlayerScore(newScore); + setCurrentStreak(newStreak); + setPlayers(prev => prev.map(p => { if (p.id !== 'host') return p; - return { ...p, score: p.score + points, streak: isCorrect ? p.streak + 1 : 0, lastAnswerCorrect: isCorrect }; + return { ...p, score: newScore, streak: newStreak, lastAnswerCorrect: isCorrect }; })); } else { const option = arg as AnswerOption; @@ -355,7 +369,7 @@ export const useGame = () => { }, []); return { - role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, currentCorrectShape, selectedOption, + role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, currentCorrectShape, selectedOption, currentPlayerScore, currentStreak, startQuizGen, startManualCreation, finalizeManualQuiz, joinGame, startGame: startHostGame, handleAnswer, nextQuestion }; }; \ No newline at end of file