kaboot/App.tsx

149 lines
No EOL
4.4 KiB
TypeScript

import React from 'react';
import { useGame } from './hooks/useGame';
import { Landing } from './components/Landing';
import { Lobby } from './components/Lobby';
import { GameScreen } from './components/GameScreen';
import { Scoreboard } from './components/Scoreboard';
import { Podium } from './components/Podium';
import { QuizCreator } from './components/QuizCreator';
import { RevealScreen } from './components/RevealScreen';
const FloatingShapes = () => {
// Deterministic "random" for SSR safety if needed, but client-side is fine here
const shapes = [...Array(15)].map((_, i) => ({
left: `${Math.random() * 100}%`,
width: `${Math.random() * 100 + 40}px`,
height: `${Math.random() * 100 + 40}px`,
animationDuration: `${Math.random() * 20 + 15}s`,
animationDelay: `-${Math.random() * 20}s`,
borderRadius: Math.random() > 0.5 ? '50%' : '20%', // Mix of circles and rounded squares
background: 'rgba(255, 255, 255, 0.05)',
}));
return (
<>
{shapes.map((style, i) => (
<div key={i} className="floating-shape" style={style} />
))}
</>
);
};
function App() {
const {
role,
gameState,
quiz,
players,
currentQuestionIndex,
timeLeft,
error,
gamePin,
startQuizGen,
startManualCreation,
finalizeManualQuiz,
joinGame,
startGame,
handleAnswer,
hasAnswered,
lastPointsEarned,
nextQuestion,
currentCorrectShape,
selectedOption
} = useGame();
const currentQ = quiz?.questions[currentQuestionIndex];
// Logic to find correct option, handling both Host (has isCorrect flag) and Client (masked, needs shape)
const correctOpt = currentQ?.options.find(o => {
if (role === 'HOST') return o.isCorrect;
return o.shape === currentCorrectShape;
});
return (
<div className="min-h-screen text-white relative">
<FloatingShapes />
<div className="relative z-10">
{gameState === 'LANDING' || gameState === 'GENERATING' ? (
<Landing
onGenerate={startQuizGen}
onCreateManual={startManualCreation}
onJoin={joinGame}
isLoading={gameState === 'GENERATING'}
error={error}
/>
) : null}
{gameState === 'CREATING' ? (
<QuizCreator
onFinalize={finalizeManualQuiz}
onCancel={() => window.location.reload()}
/>
) : null}
{gameState === 'LOBBY' ? (
<Lobby
quizTitle={quiz?.title || 'OpenHoot'}
players={players}
gamePin={gamePin}
role={role}
onStart={startGame}
/>
) : null}
{(gameState === 'COUNTDOWN' || gameState === 'QUESTION') && quiz ? (
gameState === 'COUNTDOWN' ? (
<div className="flex flex-col items-center justify-center h-screen animate-bounce">
<div className="text-4xl font-display font-bold mb-4">Get Ready!</div>
<div className="text-[12rem] font-black leading-none drop-shadow-[0_10px_0_rgba(0,0,0,0.3)]">
{timeLeft}
</div>
</div>
) : (
<GameScreen
question={quiz.questions[currentQuestionIndex]}
timeLeft={timeLeft}
totalQuestions={quiz.questions.length}
currentQuestionIndex={currentQuestionIndex}
gameState={gameState}
role={role}
onAnswer={handleAnswer}
hasAnswered={hasAnswered}
lastPointsEarned={lastPointsEarned}
/>
)
) : null}
{gameState === 'REVEAL' && correctOpt ? (
<RevealScreen
isCorrect={lastPointsEarned !== null && lastPointsEarned > 0}
pointsEarned={lastPointsEarned || 0}
newScore={0}
streak={0}
correctOption={correctOpt}
selectedOption={selectedOption}
role={role}
/>
) : null}
{gameState === 'SCOREBOARD' ? (
<Scoreboard
players={players}
onNext={nextQuestion}
isHost={role === 'HOST'}
/>
) : null}
{gameState === 'PODIUM' ? (
<Podium
players={players}
onRestart={() => window.location.reload()}
/>
) : null}
</div>
</div>
);
}
export default App;