diff --git a/src/pages/Trivia.jsx b/src/pages/Trivia.jsx
index 429af08..99ea430 100644
--- a/src/pages/Trivia.jsx
+++ b/src/pages/Trivia.jsx
@@ -17,167 +17,509 @@
* - [ ] Multiplayer / shared score (stub for future)
* - [ ] Extract quiz logic into hook (useQuiz) for reuse/testing
*/
-import { useEffect, useState } from 'react';
-import Loading from '../components/Loading.jsx';
-import ErrorMessage from '../components/ErrorMessage.jsx';
-import Card from '../components/Card.jsx';
-import HeroSection from '../components/HeroSection';
-import Quiz from '../Images/Quiz.jpg';
+
+import { useEffect, useState, useRef } from 'react';
+
+// Mock components for demonstration
+const Loading = () => (
+
+
+
Loading questions...
+
+);
+
+const ErrorMessage = ({ error }) => {
+ if (!error) return null;
+ return (
+
+ Error: {error.message || 'Something went wrong'}
+
+ );
+};
+
+const Card = ({ title, children }) => (
+
+ {title &&
{title}
}
+ {children}
+
+);
+
+const HeroSection = ({ image, title, subtitle }) => (
+
+);
export default function Trivia() {
const [questions, setQuestions] = useState([]);
- const [category, setCategory] = useState('18'); // Science: Computers
- const [difficulty, setDifficulty] = useState('easy'); // Default to easy
+ const [category, setCategory] = useState('18');
+ const [difficulty, setDifficulty] = useState('easy');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
- const [score, setScore] = useState(0);
- const [showReview, setShowReview] = useState(false);
+ const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
+ const [showResults, setShowResults] = useState(false);
const [highScore, setHighScore] = useState(0);
- const [hasSavedForSet, setHasSavedForSet] = useState(false);
+ const [fadeIn, setFadeIn] = useState(true);
+ const [initialized, setInitialized] = useState(false);
+
+ const abortControllerRef = useRef(null);
+ // Load high score only once on mount
useEffect(() => {
- fetchQuestions();
- }, [category, difficulty]);
+ const stored = window.triviaHighScore || 0;
+ setHighScore(stored);
+ }, []);
- // Load high score once on mount
+ // Initial fetch only once on mount
useEffect(() => {
- try {
- const stored =
- typeof window !== 'undefined'
- ? localStorage.getItem('triviaHighScore')
- : null;
- if (stored !== null) setHighScore(parseInt(stored, 10) || 0);
- } catch (_) {
- // ignore storage errors
+ if (!initialized) {
+ setInitialized(true);
+ fetchQuestions();
}
- }, []);
+ }, [initialized]);
async function fetchQuestions() {
+ // Cancel any ongoing request
+ if (abortControllerRef.current) {
+ abortControllerRef.current.abort();
+ }
+
+ abortControllerRef.current = new AbortController();
+
try {
setLoading(true);
setError(null);
- setScore(0);
- setShowReview(false);
- setHasSavedForSet(false);
+ setShowResults(false);
+ setCurrentQuestionIndex(0);
const res = await fetch(
- `https://opentdb.com/api.php?amount=5&category=${category}&difficulty=${difficulty}&type=multiple`
+ `https://opentdb.com/api.php?amount=5&category=${category}&difficulty=${difficulty}&type=multiple`,
+ {
+ signal: abortControllerRef.current.signal
+ }
);
- if (!res.ok) throw new Error('Failed to fetch');
+
+ if (!res.ok) throw new Error('Failed to fetch questions');
const json = await res.json();
+ if (json.response_code === 1) {
+ throw new Error('No questions available for this category/difficulty combination');
+ } else if (json.response_code === 2) {
+ throw new Error('Invalid parameter');
+ } else if (json.response_code === 5) {
+ throw new Error('Rate limit exceeded. Please wait a moment and try again.');
+ }
+
const qs = json.results.map((q) => ({
...q,
answers: shuffle([q.correct_answer, ...q.incorrect_answers]),
picked: null,
}));
setQuestions(qs);
+ setFadeIn(true);
} catch (e) {
- setError(e);
+ if (e.name === 'AbortError') {
+ // Request was cancelled, ignore
+ return;
+ } else if (e.name === 'TimeoutError') {
+ setError(new Error('Request timed out. Please try again.'));
+ } else {
+ setError(e);
+ }
} finally {
setLoading(false);
}
}
function shuffle(arr) {
- return arr.sort(() => Math.random() - 0.5);
+ const newArr = [...arr];
+ for (let i = newArr.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [newArr[i], newArr[j]] = [newArr[j], newArr[i]];
+ }
+ return newArr;
}
- function pick(qIndex, answer) {
+ function pick(answer) {
setQuestions((qs) =>
- qs.map((q, i) => (i === qIndex ? { ...q, picked: answer } : q))
+ qs.map((q, i) => (i === currentQuestionIndex ? { ...q, picked: answer } : q))
);
- if (questions[qIndex].correct_answer === answer) {
- setScore((s) => s + 1);
- }
}
- function decodeHtml(html) {
- const txt = document.createElement('textarea');
- txt.innerHTML = html;
- return txt.value;
+ function nextQuestion() {
+ if (currentQuestionIndex < questions.length - 1) {
+ setFadeIn(false);
+ setTimeout(() => {
+ setCurrentQuestionIndex((i) => i + 1);
+ setFadeIn(true);
+ }, 300);
+ } else {
+ setShowResults(true);
+ updateHighScore();
+ }
}
- const answeredCount = questions.filter((q) => q.picked !== null).length;
- const totalQuestions = questions.length;
- const progressPercent =
- totalQuestions > 0 ? (answeredCount / totalQuestions) * 100 : 0;
+ function previousQuestion() {
+ if (currentQuestionIndex > 0) {
+ setFadeIn(false);
+ setTimeout(() => {
+ setCurrentQuestionIndex((i) => i - 1);
+ setFadeIn(true);
+ }, 300);
+ }
+ }
- const allAnswered = answeredCount === totalQuestions && totalQuestions > 0;
+ function calculateScore() {
+ return questions.filter(q => q.picked === q.correct_answer).length;
+ }
- // Persist high score at end of game (once per question set)
- useEffect(() => {
- if (!allAnswered || hasSavedForSet) return;
+ function updateHighScore() {
+ const score = calculateScore();
if (score > highScore) {
- try {
- if (typeof window !== 'undefined')
- localStorage.setItem('triviaHighScore', String(score));
- } catch (_) {
- // ignore storage errors
- }
+ window.triviaHighScore = score;
setHighScore(score);
}
- setHasSavedForSet(true);
- }, [allAnswered, score, highScore, hasSavedForSet]);
+ }
+
+ function decodeHtml(html) {
+ const txt = document.createElement('textarea');
+ txt.innerHTML = html;
+ return txt.value;
+ }
function resetHighScore() {
- try {
- if (typeof window !== 'undefined')
- localStorage.removeItem('triviaHighScore');
- } catch (_) {
- // ignore storage errors
- }
+ window.triviaHighScore = 0;
setHighScore(0);
}
+ function handleCategoryChange(e) {
+ setCategory(e.target.value);
+ }
+
+ function handleDifficultyChange(e) {
+ setDifficulty(e.target.value);
+ }
+
+ function startNewQuiz() {
+ fetchQuestions();
+ }
+
+ const currentQuestion = questions[currentQuestionIndex];
+ const progressPercent = questions.length > 0
+ ? ((currentQuestionIndex + 1) / questions.length) * 100
+ : 0;
+
+ const score = calculateScore();
+ const canGoNext = currentQuestion && currentQuestion.picked !== null;
+
return (
<>
+
+
- Think Fast, Learn Faster
+ Think Fast, Learn Faster
>
}
subtitle="A trivia playground for curious minds, quick thinkers, and casual know-it-alls"
/>
-
-
-
Trivia Quiz
-
- {difficulty.charAt(0).toUpperCase() + difficulty.slice(1)}
-
+
+
+
+
Trivia Quiz
+
+ {difficulty.charAt(0).toUpperCase() + difficulty.slice(1)}
+
+
+
+
+ Current: {score}/{questions.length}
+
+
+ Best: {highScore}
+
+
- {/* Category + Difficulty Selector */}
-
+
+
+
- {/* Loading / Error */}
{loading &&
}
- {/* Progress Bar */}
- {totalQuestions > 0 && (
-
-
- Progress: {answeredCount} / {totalQuestions} answered
-
-
-
0 && !showResults && (
+
- )}
-
- {/* Score + Buttons */}
-
-
- Score: {score} | Best: {highScore}
-
-
-
-
+
+ Question {currentQuestionIndex + 1} of {questions.length}
+
- {/* Quiz Cards */}
- {questions.map((q, idx) => (
-
-
- {q.answers.map((a) => {
- const isPicked = q.picked === a;
- const isCorrect = a === q.correct_answer;
- let btnClass = '';
-
- if (showReview) {
- btnClass = isCorrect
- ? 'correct'
- : isPicked
- ? 'wrong'
- : 'neutral';
- } else if (isPicked) {
- btnClass = isCorrect ? 'correct' : 'wrong';
- }
+
+
+ {currentQuestion.answers.map((answer) => {
+ const isPicked = currentQuestion.picked === answer;
- return (
-
-
+ return (
-
+ );
+ })}
+
+
+
+
+
+
+
+
+ )}
+
+ {showResults && (
+
+
+ 🎯 Quiz Complete!
+
+ {score} / {questions.length}
+
+
+ {score === questions.length
+ ? '🌟 Perfect score! You\'re a trivia master!'
+ : score >= questions.length * 0.7
+ ? '🎉 Great job! Well done!'
+ : score >= questions.length * 0.5
+ ? '👍 Good effort! Keep practicing!'
+ : '💪 Keep learning and try again!'}
+
+ {score > highScore && (
+
+ 🏆 New High Score!
+
+ )}
+
+ Best Score: {highScore}
+
+
+
+
+
+ {questions.map((q, idx) => {
+ const isCorrect = q.picked === q.correct_answer;
+ return (
+
+
+ Question {idx + 1}: {decodeHtml(q.question)}
+
+
+ {q.answers.map((answer) => {
+ const isPicked = q.picked === answer;
+ const isCorrectAnswer = answer === q.correct_answer;
+ let className = 'review-answer';
+
+ if (isCorrectAnswer) {
+ className += ' result-correct';
+ } else if (isPicked) {
+ className += ' result-wrong';
+ } else {
+ className += ' result-neutral';
+ }
+
+ return (
+
+ {decodeHtml(answer)}
+ {isCorrectAnswer && ' ✓ (Correct Answer)'}
+ {isPicked && !isCorrectAnswer && ' ✗ (Your Answer)'}
+
+ );
+ })}
+
+
+ {isCorrect ? '✓ You got this right!' : '✗ You got this wrong'}
+
+
);
})}
-
-
- ))}
-
- {/* Review Section */}
- {showReview && (
-
-
🎯 Quiz Complete!
-
- Final Score: {score} / {totalQuestions}
-
-
Best Score: {highScore}
-
+
)}
>
);
-}
+}
\ No newline at end of file