import { useEffect, useRef, useState } from 'react'; import { QuestionsProgress } from './QuestionsProgress'; import { CheckCircle, SkipForward, Sparkles } from 'lucide-react'; import { QuestionCard } from './QuestionCard'; import { QuestionLoader } from './QuestionLoader'; import { isLoggedIn } from '../../lib/jwt'; import type { QuestionType } from '../../lib/question-group'; import { httpGet, httpPut } from '../../lib/http'; import { useToast } from '../../hooks/use-toast'; import { QuestionFinished } from './QuestionFinished'; import { Confetti } from '../Confetti'; type UserQuestionProgress = { know: string[]; dontKnow: string[]; skip: string[]; }; export type QuestionProgressType = keyof UserQuestionProgress; type QuestionsListProps = { groupId: string; questions: QuestionType[]; }; export function QuestionsList(props: QuestionsListProps) { const { questions: unshuffledQuestions, groupId } = props; const toast = useToast(); const [isLoading, setIsLoading] = useState(true); const [showConfetti, setShowConfetti] = useState(false); const [questions, setQuestions] = useState(); const [pendingQuestions, setPendingQuestions] = useState([]); const [userProgress, setUserProgress] = useState(); const containerRef = useRef(null); async function fetchUserProgress(): Promise< UserQuestionProgress | undefined > { if (!isLoggedIn()) { return; } const { response, error } = await httpGet( `${ import.meta.env.PUBLIC_API_URL }/v1-get-user-question-progress/${groupId}` ); if (error) { toast.error(error.message || 'Error fetching user progress'); return; } return response; } async function loadQuestions() { const userProgress = await fetchUserProgress(); setUserProgress(userProgress); const knownQuestions = userProgress?.know || []; const didNotKnowQuestions = userProgress?.dontKnow || []; const skipQuestions = userProgress?.skip || []; const pendingQuestions = unshuffledQuestions.filter((question) => { return ( !knownQuestions.includes(question.id) && !didNotKnowQuestions.includes(question.id) && !skipQuestions.includes(question.id) ); }); // Shuffle and set pending questions setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5)); setQuestions(unshuffledQuestions); setIsLoading(false); } async function resetProgress(type: QuestionProgressType | 'reset' = 'reset') { let knownQuestions = userProgress?.know || []; let didNotKnowQuestions = userProgress?.dontKnow || []; let skipQuestions = userProgress?.skip || []; if (!isLoggedIn()) { if (type === 'know') { knownQuestions = []; } else if (type === 'dontKnow') { didNotKnowQuestions = []; } else if (type === 'skip') { skipQuestions = []; } else if (type === 'reset') { knownQuestions = []; didNotKnowQuestions = []; skipQuestions = []; } } else { setIsLoading(true); const { response, error } = await httpPut( `${ import.meta.env.PUBLIC_API_URL }/v1-reset-question-progress/${groupId}`, { status: type, } ); if (error) { toast.error(error.message || 'Error resetting progress'); return; } knownQuestions = response?.know || []; didNotKnowQuestions = response?.dontKnow || []; skipQuestions = response?.skip || []; } const pendingQuestions = unshuffledQuestions.filter((question) => { return ( !knownQuestions.includes(question.id) && !didNotKnowQuestions.includes(question.id) && !skipQuestions.includes(question.id) ); }); setUserProgress({ know: knownQuestions, dontKnow: didNotKnowQuestions, skip: skipQuestions, }); setPendingQuestions(pendingQuestions.sort(() => Math.random() - 0.5)); setIsLoading(false); } async function updateQuestionStatus( status: QuestionProgressType, questionId: string ) { setIsLoading(true); let newProgress = userProgress || { know: [], dontKnow: [], skip: [] }; if (!isLoggedIn()) { if (status === 'know') { newProgress.know.push(questionId); } else if (status == 'dontKnow') { newProgress.dontKnow.push(questionId); } else if (status == 'skip') { newProgress.skip.push(questionId); } } else { const { response, error } = await httpPut( `${ import.meta.env.PUBLIC_API_URL }/v1-update-question-status/${groupId}`, { status, questionId, questionGroupId: groupId, } ); if (error || !response) { toast.error(error?.message || 'Error marking question status'); return; } newProgress = response; } const updatedQuestionList = pendingQuestions.filter( (q) => q.id !== questionId ); setUserProgress(newProgress); setPendingQuestions(updatedQuestionList); setIsLoading(false); if (updatedQuestionList.length === 0) { setShowConfetti(true); } } useEffect(() => { loadQuestions().then(() => null); }, [unshuffledQuestions]); const knowCount = userProgress?.know.length || 0; const dontKnowCount = userProgress?.dontKnow.length || 0; const skipCount = userProgress?.skip.length || 0; const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0; const currQuestion = pendingQuestions[0]; const hasFinished = !isLoading && hasProgress && !currQuestion; return (
{ resetProgress('reset').finally(() => null); }} /> {showConfetti && containerRef.current && ( { setShowConfetti(false); }} /> )}
{hasFinished && ( { resetProgress(type).finally(() => null); }} /> )} {!isLoading && currQuestion && } {isLoading && }
); }