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

@ -5,10 +5,10 @@ import { QuestionCard } from './QuestionCard';
import { QuestionLoader } from './QuestionLoader';
import { isLoggedIn } from '../../lib/jwt';
import type { QuestionType } from '../../lib/question-group';
import { Confetti } from '../Confetti';
import { httpGet, httpPut } from '../../lib/http';
import { useToast } from '../../hooks/use-toast';
import { QuestionFinished } from './QuestionFinished';
import { Confetti } from '../Confetti';
type UserQuestionProgress = {
know: string[];
@ -29,25 +29,12 @@ export function QuestionsList(props: QuestionsListProps) {
const toast = useToast();
const [isLoading, setIsLoading] = useState(true);
const [confettiEl, setConfettiEl] = useState<HTMLElement | null>(null);
const [showConfetti, setShowConfetti] = useState(false);
const [questions, setQuestions] = useState<QuestionType[]>();
const [pendingQuestions, setPendingQuestions] = useState<QuestionType[]>([]);
const [userProgress, setUserProgress] = useState<UserQuestionProgress>();
const alreadyKnowRef = useRef<HTMLButtonElement>(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);
}
const containerRef = useRef<HTMLDivElement>(null);
async function fetchUserProgress(): Promise<
UserQuestionProgress | undefined
@ -57,7 +44,9 @@ export function QuestionsList(props: QuestionsListProps) {
}
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) {
@ -112,7 +101,9 @@ export function QuestionsList(props: QuestionsListProps) {
setIsLoading(true);
const { response, error } = await httpPut<UserQuestionProgress>(
`/v1-reset-question-progress/${groupId}`,
`${
import.meta.env.PUBLIC_API_URL
}/v1-reset-question-progress/${groupId}`,
{
type,
}
@ -163,7 +154,9 @@ export function QuestionsList(props: QuestionsListProps) {
}
} else {
const { response, error } = await httpPut<UserQuestionProgress>(
`/v1-update-question-status/${groupId}`,
`${
import.meta.env.PUBLIC_API_URL
}/v1-update-question-status/${groupId}`,
{
status,
questionId,
@ -179,9 +172,17 @@ export function QuestionsList(props: QuestionsListProps) {
newProgress = response;
}
const updatedQuestionList = pendingQuestions.filter(
(q) => q.id !== questionId
);
setUserProgress(newProgress);
setPendingQuestions(pendingQuestions.filter((q) => q.id !== questionId));
setPendingQuestions(updatedQuestionList);
setIsLoading(false);
if (updatedQuestionList.length === 0) {
setShowConfetti(true);
}
}
useEffect(() => {
@ -194,16 +195,10 @@ export function QuestionsList(props: QuestionsListProps) {
const hasProgress = knowCount > 0 || dontKnowCount > 0 || skipCount > 0;
const currQuestion = pendingQuestions[0];
const hasFinished = !isLoading && hasProgress && !currQuestion;
return (
<div className="mb-40 gap-3 text-center">
<Confetti
element={confettiEl}
onDone={() => {
setConfettiEl(null);
}}
/>
<QuestionsProgress
knowCount={knowCount}
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">
{!isLoading && hasProgress && !currQuestion && (
{showConfetti && containerRef.current && (
<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
totalCount={unshuffledQuestions?.length || questions?.length || 0}
knowCount={knowCount}
@ -232,12 +240,14 @@ export function QuestionsList(props: QuestionsListProps) {
{isLoading && <QuestionLoader />}
</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
disabled={isLoading || !currQuestion}
ref={alreadyKnowRef}
onClick={(e) => {
showConfetti(alreadyKnowRef.current);
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"
@ -246,9 +256,7 @@ export function QuestionsList(props: QuestionsListProps) {
Already Know that
</button>
<button
ref={didNotKnowRef}
onClick={() => {
showConfetti(didNotKnowRef.current);
updateQuestionStatus('dontKnow', currQuestion.id).finally(
() => null
);

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

Loading…
Cancel
Save