From d2734b0059064e0c5b1e844e50a68a93e3086528 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Sun, 17 Nov 2024 14:31:51 -0800 Subject: [PATCH] Add progress loading skeletons --- src/components/Course/CertificateView.tsx | 2 +- src/components/Course/Chapter.tsx | 44 ++++++++++++++-------- src/components/Course/CircularProgress.tsx | 11 ++++-- src/components/Course/CourseLayout.tsx | 3 +- src/components/Course/CourseSidebar.tsx | 23 +++++++---- src/components/Course/CourseSkeletons.tsx | 15 ++++++++ 6 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 src/components/Course/CourseSkeletons.tsx diff --git a/src/components/Course/CertificateView.tsx b/src/components/Course/CertificateView.tsx index c3a40dc15..f3ee46e5e 100644 --- a/src/components/Course/CertificateView.tsx +++ b/src/components/Course/CertificateView.tsx @@ -146,7 +146,7 @@ export function CertificateView(props: CertificateViewProps) { <>

Congratulations!

-

+

You finished the course. Download the completion certificate below and share it with the world.

diff --git a/src/components/Course/Chapter.tsx b/src/components/Course/Chapter.tsx index 9f4525e88..ef9654f83 100644 --- a/src/components/Course/Chapter.tsx +++ b/src/components/Course/Chapter.tsx @@ -7,6 +7,7 @@ import { CheckIcon } from '../ReactIcons/CheckIcon'; import { getPercentage } from '../../helper/number'; import { useIsMounted } from '../../hooks/use-is-mounted'; import { CircularProgress } from './CircularProgress'; +import { ChapterNumberSkeleton, LessonNumberSkeleton } from './CourseSkeletons'; function LeftBorder({ hasCompleted }: { hasCompleted?: boolean }) { return ( @@ -23,6 +24,8 @@ type ChapterProps = ChapterFileType & { isActive?: boolean; isCompleted?: boolean; + isLoading?: boolean; + activeCourseId: string; activeChapterId?: string; activeLessonId?: string; @@ -38,6 +41,8 @@ export function Chapter(props: ChapterProps) { isActive = false, onChapterClick, + isLoading = false, + activeCourseId, activeChapterId, activeLessonId, @@ -99,7 +104,8 @@ export function Chapter(props: ChapterProps) {
@@ -147,6 +154,7 @@ export function Chapter(props: ChapterProps) { chapterId={chapterId} lessons={exercises} completedLessonSet={completeLessonSet} + isLoading={isLoading} /> )} @@ -168,6 +176,7 @@ type LessonListProps = { chapterId: string; lessons: LessonFileType[]; completedLessonSet: Set; + isLoading?: boolean; }; function LessonList(props: LessonListProps) { @@ -178,6 +187,7 @@ function LessonList(props: LessonListProps) { chapterId, lessons, completedLessonSet, + isLoading = false, } = props; return ( @@ -196,6 +206,7 @@ function LessonList(props: LessonListProps) { chapterId={chapterId} isActive={isActive} isCompleted={isCompleted} + isLoading={isLoading} /> ); })} @@ -209,6 +220,7 @@ type LessonProps = LessonFileType & { courseId: string; counter: number; chapterId: string; + isLoading?: boolean; }; export function Lesson(props: LessonProps) { @@ -220,11 +232,10 @@ export function Lesson(props: LessonProps) { id: lessonId, counter, isCompleted, + isLoading = false, } = props; const { title } = frontmatter; - const isMounted = useIsMounted(); - const { isLoading } = useCourseProgress(courseId); const href = `/learn/${courseId}/${chapterId}/${lessonId}`; return ( @@ -234,18 +245,21 @@ export function Lesson(props: LessonProps) { } href={href} > -
- {!isCompleted && counter} - {isCompleted && } -
+ {!isLoading && ( +
+ {!isCompleted && counter} + {isCompleted && } +
+ )} + {isLoading && } - {isVisible && ( +
+ {isVisible && !isLoading && ( )} - {children} + + {!isLoading && children} + {isLoading && }
); } diff --git a/src/components/Course/CourseLayout.tsx b/src/components/Course/CourseLayout.tsx index e13fb7428..87324b417 100644 --- a/src/components/Course/CourseLayout.tsx +++ b/src/components/Course/CourseLayout.tsx @@ -27,7 +27,7 @@ export function CourseLayout(props: CourseLayoutProps) { const $currentLesson = useStore(currentLesson); const [showNextWarning, setShowNextWarning] = useState(false); - const { data: courseProgress } = useCourseProgress(activeCourseId); + const { data: courseProgress, isPending: isCourseProgressPending } = useCourseProgress(activeCourseId); const completeLesson = useCompleteLessonMutation(activeCourseId); const completeLessonSet = useMemo( @@ -135,6 +135,7 @@ export function CourseLayout(props: CourseLayoutProps) { {children} diff --git a/src/components/Course/CourseSidebar.tsx b/src/components/Course/CourseSidebar.tsx index 5da207dd0..50ae57b0a 100644 --- a/src/components/Course/CourseSidebar.tsx +++ b/src/components/Course/CourseSidebar.tsx @@ -2,9 +2,11 @@ import { useState } from 'react'; import type { ChapterFileType, LessonFileType } from '../../lib/course'; import { Chapter } from './Chapter'; import { StickyNote, ChevronLeft } from 'lucide-react'; -import { RoadmapLogoIcon } from '../ReactIcons/RoadmapLogo'; +import { ProgressPercentageSkeleton } from './CourseSkeletons'; export type CourseSidebarProps = { + isLoading: boolean; + activeCourseId: string; activeChapterId?: string; activeLessonId?: string; @@ -24,6 +26,7 @@ export function CourseSidebar(props: CourseSidebarProps) { activeCourseId, activeChapterId, activeLessonId, + isLoading: isProgressLoading, } = props; const [openedChapterId, setOpenedChapterId] = useState(activeChapterId); @@ -43,12 +46,17 @@ export function CourseSidebar(props: CourseSidebarProps) {

{title}

-
- - {completedPercentage}% - {' '} - Completed -
+ + {!isProgressLoading && ( +
+ + {completedPercentage}% + {' '} + Completed +
+ )} + + {isProgressLoading && }
@@ -61,6 +69,7 @@ export function CourseSidebar(props: CourseSidebarProps) { { if (isActive) { setOpenedChapterId(''); diff --git a/src/components/Course/CourseSkeletons.tsx b/src/components/Course/CourseSkeletons.tsx new file mode 100644 index 000000000..adbef6e83 --- /dev/null +++ b/src/components/Course/CourseSkeletons.tsx @@ -0,0 +1,15 @@ +export function ProgressPercentageSkeleton() { + return
; +} + +export function ChapterNumberSkeleton() { + return ( +
+ ); +} + +export function LessonNumberSkeleton() { + return ( +
+ ); +}