-
+
diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js
index b311d4d..7218a4f 100644
--- a/src/pages/course/[slug]/index.js
+++ b/src/pages/course/[slug]/index.js
@@ -1,127 +1,96 @@
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 CourseDetailsNew from "@/components/content/courses/CourseDetailsNew";
import VideoLesson from "@/components/content/courses/VideoLesson";
import DocumentLesson from "@/components/content/courses/DocumentLesson";
-import CourseDetailsNew from "@/components/content/courses/CourseDetailsNew";
-import { Divider } from "primereact/divider";
-import dynamic from 'next/dynamic';
import { useNDKContext } from "@/context/NDKContext";
import { useToast } from '@/hooks/useToast';
import { useSession } from 'next-auth/react';
-import { nip04 } from 'nostr-tools';
+import { nip04, nip19 } from 'nostr-tools';
import { ProgressSpinner } from 'primereact/progressspinner';
+import { Accordion, AccordionTab } from 'primereact/accordion';
+import dynamic from 'next/dynamic';
-const MDDisplay = dynamic(
- () => import("@uiw/react-markdown-preview"),
- {
- ssr: false,
- }
-);
+const MDDisplay = dynamic(() => import("@uiw/react-markdown-preview"), { ssr: false });
-const Course = () => {
+const useCourseData = (ndk, fetchAuthor, router) => {
const [course, setCourse] = useState(null);
const [lessonIds, setLessonIds] = useState([]);
- const [lessons, setLessons] = useState([]);
- const [paidCourse, setPaidCourse] = useState(false);
- const [decryptionPerformed, setDecryptionPerformed] = useState(false);
- const [loading, setLoading] = useState(true);
- const router = useRouter();
- const {ndk, addSigner} = useNDKContext();
- const { data: session, update } = useSession();
- const { showToast } = useToast();
-
- const privkey = process.env.NEXT_PUBLIC_APP_PRIV_KEY;
- const pubkey = process.env.NEXT_PUBLIC_APP_PUBLIC_KEY;
-
- const fetchAuthor = useCallback(async (pubkey) => {
- const author = await ndk.getUser({ pubkey });
- const profile = await author.fetchProfile();
- const fields = await findKind0Fields(profile);
- if (fields) {
- return fields;
- }
- }, [ndk]);
useEffect(() => {
if (router.isReady) {
const { slug } = router.query;
-
- const fetchCourse = async (slug) => {
+ const { data } = nip19.decode(slug);
+ if (!data) {
+ showToast('error', 'Error', 'Course not found');
+ return;
+ }
+ const id = data?.identifier;
+ const fetchCourse = async (id) => {
try {
await ndk.connect();
-
- const filter = {
- ids: [slug]
- }
-
+ const filter = { ids: [id] };
const event = await ndk.fetchEvent(filter);
-
if (event) {
const author = await fetchAuthor(event.pubkey);
const aTags = event.tags.filter(tag => tag[0] === 'a');
const lessonIds = aTags.map(tag => tag[1].split(':')[2]);
setLessonIds(lessonIds);
- const parsedCourse = {
- ...parseCourseEvent(event),
- author
- };
+ const parsedCourse = { ...parseCourseEvent(event), author };
setCourse(parsedCourse);
}
} catch (error) {
console.error('Error fetching event:', error);
}
};
- if (ndk) {
- fetchCourse(slug);
+ if (ndk && id) {
+ fetchCourse(id);
}
}
}, [router.isReady, router.query, ndk, fetchAuthor]);
+ return { course, lessonIds };
+};
+
+const useLessons = (ndk, fetchAuthor, lessonIds) => {
+ const [lessons, setLessons] = useState([]);
+ const [uniqueLessons, setUniqueLessons] = useState([]);
+
useEffect(() => {
if (lessonIds.length > 0) {
-
const fetchLesson = async (lessonId) => {
try {
await ndk.connect();
-
- const filter = {
- "#d": [lessonId]
- }
-
+ const filter = { "#d": [lessonId] };
const event = await ndk.fetchEvent(filter);
-
if (event) {
const author = await fetchAuthor(event.pubkey);
- const parsedLesson = {
- ...parseEvent(event),
- author
- };
+ const parsedLesson = { ...parseEvent(event), author };
setLessons(prev => [...prev, parsedLesson]);
}
} catch (error) {
console.error('Error fetching event:', error);
}
};
-
lessonIds.forEach(lessonId => fetchLesson(lessonId));
}
}, [lessonIds, ndk, fetchAuthor]);
useEffect(() => {
- console.log('lessons', lessons);
+ const uniqueLessonSet = new Set(lessons.map(JSON.stringify));
+ const newUniqueLessons = Array.from(uniqueLessonSet).map(JSON.parse);
+ setUniqueLessons(newUniqueLessons);
}, [lessons]);
- useEffect(() => {
- console.log('lessonIds', lessonIds);
- }, [lessonIds]);
+ return { lessons, uniqueLessons, setLessons };
+};
- useEffect(() => {
- if (course?.price && course?.price > 0) {
- setPaidCourse(true);
- }
- }, [course]);
+const useDecryption = (session, paidCourse, course, lessons, setLessons) => {
+ const [decryptionPerformed, setDecryptionPerformed] = useState(false);
+ const [loading, setLoading] = useState(true);
+ const privkey = process.env.NEXT_PUBLIC_APP_PRIV_KEY;
+ const pubkey = process.env.NEXT_PUBLIC_APP_PUBLIC_KEY;
useEffect(() => {
const decryptContent = async () => {
@@ -132,8 +101,6 @@ const Course = () => {
session.user?.role?.subscribed ||
session.user?.pubkey === course?.pubkey;
- console.log('canAccess', canAccess);
-
if (canAccess && lessons.length > 0) {
try {
const decryptedLessons = await Promise.all(lessons.map(async (lesson) => {
@@ -151,13 +118,57 @@ const Course = () => {
setLoading(false);
}
decryptContent();
- }, [session, paidCourse, course, lessons, privkey, pubkey, decryptionPerformed]);
+ }, [session, paidCourse, course, lessons, privkey, pubkey, decryptionPerformed, setLessons]);
+
+ return { decryptionPerformed, loading };
+};
+
+const Course = () => {
+ const router = useRouter();
+ const { ndk, addSigner } = useNDKContext();
+ const { data: session, update } = useSession();
+ const { showToast } = useToast();
+ const [paidCourse, setPaidCourse] = useState(false);
+ const [expandedIndex, setExpandedIndex] = useState(null);
+
+ const fetchAuthor = useCallback(async (pubkey) => {
+ const author = await ndk.getUser({ pubkey });
+ const profile = await author.fetchProfile();
+ const fields = await findKind0Fields(profile);
+ return fields;
+ }, [ndk]);
+
+ const { course, lessonIds } = useCourseData(ndk, fetchAuthor, router);
+ const { lessons, uniqueLessons, setLessons } = useLessons(ndk, fetchAuthor, lessonIds);
+ const { decryptionPerformed, loading } = useDecryption(session, paidCourse, course, lessons, setLessons);
useEffect(() => {
- if (course && lessons.length > 0 && (!paidCourse || decryptionPerformed)) {
- setLoading(false);
+ if (course?.price && course?.price > 0) {
+ setPaidCourse(true);
}
- }, [course, lessons, paidCourse, decryptionPerformed]);
+ }, [course]);
+
+ useEffect(() => {
+ if (router.isReady) {
+ const { active } = router.query;
+ if (active !== undefined) {
+ setExpandedIndex(parseInt(active, 10));
+ } else {
+ setExpandedIndex(null);
+ }
+ }
+ }, [router.isReady, router.query]);
+
+ const handleAccordionChange = (e) => {
+ const newIndex = e.index === expandedIndex ? null : e.index;
+ setExpandedIndex(newIndex);
+
+ if (newIndex !== null) {
+ router.push(`/course/${router.query.slug}?active=${newIndex}`, undefined, { shallow: true });
+ } else {
+ router.push(`/course/${router.query.slug}`, undefined, { shallow: true });
+ }
+ };
const handlePaymentSuccess = async (response) => {
if (response && response?.preimage) {
@@ -187,18 +198,41 @@ const Course = () => {
- {lessons.length > 0 && lessons.map((lesson, index) => (
-
-
Lesson {index + 1}
-
- {lesson.type === 'workshop' ?
:
}
-
- ))}
+
+ {uniqueLessons.length > 0 && uniqueLessons.map((lesson, index) => (
+
+ {`Lesson ${index + 1}: ${lesson.title}`}
+
+ {lesson.type === 'workshop' ?
+ :
+
+ }
+