Change confetti to show on completion of quiz

pull/4459/head
Kamran Ahmed 1 year ago
parent 437d879af3
commit 1ed54bad90
  1. 11
      src/components/Confetti.tsx
  2. 76
      src/components/Questions/QuestionsList.tsx
  3. 2
      src/pages/questions/[questionGroupId].astro

@ -9,12 +9,13 @@ type ConfettiPosition = {
}; };
type ConfettiProps = { type ConfettiProps = {
element: HTMLElement | null; pieces?: number;
onDone: () => void; element?: HTMLElement | null;
onDone?: () => void;
}; };
export function Confetti(props: ConfettiProps) { export function Confetti(props: ConfettiProps) {
const { element, onDone } = props; const { element = document.body, onDone = () => null, pieces = 40 } = props;
const [confettiPos, setConfettiPos] = useState< const [confettiPos, setConfettiPos] = useState<
undefined | ConfettiPosition undefined | ConfettiPosition
@ -48,7 +49,7 @@ export function Confetti(props: ConfettiProps) {
return ( return (
<ReactConfetti <ReactConfetti
height={document.body.scrollHeight} height={document.body.scrollHeight}
numberOfPieces={40} numberOfPieces={pieces}
recycle={false} recycle={false}
onConfettiComplete={(confettiInstance) => { onConfettiComplete={(confettiInstance) => {
setConfettiPos(undefined); setConfettiPos(undefined);
@ -56,7 +57,7 @@ export function Confetti(props: ConfettiProps) {
}} }}
initialVelocityX={4} initialVelocityX={4}
initialVelocityY={8} initialVelocityY={8}
tweenDuration={25} tweenDuration={10}
confettiSource={{ confettiSource={{
x: confettiPos.x, x: confettiPos.x,
y: confettiPos.y, y: confettiPos.y,

@ -5,10 +5,10 @@ import { QuestionCard } from './QuestionCard';
import { QuestionLoader } from './QuestionLoader'; import { QuestionLoader } from './QuestionLoader';
import { isLoggedIn } from '../../lib/jwt'; import { isLoggedIn } from '../../lib/jwt';
import type { QuestionType } from '../../lib/question-group'; import type { QuestionType } from '../../lib/question-group';
import { Confetti } from '../Confetti';
import { httpGet, httpPut } from '../../lib/http'; import { httpGet, httpPut } from '../../lib/http';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { QuestionFinished } from './QuestionFinished'; import { QuestionFinished } from './QuestionFinished';
import { Confetti } from '../Confetti';
type UserQuestionProgress = { type UserQuestionProgress = {
know: string[]; know: string[];
@ -29,25 +29,12 @@ export function QuestionsList(props: QuestionsListProps) {
const toast = useToast(); const toast = useToast();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [confettiEl, setConfettiEl] = useState<HTMLElement | null>(null); const [showConfetti, setShowConfetti] = useState(false);
const [questions, setQuestions] = useState<QuestionType[]>(); const [questions, setQuestions] = useState<QuestionType[]>();
const [pendingQuestions, setPendingQuestions] = useState<QuestionType[]>([]); const [pendingQuestions, setPendingQuestions] = useState<QuestionType[]>([]);
const [userProgress, setUserProgress] = useState<UserQuestionProgress>(); const [userProgress, setUserProgress] = useState<UserQuestionProgress>();
const alreadyKnowRef = useRef<HTMLButtonElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const didNotKnowRef = useRef<HTMLButtonElement>(null);
function showConfetti(el: HTMLElement | null) {
// If confetti is already showing, remove that first
if (confettiEl) {
setConfettiEl(null);
}
window.setTimeout(() => {
setConfettiEl(el);
}, 0);
}
async function fetchUserProgress(): Promise< async function fetchUserProgress(): Promise<
UserQuestionProgress | undefined UserQuestionProgress | undefined
@ -57,7 +44,9 @@ export function QuestionsList(props: QuestionsListProps) {
} }
const { response, error } = await httpGet<UserQuestionProgress>( const { response, error } = await httpGet<UserQuestionProgress>(
`/v1-get-user-question-progress/${groupId}` `${
import.meta.env.PUBLIC_API_URL
}/v1-get-user-question-progress/${groupId}`
); );
if (error) { if (error) {
@ -112,7 +101,9 @@ export function QuestionsList(props: QuestionsListProps) {
setIsLoading(true); setIsLoading(true);
const { response, error } = await httpPut<UserQuestionProgress>( const { response, error } = await httpPut<UserQuestionProgress>(
`/v1-reset-question-progress/${groupId}`, `${
import.meta.env.PUBLIC_API_URL
}/v1-reset-question-progress/${groupId}`,
{ {
type, type,
} }
@ -163,7 +154,9 @@ export function QuestionsList(props: QuestionsListProps) {
} }
} else { } else {
const { response, error } = await httpPut<UserQuestionProgress>( const { response, error } = await httpPut<UserQuestionProgress>(
`/v1-update-question-status/${groupId}`, `${
import.meta.env.PUBLIC_API_URL
}/v1-update-question-status/${groupId}`,
{ {
status, status,
questionId, questionId,
@ -179,9 +172,17 @@ export function QuestionsList(props: QuestionsListProps) {
newProgress = response; newProgress = response;
} }
const updatedQuestionList = pendingQuestions.filter(
(q) => q.id !== questionId
);
setUserProgress(newProgress); setUserProgress(newProgress);
setPendingQuestions(pendingQuestions.filter((q) => q.id !== questionId)); setPendingQuestions(updatedQuestionList);
setIsLoading(false); setIsLoading(false);
if (updatedQuestionList.length === 0) {
setShowConfetti(true);
}
} }
useEffect(() => { useEffect(() => {
@ -194,16 +195,10 @@ export function QuestionsList(props: QuestionsListProps) {
const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0; const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0;
const currQuestion = pendingQuestions[0]; const currQuestion = pendingQuestions[0];
const hasFinished = !isLoading && hasProgress && !currQuestion;
return ( return (
<div className="mb-40 gap-3 text-center"> <div className="mb-40 gap-3 text-center">
<Confetti
element={confettiEl}
onDone={() => {
setConfettiEl(null);
}}
/>
<QuestionsProgress <QuestionsProgress
knowCount={knowCount} knowCount={knowCount}
didNotKnowCount={dontKnowCount} didNotKnowCount={dontKnowCount}
@ -216,8 +211,21 @@ export function QuestionsList(props: QuestionsListProps) {
}} }}
/> />
<div className="relative mb-4 flex min-h-[400px] w-full overflow-hidden rounded-lg border border-gray-300 bg-white"> {showConfetti && containerRef.current && (
{!isLoading && hasProgress && !currQuestion && ( <Confetti
pieces={100}
element={containerRef.current}
onDone={() => {
setShowConfetti(false);
}}
/>
)}
<div
ref={containerRef}
className="relative mb-4 flex min-h-[400px] w-full overflow-hidden rounded-lg border border-gray-300 bg-white"
>
{hasFinished && (
<QuestionFinished <QuestionFinished
totalCount={unshuffledQuestions?.length || questions?.length || 0} totalCount={unshuffledQuestions?.length || questions?.length || 0}
knowCount={knowCount} knowCount={knowCount}
@ -232,12 +240,14 @@ export function QuestionsList(props: QuestionsListProps) {
{isLoading && <QuestionLoader />} {isLoading && <QuestionLoader />}
</div> </div>
<div className="flex flex-col gap-3 sm:flex-row"> <div
className={`flex flex-col gap-3 sm:flex-row ${
hasFinished ? 'invisible' : 'visible'
}`}
>
<button <button
disabled={isLoading || !currQuestion} disabled={isLoading || !currQuestion}
ref={alreadyKnowRef}
onClick={(e) => { onClick={(e) => {
showConfetti(alreadyKnowRef.current);
updateQuestionStatus('know', currQuestion.id).finally(() => null); updateQuestionStatus('know', currQuestion.id).finally(() => null);
}} }}
className="flex flex-1 items-center rounded-xl border border-gray-300 bg-white py-3 px-4 text-black transition-colors hover:border-black hover:bg-black hover:text-white disabled:pointer-events-none disabled:opacity-50" className="flex flex-1 items-center rounded-xl border border-gray-300 bg-white py-3 px-4 text-black transition-colors hover:border-black hover:bg-black hover:text-white disabled:pointer-events-none disabled:opacity-50"
@ -246,9 +256,7 @@ export function QuestionsList(props: QuestionsListProps) {
Already Know that Already Know that
</button> </button>
<button <button
ref={didNotKnowRef}
onClick={() => { onClick={() => {
showConfetti(didNotKnowRef.current);
updateQuestionStatus('dontKnow', currQuestion.id).finally( updateQuestionStatus('dontKnow', currQuestion.id).finally(
() => null () => null
); );

@ -48,7 +48,7 @@ const { frontmatter } = questionGroup;
</p> </p>
</div> </div>
<QuestionsList questions={questionGroup.questions} client:load /> <QuestionsList groupId={questionGroup.id} questions={questionGroup.questions} client:load />
</div> </div>
</div> </div>

Loading…
Cancel
Save