diff --git a/components/course/course-sidebar.tsx b/components/course/course-sidebar.tsx index e17c890..ff872e5 100644 --- a/components/course/course-sidebar.tsx +++ b/components/course/course-sidebar.tsx @@ -49,48 +49,41 @@ export function CourseSidebar({ const { course, stages } = useCourseStore(); const { slug } = useParams<{ slug: string }>(); - const [extensionGroups, setExtensionGroups] = React.useState< - ExtensionGroup[] - >([]); - const [loading, setLoading] = React.useState(true); - const { data: extensions = [], isLoading: extensionsLoading } = useExtensions(slug); - React.useEffect(() => { - if (!extensionsLoading) { - try { - const grouped = stages.reduce((groups, stage) => { - if (stage.stage.extension_slug) { - const extension = extensions.find( - (ext) => ext.slug === stage.stage.extension_slug, - ); - const existingGroup = groups.find( - (g) => g.slug === stage.stage.extension_slug, - ); - - if (existingGroup) { - existingGroup.stages.push(stage); - } else { - groups.push({ - title: extension?.name || "Extension", - slug: stage.stage.extension_slug, - stages: [stage], - }); - } + const extensionGroups = React.useMemo(() => { + if (extensionsLoading) return []; + try { + return stages.reduce((groups, stage) => { + if (stage.stage.extension_slug) { + const extension = extensions.find( + (ext) => ext.slug === stage.stage.extension_slug, + ); + const existingGroup = groups.find( + (g) => g.slug === stage.stage.extension_slug, + ); + + if (existingGroup) { + existingGroup.stages.push(stage); + } else { + groups.push({ + title: extension?.name || "Extension", + slug: stage.stage.extension_slug, + stages: [stage], + }); } - return groups; - }, []); - - setExtensionGroups(grouped); - setLoading(false); - } catch (err) { - console.error(err); - setLoading(false); - } + } + return groups; + }, []); + } catch (err) { + console.error(err); + return []; } }, [extensionsLoading, extensions, stages]); + const loading = extensionsLoading; + const baseStages = stages.filter((stage) => !stage.stage.extension_slug); if (loading) return ; diff --git a/components/stage/overlay.tsx b/components/stage/overlay.tsx index f8a4ea9..fc67a90 100644 --- a/components/stage/overlay.tsx +++ b/components/stage/overlay.tsx @@ -37,7 +37,8 @@ export default function Overlay({ const { currentIndex, backIndex, backDirection } = useNavigation(); useEffect(() => { - setIsVisible(visible); + const id = setTimeout(() => setIsVisible(visible), 0); + return () => clearTimeout(id); }, [visible]); if (!isVisible || currentIndex === backIndex) { diff --git a/hooks/use-mobile.ts b/hooks/use-mobile.ts index 2b0fe1d..88fd73f 100644 --- a/hooks/use-mobile.ts +++ b/hooks/use-mobile.ts @@ -3,17 +3,21 @@ import * as React from "react" const MOBILE_BREAKPOINT = 768 export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState(undefined) + const [isMobile, setIsMobile] = React.useState(() => { + if (typeof window !== "undefined") { + return window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`).matches + } + return false + }) React.useEffect(() => { const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + setIsMobile(mql.matches) } mql.addEventListener("change", onChange) - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) return () => mql.removeEventListener("change", onChange) }, []) - return !!isMobile + return isMobile } diff --git a/package.json b/package.json index 48dc366..9dbb4dc 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "clsx": "^2.1.1", "lightningcss": "^1.30.1", "lucide-react": "^1.7.0", - "next": "16.2.3", + "next": "16.2.4", "next-themes": "^0.4.6", "pg": "^8.16.2", "react": "19.2.5",