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 (
+
+ );
+}