diff --git a/src/components/content/courses/CourseDetails.js b/src/components/content/courses/details/CourseDetails.js
similarity index 98%
rename from src/components/content/courses/CourseDetails.js
rename to src/components/content/courses/details/CourseDetails.js
index d15a11c..e1c3c83 100644
--- a/src/components/content/courses/CourseDetails.js
+++ b/src/components/content/courses/details/CourseDetails.js
@@ -19,7 +19,7 @@ import { ProgressSpinner } from 'primereact/progressspinner';
import { Toast } from 'primereact/toast';
// Import the desktop and mobile components
-import DesktopCourseDetails from './DesktopCourseDetails';
+import DesktopCourseDetails from '@/components/content/courses/details/DesktopCourseDetails';
import MobileCourseDetails from './MobileCourseDetails';
export default function CourseDetails({
diff --git a/src/components/content/courses/DesktopCourseDetails.js b/src/components/content/courses/details/DesktopCourseDetails.js
similarity index 100%
rename from src/components/content/courses/DesktopCourseDetails.js
rename to src/components/content/courses/details/DesktopCourseDetails.js
diff --git a/src/components/content/courses/DraftCourseDetails.js b/src/components/content/courses/details/DraftCourseDetails.js
similarity index 100%
rename from src/components/content/courses/DraftCourseDetails.js
rename to src/components/content/courses/details/DraftCourseDetails.js
diff --git a/src/components/content/courses/DraftCourseLesson.js b/src/components/content/courses/details/DraftCourseLesson.js
similarity index 100%
rename from src/components/content/courses/DraftCourseLesson.js
rename to src/components/content/courses/details/DraftCourseLesson.js
diff --git a/src/components/content/courses/MobileCourseDetails.js b/src/components/content/courses/details/MobileCourseDetails.js
similarity index 100%
rename from src/components/content/courses/MobileCourseDetails.js
rename to src/components/content/courses/details/MobileCourseDetails.js
diff --git a/src/components/content/courses/CourseHeader.js b/src/components/content/courses/layout/CourseHeader.js
similarity index 100%
rename from src/components/content/courses/CourseHeader.js
rename to src/components/content/courses/layout/CourseHeader.js
diff --git a/src/components/content/courses/CourseSidebar.js b/src/components/content/courses/layout/CourseSidebar.js
similarity index 100%
rename from src/components/content/courses/CourseSidebar.js
rename to src/components/content/courses/layout/CourseSidebar.js
diff --git a/src/components/content/courses/CombinedLesson.js b/src/components/content/courses/lessons/CombinedLesson.js
similarity index 100%
rename from src/components/content/courses/CombinedLesson.js
rename to src/components/content/courses/lessons/CombinedLesson.js
diff --git a/src/components/content/courses/CourseLesson.js b/src/components/content/courses/lessons/CourseLesson.js
similarity index 100%
rename from src/components/content/courses/CourseLesson.js
rename to src/components/content/courses/lessons/CourseLesson.js
diff --git a/src/components/content/courses/DocumentLesson.js b/src/components/content/courses/lessons/DocumentLesson.js
similarity index 100%
rename from src/components/content/courses/DocumentLesson.js
rename to src/components/content/courses/lessons/DocumentLesson.js
diff --git a/src/components/content/courses/VideoLesson.js b/src/components/content/courses/lessons/VideoLesson.js
similarity index 100%
rename from src/components/content/courses/VideoLesson.js
rename to src/components/content/courses/lessons/VideoLesson.js
diff --git a/src/components/content/courses/tabs/CourseContent.js b/src/components/content/courses/tabs/CourseContent.js
new file mode 100644
index 0000000..e4e7953
--- /dev/null
+++ b/src/components/content/courses/tabs/CourseContent.js
@@ -0,0 +1,82 @@
+import React from 'react';
+import VideoLesson from '@/components/content/courses/lessons/VideoLesson';
+import DocumentLesson from '@/components/content/courses/lessons/DocumentLesson';
+import CombinedLesson from '@/components/content/courses/lessons/CombinedLesson';
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
+
+/**
+ * Component to display course content including lessons
+ */
+const CourseContent = ({
+ lessons,
+ activeIndex,
+ course,
+ paidCourse,
+ decryptedLessonIds,
+ setCompleted
+}) => {
+ const renderLesson = (lesson) => {
+ if (!lesson) return null;
+
+ // Check if this specific lesson is decrypted
+ const lessonDecrypted = !paidCourse || decryptedLessonIds[lesson.id] || false;
+
+ if (lesson.topics?.includes('video') && lesson.topics?.includes('document')) {
+ return (
+
+ );
+ } else if (lesson.type === 'video' && !lesson.topics?.includes('document')) {
+ return (
+
+ );
+ } else if (lesson.type === 'document' && !lesson.topics?.includes('video')) {
+ return (
+
+ );
+ }
+
+ return null;
+ };
+
+ return (
+ <>
+ {lessons.length > 0 && lessons[activeIndex] ? (
+
+
+ {renderLesson(lessons[activeIndex])}
+
+
+ ) : (
+
+
Select a lesson from the sidebar to begin learning.
+
+ )}
+
+ {course?.content && (
+
+
+
+ )}
+ >
+ );
+};
+
+export default CourseContent;
\ No newline at end of file
diff --git a/src/components/content/courses/tabs/CourseOverview.js b/src/components/content/courses/tabs/CourseOverview.js
new file mode 100644
index 0000000..67dd049
--- /dev/null
+++ b/src/components/content/courses/tabs/CourseOverview.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import { Tag } from 'primereact/tag';
+import CourseDetails from '../details/CourseDetails';
+
+/**
+ * Component to display course overview with details
+ */
+const CourseOverview = ({
+ course,
+ paidCourse,
+ lessons,
+ decryptionPerformed,
+ handlePaymentSuccess,
+ handlePaymentError,
+ isMobileView,
+ completedLessons
+}) => {
+ // Determine if course is completed
+ const isCompleted = completedLessons.length > 0;
+
+ return (
+
+ {isMobileView && course && (
+
+ {/* Completed tag above image in mobile view */}
+ {isCompleted && (
+
+
+
+ )}
+
+ {/* Course image */}
+ {course.image && (
+
+

+
+ )}
+
+ )}
+
+
+ );
+};
+
+export default CourseOverview;
\ No newline at end of file
diff --git a/src/components/content/courses/tabs/CourseQA.js b/src/components/content/courses/tabs/CourseQA.js
new file mode 100644
index 0000000..4be5783
--- /dev/null
+++ b/src/components/content/courses/tabs/CourseQA.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
+
+/**
+ * Component to display course comments and Q&A section
+ */
+const CourseQA = ({ nAddress, isAuthorized, nsec, npub }) => {
+ return (
+
+
Comments
+ {nAddress !== null && isAuthorized ? (
+
+
+
+ ) : (
+
+
+ Comments are only available to content purchasers, subscribers, and the content creator.
+
+
+ )}
+
+ );
+};
+
+export default CourseQA;
\ No newline at end of file
diff --git a/src/components/navbar/Navbar.js b/src/components/navbar/Navbar.js
index 9fc60c1..344d8f1 100644
--- a/src/components/navbar/Navbar.js
+++ b/src/components/navbar/Navbar.js
@@ -8,7 +8,7 @@ import { useSession } from 'next-auth/react';
import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css';
import useWindowWidth from '@/hooks/useWindowWidth';
-import CourseHeader from '../content/courses/CourseHeader';
+import CourseHeader from '../content/courses/layout/CourseHeader';
import { useNDKContext } from '@/context/NDKContext';
import { nip19 } from 'nostr-tools';
import { parseCourseEvent } from '@/utils/nostr';
diff --git a/src/hooks/courses/useCourseData.js b/src/hooks/courses/useCourseData.js
index 0140066..c6a6663 100644
--- a/src/hooks/courses/useCourseData.js
+++ b/src/hooks/courses/useCourseData.js
@@ -1,7 +1,7 @@
-import { useEffect, useState } from 'react';
-import { parseCourseEvent } from '@/utils/nostr';
+import { useState, useEffect } from 'react';
import { nip19 } from 'nostr-tools';
-import { useToast } from '../useToast';
+import { parseCourseEvent } from '@/utils/nostr';
+import { useToast } from '@/hooks/useToast';
/**
* Hook to fetch and manage course data
@@ -21,7 +21,8 @@ const useCourseData = (ndk, fetchAuthor, router) => {
if (!router.isReady) return;
const { slug } = router.query;
-
+ let id;
+
const fetchCourseId = async () => {
if (slug.includes('naddr')) {
const { data } = nip19.decode(slug);
@@ -54,7 +55,7 @@ const useCourseData = (ndk, fetchAuthor, router) => {
const initializeCourse = async () => {
setLoading(true);
- const id = await fetchCourseId();
+ id = await fetchCourseId();
if (!id) {
setLoading(false);
return;
diff --git a/src/hooks/courses/useCourseNavigation.js b/src/hooks/courses/useCourseNavigation.js
new file mode 100644
index 0000000..8f502b4
--- /dev/null
+++ b/src/hooks/courses/useCourseNavigation.js
@@ -0,0 +1,142 @@
+import { useState, useEffect, useMemo } from 'react';
+
+/**
+ * Hook to manage course navigation and tab logic
+ * @param {Object} router - Next.js router instance
+ * @param {Boolean} isMobileView - Whether the current view is mobile
+ * @returns {Object} Navigation state and functions
+ */
+const useCourseNavigation = (router, isMobileView) => {
+ const [activeIndex, setActiveIndex] = useState(0);
+ const [sidebarVisible, setSidebarVisible] = useState(false);
+ const [activeTab, setActiveTab] = useState('overview'); // Default to overview tab
+
+ // Memoized function to get the tab map based on view mode
+ const tabMap = useMemo(() => {
+ const baseTabMap = ['overview', 'content', 'qa'];
+ if (isMobileView) {
+ const mobileTabMap = [...baseTabMap];
+ mobileTabMap.splice(2, 0, 'lessons');
+ return mobileTabMap;
+ }
+ return baseTabMap;
+ }, [isMobileView]);
+
+ // Initialize navigation state based on router
+ useEffect(() => {
+ if (router.isReady) {
+ const { active } = router.query;
+ if (active !== undefined) {
+ setActiveIndex(parseInt(active, 10));
+ // If we have an active lesson, switch to content tab
+ setActiveTab('content');
+ } else {
+ setActiveIndex(0);
+ // Default to overview tab when no active parameter
+ setActiveTab('overview');
+ }
+
+ // Auto-open sidebar on desktop, close on mobile
+ setSidebarVisible(!isMobileView);
+ }
+ }, [router.isReady, router.query, isMobileView]);
+
+ // Function to handle lesson selection
+ const handleLessonSelect = (index) => {
+ setActiveIndex(index);
+ router.push(`/course/${router.query.slug}?active=${index}`, undefined, { shallow: true });
+
+ // On mobile, switch to content tab after selection
+ if (isMobileView) {
+ setActiveTab('content');
+ setSidebarVisible(false);
+ }
+ };
+
+ // Function to toggle tab
+ const toggleTab = (index) => {
+ const tabName = tabMap[index];
+ setActiveTab(tabName);
+
+ // Only show/hide sidebar on mobile - desktop keeps sidebar visible
+ if (isMobileView) {
+ setSidebarVisible(tabName === 'lessons');
+ }
+ };
+
+ // Function to toggle sidebar visibility
+ const toggleSidebar = () => {
+ setSidebarVisible(!sidebarVisible);
+ };
+
+ // Map active tab name back to index for MenuTab
+ const getActiveTabIndex = () => {
+ return tabMap.indexOf(activeTab);
+ };
+
+ // Create tab items for MenuTab
+ const getTabItems = () => {
+ const items = [
+ {
+ label: 'Overview',
+ icon: 'pi pi-home',
+ },
+ {
+ label: 'Content',
+ icon: 'pi pi-book',
+ }
+ ];
+
+ // Add lessons tab only on mobile
+ if (isMobileView) {
+ items.push({
+ label: 'Lessons',
+ icon: 'pi pi-list',
+ });
+ }
+
+ items.push({
+ label: 'Comments',
+ icon: 'pi pi-comments',
+ });
+
+ return items;
+ };
+
+ // Add keyboard navigation support for tabs
+ useEffect(() => {
+ const handleKeyDown = (e) => {
+ if (e.key === 'ArrowRight') {
+ const currentIndex = getActiveTabIndex();
+ const nextIndex = (currentIndex + 1) % tabMap.length;
+ toggleTab(nextIndex);
+ } else if (e.key === 'ArrowLeft') {
+ const currentIndex = getActiveTabIndex();
+ const prevIndex = (currentIndex - 1 + tabMap.length) % tabMap.length;
+ toggleTab(prevIndex);
+ }
+ };
+
+ document.addEventListener('keydown', handleKeyDown);
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [activeTab, tabMap]);
+
+ return {
+ activeIndex,
+ setActiveIndex,
+ activeTab,
+ setActiveTab,
+ sidebarVisible,
+ setSidebarVisible,
+ handleLessonSelect,
+ toggleTab,
+ toggleSidebar,
+ getActiveTabIndex,
+ getTabItems,
+ tabMap
+ };
+};
+
+export default useCourseNavigation;
\ No newline at end of file
diff --git a/src/hooks/courses/useLessons.js b/src/hooks/courses/useLessons.js
index 2c80706..2e98ea9 100644
--- a/src/hooks/courses/useLessons.js
+++ b/src/hooks/courses/useLessons.js
@@ -30,7 +30,7 @@ const useLessons = (ndk, fetchAuthor, lessonIds, pubkey) => {
const events = await ndk.fetchEvents(filter);
const newLessons = [];
- // Process events
+ // Process events (no need to check for duplicates here)
for (const event of events) {
const author = await fetchAuthor(event.pubkey);
const parsedLesson = { ...parseEvent(event), author };
@@ -47,7 +47,7 @@ const useLessons = (ndk, fetchAuthor, lessonIds, pubkey) => {
}
}, [lessonIds, ndk, fetchAuthor, pubkey]);
- // Deduplicate lessons
+ // Keep this deduplication logic using Map
useEffect(() => {
const newUniqueLessons = Array.from(
new Map(lessons.map(lesson => [lesson.id, lesson])).values()
diff --git a/src/pages/course/[slug]/draft/index.js b/src/pages/course/[slug]/draft/index.js
index f7d0ef1..8fd1591 100644
--- a/src/pages/course/[slug]/draft/index.js
+++ b/src/pages/course/[slug]/draft/index.js
@@ -2,8 +2,8 @@ import React, { useEffect, useState, useCallback } from 'react';
import { useRouter } from 'next/router';
import axios from 'axios';
import { parseEvent, findKind0Fields } from '@/utils/nostr';
-import DraftCourseDetails from '@/components/content/courses/DraftCourseDetails';
-import DraftCourseLesson from '@/components/content/courses/DraftCourseLesson';
+import DraftCourseDetails from '@/components/content/courses/details/DraftCourseDetails';
+import DraftCourseLesson from '@/components/content/courses/details/DraftCourseLesson';
import { useNDKContext } from '@/context/NDKContext';
import { useSession } from 'next-auth/react';
import { useIsAdmin } from '@/hooks/useIsAdmin';
diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js
index c4700e7..eb7fe99 100644
--- a/src/pages/course/[slug]/index.js
+++ b/src/pages/course/[slug]/index.js
@@ -1,168 +1,55 @@
-import React, { useEffect, useState, useCallback, useMemo, useRef } from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
import { useRouter } from 'next/router';
-import { parseCourseEvent, parseEvent, findKind0Fields } from '@/utils/nostr';
-import CourseDetails from '@/components/content/courses/CourseDetails';
-import VideoLesson from '@/components/content/courses/VideoLesson';
-import DocumentLesson from '@/components/content/courses/DocumentLesson';
-import CombinedLesson from '@/components/content/courses/CombinedLesson';
-import CourseSidebar from '@/components/content/courses/CourseSidebar';
+import { findKind0Fields } from '@/utils/nostr';
import { useNDKContext } from '@/context/NDKContext';
import { useSession } from 'next-auth/react';
import { nip19 } from 'nostr-tools';
import { useToast } from '@/hooks/useToast';
import { ProgressSpinner } from 'primereact/progressspinner';
-import { useDecryptContent } from '@/hooks/encryption/useDecryptContent';
+
+// Hooks
import useCourseDecryption from '@/hooks/encryption/useCourseDecryption';
-import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
-import appConfig from '@/config/appConfig';
+import useCourseData from '@/hooks/courses/useCourseData';
+import useLessons from '@/hooks/courses/useLessons';
+import useCourseNavigation from '@/hooks/courses/useCourseNavigation';
import useWindowWidth from '@/hooks/useWindowWidth';
+
+// Components
+import CourseSidebar from '@/components/content/courses/layout/CourseSidebar';
+import CourseContent from '@/components/content/courses/tabs/CourseContent';
+import CourseQA from '@/components/content/courses/tabs/CourseQA';
+import CourseOverview from '@/components/content/courses/tabs/CourseOverview';
import MenuTab from '@/components/menutab/MenuTab';
-import { Tag } from 'primereact/tag';
-import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
-const useCourseData = (ndk, fetchAuthor, router) => {
- const [course, setCourse] = useState(null);
- const [lessonIds, setLessonIds] = useState([]);
- const [paidCourse, setPaidCourse] = useState(null);
- const [loading, setLoading] = useState(true);
- const { showToast } = useToast();
-
- useEffect(() => {
- if (!router.isReady) return;
-
- const { slug } = router.query;
- let id;
-
- const fetchCourseId = async () => {
- if (slug.includes('naddr')) {
- const { data } = nip19.decode(slug);
- if (!data?.identifier) {
- showToast('error', 'Error', 'Resource not found');
- return null;
- }
- return data.identifier;
- } else {
- return slug;
- }
- };
-
- const fetchCourse = async courseId => {
- try {
- await ndk.connect();
- const event = await ndk.fetchEvent({ '#d': [courseId] });
- if (!event) return null;
-
- const author = await fetchAuthor(event.pubkey);
- const lessonIds = event.tags.filter(tag => tag[0] === 'a').map(tag => tag[1].split(':')[2]);
-
- const parsedCourse = { ...parseCourseEvent(event), author };
- return { parsedCourse, lessonIds };
- } catch (error) {
- console.error('Error fetching event:', error);
- return null;
- }
- };
-
- const initializeCourse = async () => {
- setLoading(true);
- id = await fetchCourseId();
- if (!id) {
- setLoading(false);
- return;
- }
-
- const courseData = await fetchCourse(id);
- if (courseData) {
- const { parsedCourse, lessonIds } = courseData;
- setCourse(parsedCourse);
- setLessonIds(lessonIds);
- setPaidCourse(parsedCourse.price && parsedCourse.price > 0);
- }
- setLoading(false);
- };
-
- initializeCourse();
- }, [router.isReady, router.query, ndk, fetchAuthor, showToast]);
-
- return { course, lessonIds, paidCourse, loading };
-};
-
-const useLessons = (ndk, fetchAuthor, lessonIds, pubkey) => {
- const [lessons, setLessons] = useState([]);
- const [uniqueLessons, setUniqueLessons] = useState([]);
- const { showToast } = useToast();
-
- useEffect(() => {
- if (lessonIds.length > 0 && pubkey) {
- const fetchLessons = async () => {
- try {
- await ndk.connect();
-
- // Create a single filter with all lesson IDs to avoid multiple calls
- const filter = {
- '#d': lessonIds,
- kinds: [30023, 30402],
- authors: [pubkey],
- };
-
- const events = await ndk.fetchEvents(filter);
- const newLessons = [];
-
- // Process events (no need to check for duplicates here)
- for (const event of events) {
- const author = await fetchAuthor(event.pubkey);
- const parsedLesson = { ...parseEvent(event), author };
- newLessons.push(parsedLesson);
- }
-
- setLessons(newLessons);
- } catch (error) {
- console.error('Error fetching events:', error);
- }
- };
-
- fetchLessons();
- }
- }, [lessonIds, ndk, fetchAuthor, pubkey]);
-
- // Keep this deduplication logic using Map
- useEffect(() => {
- const newUniqueLessons = Array.from(
- new Map(lessons.map(lesson => [lesson.id, lesson])).values()
- );
- setUniqueLessons(newUniqueLessons);
- }, [lessons]);
-
- return { lessons, uniqueLessons, setLessons };
-};
+// Config
+import appConfig from '@/config/appConfig';
const Course = () => {
const router = useRouter();
const { ndk, addSigner } = useNDKContext();
const { data: session, update } = useSession();
const { showToast } = useToast();
- const [activeIndex, setActiveIndex] = useState(0);
const [completedLessons, setCompletedLessons] = useState([]);
const [nAddresses, setNAddresses] = useState({});
const [nsec, setNsec] = useState(null);
const [npub, setNpub] = useState(null);
- const [sidebarVisible, setSidebarVisible] = useState(false);
const [nAddress, setNAddress] = useState(null);
const windowWidth = useWindowWidth();
const isMobileView = windowWidth <= 968;
- const [activeTab, setActiveTab] = useState('overview'); // Default to overview tab
const navbarHeight = 60; // Match the height from Navbar component
- // Memoized function to get the tab map based on view mode
- const getTabMap = useMemo(() => {
- const baseTabMap = ['overview', 'content', 'qa'];
- if (isMobileView) {
- const mobileTabMap = [...baseTabMap];
- mobileTabMap.splice(2, 0, 'lessons');
- return mobileTabMap;
- }
- return baseTabMap;
- }, [isMobileView]);
+ // Use our navigation hook
+ const {
+ activeIndex,
+ activeTab,
+ sidebarVisible,
+ setSidebarVisible,
+ handleLessonSelect,
+ toggleTab,
+ toggleSidebar,
+ getActiveTabIndex,
+ getTabItems,
+ } = useCourseNavigation(router, isMobileView);
useEffect(() => {
if (router.isReady && router.query.slug) {
@@ -179,24 +66,6 @@ const Course = () => {
}
}, [router.isReady, router.query.slug, showToast, router]);
- useEffect(() => {
- if (router.isReady) {
- const { active } = router.query;
- if (active !== undefined) {
- setActiveIndex(parseInt(active, 10));
- // If we have an active lesson, switch to content tab
- setActiveTab('content');
- } else {
- setActiveIndex(0);
- // Default to overview tab when no active parameter
- setActiveTab('overview');
- }
-
- // Auto-open sidebar on desktop, close on mobile
- setSidebarVisible(!isMobileView);
- }
- }, [router.isReady, router.query, isMobileView]);
-
const setCompleted = useCallback(lessonId => {
setCompletedLessons(prev => [...prev, lessonId]);
}, []);
@@ -268,18 +137,7 @@ const Course = () => {
session?.user?.role?.subscribed ||
session?.user?.pubkey === course?.pubkey ||
!paidCourse ||
- session?.user?.purchased?.some(purchase => purchase.courseId === course?.d)
-
- const handleLessonSelect = index => {
- setActiveIndex(index);
- router.push(`/course/${router.query.slug}?active=${index}`, undefined, { shallow: true });
-
- // On mobile, switch to content tab after selection
- if (isMobileView) {
- setActiveTab('content');
- setSidebarVisible(false);
- }
- };
+ session?.user?.purchased?.some(purchase => purchase.courseId === course?.d);
const handlePaymentSuccess = async response => {
if (response && response?.preimage) {
@@ -298,141 +156,6 @@ const Course = () => {
);
};
- const toggleTab = (index) => {
- const tabName = getTabMap[index];
- setActiveTab(tabName);
-
- // Only show/hide sidebar on mobile - desktop keeps sidebar visible
- if (isMobileView) {
- setSidebarVisible(tabName === 'lessons');
- }
- };
-
- const handleToggleSidebar = () => {
- setSidebarVisible(!sidebarVisible);
- };
-
- // Map active tab name back to index for MenuTab
- const getActiveTabIndex = () => {
- return getTabMap.indexOf(activeTab);
- };
-
- // Create tab items for MenuTab
- const getTabItems = () => {
- const items = [
- {
- label: 'Overview',
- icon: 'pi pi-home',
- },
- {
- label: 'Content',
- icon: 'pi pi-book',
- }
- ];
-
- // Add lessons tab only on mobile
- if (isMobileView) {
- items.push({
- label: 'Lessons',
- icon: 'pi pi-list',
- });
- }
-
- items.push({
- label: 'Comments',
- icon: 'pi pi-comments',
- });
-
- return items;
- };
-
- // Add keyboard navigation support for tabs
- useEffect(() => {
- const handleKeyDown = (e) => {
- if (e.key === 'ArrowRight') {
- const currentIndex = getActiveTabIndex();
- const nextIndex = (currentIndex + 1) % getTabMap.length;
- toggleTab(nextIndex);
- } else if (e.key === 'ArrowLeft') {
- const currentIndex = getActiveTabIndex();
- const prevIndex = (currentIndex - 1 + getTabMap.length) % getTabMap.length;
- toggleTab(prevIndex);
- }
- };
-
- document.addEventListener('keydown', handleKeyDown);
- return () => {
- document.removeEventListener('keydown', handleKeyDown);
- };
- }, [activeTab, getTabMap, toggleTab]);
-
- // Render the QA section (empty for now)
- const renderQASection = () => {
- return (
-
-
Comments
- {nAddress !== null && isAuthorized ? (
-
-
-
- ) : (
-
-
- Comments are only available to content purchasers, subscribers, and the content creator.
-
-
- )}
-
- );
- };
- // Render Course Overview section
- const renderOverviewSection = () => {
- // Get isCompleted status for use in the component
- const isCompleted = completedLessons.length > 0;
-
- return (
-
- {isMobileView && course && (
-
- {/* Completed tag above image in mobile view */}
- {isCompleted && (
-
-
-
- )}
-
- {/* Course image */}
- {course.image && (
-
-

-
- )}
-
- )}
-
-
- );
- };
-
if (courseLoading || decryptionLoading) {
return (
@@ -441,45 +164,6 @@ const Course = () => {
);
}
- const renderLesson = lesson => {
- if (!lesson) return null;
-
- // Check if this specific lesson is decrypted
- const lessonDecrypted = !paidCourse || decryptedLessonIds[lesson.id] || false;
-
- if (lesson.topics?.includes('video') && lesson.topics?.includes('document')) {
- return (
-
- );
- } else if (lesson.type === 'video' && !lesson.topics?.includes('document')) {
- return (
-
- );
- } else if (lesson.type === 'document' && !lesson.topics?.includes('video')) {
- return (
-
- );
- }
- };
-
return (
<>
@@ -494,45 +178,50 @@ const Course = () => {
activeIndex={getActiveTabIndex()}
onTabChange={(index) => toggleTab(index)}
sidebarVisible={sidebarVisible}
- onToggleSidebar={handleToggleSidebar}
+ onToggleSidebar={toggleSidebar}
isMobileView={isMobileView}
/>
- {/* Revised layout structure to prevent content flexing */}
+ {/* Main content area with fixed width */}
- {/* Main content area with fixed width */}
+
{/* Overview tab content */}
- {renderOverviewSection()}
+
{/* Content tab content */}
- {uniqueLessons.length > 0 && uniqueLessons[activeIndex] ? (
-
-
- {renderLesson(uniqueLessons[activeIndex])}
-
-
- ) : (
-
-
Select a lesson from the sidebar to begin learning.
-
- )}
-
- {course?.content && (
-
-
-
- )}
+
{/* QA tab content */}
- {renderQASection()}
+
@@ -556,12 +245,7 @@ const Course = () => {
{
- handleLessonSelect(index);
- if (isMobileView) {
- setActiveTab('content'); // Use the tab name directly
- }
- }}
+ onLessonSelect={handleLessonSelect}
completedLessons={completedLessons}
isMobileView={isMobileView}
sidebarVisible={sidebarVisible}
@@ -577,17 +261,12 @@ const Course = () => {
{
- handleLessonSelect(index);
- if (isMobileView) {
- setActiveTab('content'); // Use the tab name directly
- }
- }}
+ onLessonSelect={handleLessonSelect}
completedLessons={completedLessons}
isMobileView={isMobileView}
onClose={() => {
setSidebarVisible(false);
- setActiveTab('content');
+ toggleTab(getActiveTabIndex());
}}
sidebarVisible={sidebarVisible}
setSidebarVisible={setSidebarVisible}