mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-03-15 12:49:43 +01:00
Change confetti to show on completion of quiz
This commit is contained in:
parent
437d879af3
commit
1ed54bad90
@ -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…
x
Reference in New Issue
Block a user