From 44484afb84897c3ca3dea5ff6df735292bf2419e Mon Sep 17 00:00:00 2001 From: dwindown Date: Sun, 4 Jan 2026 11:09:59 +0700 Subject: [PATCH] Fix timeline borders, revert sidebar accordion, and fix video reloading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove border-bottom from TimelineChapters component for better readability - Revert collapsible timeline changes from bootcamp sidebar - Fix video reloading issue by adding key prop to force remount on lesson change - Prevent resume prompt from showing multiple times during video playback - Resume prompt now only shows once when component mounts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/components/TimelineChapters.tsx | 1 - src/components/VideoPlayerWithChapters.tsx | 6 +- src/pages/Bootcamp.tsx | 205 +++++---------------- 3 files changed, 48 insertions(+), 164 deletions(-) diff --git a/src/components/TimelineChapters.tsx b/src/components/TimelineChapters.tsx index d36805f..e8e9c2e 100644 --- a/src/components/TimelineChapters.tsx +++ b/src/components/TimelineChapters.tsx @@ -76,7 +76,6 @@ export function TimelineChapters({ ? `bg-primary/10 border-l-4` : 'border-l-4 border-transparent' } - ${!isLast ? 'border-b border-border/50' : ''} `} style={ active diff --git a/src/components/VideoPlayerWithChapters.tsx b/src/components/VideoPlayerWithChapters.tsx index 125369b..f498bf6 100644 --- a/src/components/VideoPlayerWithChapters.tsx +++ b/src/components/VideoPlayerWithChapters.tsx @@ -53,6 +53,7 @@ export const VideoPlayerWithChapters = forwardRef(null); + const hasShownResumePromptRef = useRef(false); // Determine if using Adilo (M3U8) or YouTube const isAdilo = videoHost === 'adilo' || m3u8Url; @@ -252,11 +253,12 @@ export const VideoPlayerWithChapters = forwardRef { - if (!progressLoading && hasProgress && progress && progress.last_position > 5) { + if (!hasShownResumePromptRef.current && !progressLoading && hasProgress && progress && progress.last_position > 5) { setShowResumePrompt(true); setResumeTime(progress.last_position); + hasShownResumePromptRef.current = true; } }, [progressLoading, hasProgress, progress]); diff --git a/src/pages/Bootcamp.tsx b/src/pages/Bootcamp.tsx index 99f9d45..20db97d 100644 --- a/src/pages/Bootcamp.tsx +++ b/src/pages/Bootcamp.tsx @@ -8,7 +8,7 @@ import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { toast } from '@/hooks/use-toast'; import { formatDuration } from '@/lib/format'; -import { ChevronLeft, ChevronRight, Check, Play, BookOpen, Clock, Menu, Star, CheckCircle, ChevronDown, ChevronUp } from 'lucide-react'; +import { ChevronLeft, ChevronRight, Check, Play, BookOpen, Clock, Menu, Star, CheckCircle } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; import { ReviewModal } from '@/components/reviews/ReviewModal'; @@ -82,10 +82,6 @@ export default function Bootcamp() { const [accentColor, setAccentColor] = useState(''); const playerRef = useRef(null); - // Collapsible state for modules and lessons - const [collapsedModules, setCollapsedModules] = useState>(new Set()); - const [collapsedLessons, setCollapsedLessons] = useState>(new Set()); - useEffect(() => { if (!authLoading && !user) { navigate('/auth'); @@ -402,6 +398,7 @@ export default function Bootcamp() { {/* Video Player - Full Width */}
sum + m.lessons.length, 0); const isBootcampCompleted = totalLessons > 0 && completedCount >= totalLessons; - // Toggle functions for collapsible modules and lessons - const toggleModule = (moduleId: string) => { - setCollapsedModules(prev => { - const newSet = new Set(prev); - if (newSet.has(moduleId)) { - newSet.delete(moduleId); - } else { - newSet.add(moduleId); - } - return newSet; - }); - }; - - const toggleLesson = (lessonId: string) => { - setCollapsedLessons(prev => { - const newSet = new Set(prev); - if (newSet.has(lessonId)) { - newSet.delete(lessonId); - } else { - newSet.add(lessonId); - } - return newSet; - }); - }; - const renderSidebarContent = () => (
- {modules.map((module) => { - const isModuleCollapsed = collapsedModules.has(module.id); + {modules.map((module) => ( +
+

+ {module.title} +

+
+ {module.lessons.map((lesson) => { + const isCompleted = isLessonCompleted(lesson.id); + const isSelected = selectedLesson?.id === lesson.id; + const isReleased = !lesson.release_at || new Date(lesson.release_at) <= new Date(); - return ( -
- {/* Module Header - Collapsible */} - - - {/* Lessons - Hidden if module is collapsed */} - {!isModuleCollapsed && ( -
- {module.lessons.map((lesson) => { - const isCompleted = isLessonCompleted(lesson.id); - const isSelected = selectedLesson?.id === lesson.id; - const isReleased = !lesson.release_at || new Date(lesson.release_at) <= new Date(); - const isLessonCollapsed = collapsedLessons.has(lesson.id); - const hasChapters = lesson.chapters && lesson.chapters.length > 0; - - return ( -
- {/* Lesson Button - Collapsible if has chapters */} - - - {/* Timeline Chapters - Shown if lesson is expanded */} - {hasChapters && !isLessonCollapsed && ( -
- {lesson.chapters?.map((chapter, index) => { - const isChapterActive = currentTime >= chapter.time && - (!lesson.chapters?.[index + 1] || currentTime < lesson.chapters[index + 1].time); - - return ( - - ); - })} -
- )} -
- ); - })} -
- )} + return ( + + ); + })}
- ); - })} +
+ ))}
);