diff --git a/src/components/content/carousels/DocumentsCarousel.js b/src/components/content/carousels/DocumentsCarousel.js index 7a182e6..8484432 100644 --- a/src/components/content/carousels/DocumentsCarousel.js +++ b/src/components/content/carousels/DocumentsCarousel.js @@ -29,6 +29,7 @@ const responsiveOptions = [ export default function DocumentsCarousel() { const [processedDocuments, setProcessedDocuments] = useState([]); const [paidLessons, setPaidLessons] = useState([]); + const [freeLessons, setFreeLessons] = useState([]); const { documents, documentsLoading, documentsError } = useDocuments() const windowWidth = useWindowWidth(); const isMobileView = windowWidth <= 450; @@ -40,6 +41,8 @@ export default function DocumentsCarousel() { res.data.forEach(lesson => { if (lesson?.resource?.price > 0) { setPaidLessons(prev => [...prev, lesson?.resourceId]); + } else { + setFreeLessons(prev => [...prev, lesson?.resourceId]); } }); } @@ -91,7 +94,7 @@ export default function DocumentsCarousel() { }} itemTemplate={(item) => processedDocuments.length > 0 ? - : + : } responsiveOptions={responsiveOptions} /> diff --git a/src/components/content/carousels/GenericCarousel.js b/src/components/content/carousels/GenericCarousel.js index e20610d..020e15b 100644 --- a/src/components/content/carousels/GenericCarousel.js +++ b/src/components/content/carousels/GenericCarousel.js @@ -1,4 +1,5 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import axios from 'axios'; import { Carousel } from 'primereact/carousel'; import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton'; import { VideoTemplate } from '@/components/content/carousels/templates/VideoTemplate'; @@ -23,12 +24,21 @@ const responsiveOptions = [ export default function GenericCarousel({items, selectedTopic, title}) { const [carousels, setCarousels] = useState([]); + const [lessons, setLessons] = useState([]); const memoizedItems = useMemo(() => items, [items]); useEffect(() => { - console.log("carousel update", carousels); - }, [carousels]); + axios.get('/api/lessons').then(res => { + if (res.data) { + res.data.forEach(lesson => { + setLessons(prev => [...prev, lesson?.resourceId]); + }); + } + }).catch(err => { + console.log('err', err); + }); + }, []); const getItemsPerCarousel = useCallback(() => { const width = window.innerWidth; @@ -65,9 +75,9 @@ export default function GenericCarousel({items, selectedTopic, title}) { itemTemplate={(item) => { if (carouselItems.length > 0) { if (item.type === 'document') { - return ; + return ; } else if (item.type === 'video') { - return ; + return ; } else if (item.type === 'course') { return ; } diff --git a/src/components/content/carousels/VideosCarousel.js b/src/components/content/carousels/VideosCarousel.js index c92ba6b..bc954aa 100644 --- a/src/components/content/carousels/VideosCarousel.js +++ b/src/components/content/carousels/VideosCarousel.js @@ -29,6 +29,7 @@ const responsiveOptions = [ export default function VideosCarousel() { const [processedVideos, setProcessedVideos] = useState([]); const [paidLessons, setPaidLessons] = useState([]); + const [freeLessons, setFreeLessons] = useState([]); const { videos, videosLoading, videosError } = useVideos(); const windowWidth = useWindowWidth(); const isMobileView = windowWidth <= 450; @@ -39,6 +40,8 @@ export default function VideosCarousel() { res.data.forEach(lesson => { if (lesson?.resource?.price > 0) { setPaidLessons(prev => [...prev, lesson?.resourceId]); + } else { + setFreeLessons(prev => [...prev, lesson?.resourceId]); } }); } @@ -89,7 +92,7 @@ export default function VideosCarousel() { itemTemplate={(item) => !processedVideos.length ? : - + } responsiveOptions={responsiveOptions} /> diff --git a/src/components/content/carousels/templates/DocumentTemplate.js b/src/components/content/carousels/templates/DocumentTemplate.js index 804acbb..c7bf1c0 100644 --- a/src/components/content/carousels/templates/DocumentTemplate.js +++ b/src/components/content/carousels/templates/DocumentTemplate.js @@ -14,7 +14,7 @@ import useWindowWidth from "@/hooks/useWindowWidth"; import GenericButton from "@/components/buttons/GenericButton"; import appConfig from "@/config/appConfig"; -export function DocumentTemplate({ document }) { +export function DocumentTemplate({ document, isLesson }) { const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: document }); const [nAddress, setNAddress] = useState(null); const [zapAmount, setZapAmount] = useState(0); @@ -73,6 +73,7 @@ export function DocumentTemplate({ document }) {
+ {isLesson && } {document?.topics?.map((topic, index) => ( {topic} diff --git a/src/components/content/carousels/templates/VideoTemplate.js b/src/components/content/carousels/templates/VideoTemplate.js index e64a16b..b34fa7e 100644 --- a/src/components/content/carousels/templates/VideoTemplate.js +++ b/src/components/content/carousels/templates/VideoTemplate.js @@ -15,7 +15,7 @@ import useWindowWidth from "@/hooks/useWindowWidth"; import GenericButton from "@/components/buttons/GenericButton"; import appConfig from "@/config/appConfig"; -export function VideoTemplate({ video }) { +export function VideoTemplate({ video, isLesson }) { const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: video }); const [nAddress, setNAddress] = useState(null); const [zapAmount, setZapAmount] = useState(0); @@ -74,6 +74,7 @@ export function VideoTemplate({ video }) {
+ {isLesson && } {video?.topics?.map((topic, index) => ( {topic} diff --git a/src/components/content/documents/DocumentDetails.js b/src/components/content/documents/DocumentDetails.js index 6fe0a3e..74a425b 100644 --- a/src/components/content/documents/DocumentDetails.js +++ b/src/components/content/documents/DocumentDetails.js @@ -13,7 +13,6 @@ import { getTotalFromZaps } from "@/utils/lightning"; import { useSession } from "next-auth/react"; import useWindowWidth from "@/hooks/useWindowWidth"; import dynamic from "next/dynamic"; -import { auth } from "@getalby/sdk"; const MDDisplay = dynamic( () => import("@uiw/react-markdown-preview"), @@ -22,8 +21,9 @@ const MDDisplay = dynamic( } ); -const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView }) => { +const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView, isLesson }) => { const [zapAmount, setZapAmount] = useState(0); + const [course, setCourse] = useState(null); const router = useRouter(); const { returnImageProxy } = useImageProxy(); const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); @@ -39,6 +39,18 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, } }, [zaps, processedEvent]); + useEffect(() => { + if (isLesson) { + axios.get(`/api/resources/${processedEvent.d}`).then(res => { + if (res.data && res.data.lessons[0]?.courseId) { + setCourse(res.data.lessons[0]?.courseId); + } + }).catch(err => { + console.log('err', err); + }); + } + }, [processedEvent.d, isLesson]); + useEffect(() => { console.log("authorView", authorView); }, [authorView]); @@ -132,9 +144,10 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,

{title}

+ {isLesson && } {topics && topics.length > 0 && ( topics.map((topic, index) => ( - + )) )}
@@ -181,7 +194,8 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price, />
) : ( -
+
+ {course && window.open(`/course/${course}`, '_blank')} label="Open Course" tooltip="This is a lesson in a course" tooltipOptions={{ position: 'top' }} />} { +const VideoDetails = ({ processedEvent, topics, title, summary, image, price, author, paidResource, decryptedContent, nAddress, handlePaymentSuccess, handlePaymentError, authorView, isLesson }) => { const [zapAmount, setZapAmount] = useState(0); + const [course, setCourse] = useState(null); const router = useRouter(); const { returnImageProxy } = useImageProxy(); const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); @@ -31,6 +32,18 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au const windowWidth = useWindowWidth(); const isMobileView = windowWidth <= 768; + useEffect(() => { + if (isLesson) { + axios.get(`/api/resources/${processedEvent.d}`).then(res => { + if (res.data && res.data.lessons[0]?.courseId) { + setCourse(res.data.lessons[0]?.courseId); + } + }).catch(err => { + console.log('err', err); + }); + } + }, [processedEvent.d, isLesson]); + useEffect(() => { if (zaps.length > 0) { const total = getTotalFromZaps(zaps, processedEvent); @@ -124,6 +137,7 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au

{title}

+ {isLesson && } {topics && topics.length > 0 && ( topics.map((topic, index) => ( @@ -165,11 +179,12 @@ const VideoDetails = ({ processedEvent, topics, title, summary, image, price, au
router.push(`/details/${nAddress}/edit`)} label="Edit" severity='warning' outlined /> - window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={ isMobileView ? null : "View Nostr Event" } tooltipOptions={{ position: 'right' }} /> + window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={isMobileView ? null : "View Nostr Event"} tooltipOptions={{ position: 'right' }} />
) : (
- window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={ isMobileView ? null : "View Nostr Event" } tooltipOptions={{ position: paidResource ? 'left' : 'right' }} /> + {course && window.open(`/course/${course}`, '_blank')} label="Open Course" tooltip="This is a lesson in a course" tooltipOptions={{ position: 'top' }} />} + window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={isMobileView ? null : "View Nostr Event"} tooltipOptions={{ position: paidResource ? 'left' : 'right' }} />
)}
diff --git a/src/hooks/tracking/useTrackDocumentLesson.js b/src/hooks/tracking/useTrackDocumentLesson.js index 51d0cd4..c61a712 100644 --- a/src/hooks/tracking/useTrackDocumentLesson.js +++ b/src/hooks/tracking/useTrackDocumentLesson.js @@ -14,7 +14,6 @@ const useTrackDocumentLesson = ({ lessonId, courseId, readTime, paidCourse, decr useEffect(() => { if (session?.user?.role?.admin) { setIsAdmin(true); - setIsCompleted(true); // Automatically mark as completed for admins } }, [session]); diff --git a/src/hooks/tracking/useTrackVideoLesson.js b/src/hooks/tracking/useTrackVideoLesson.js index 2de2742..1fd0183 100644 --- a/src/hooks/tracking/useTrackVideoLesson.js +++ b/src/hooks/tracking/useTrackVideoLesson.js @@ -14,7 +14,6 @@ const useTrackVideoLesson = ({lessonId, videoDuration, courseId, videoPlayed, pa useEffect(() => { if (session?.user?.role?.admin) { setIsAdmin(true); - setIsCompleted(true); // Automatically mark as completed for admins } }, [session]); diff --git a/src/pages/content/index.js b/src/pages/content/index.js index 4043098..fedd338 100644 --- a/src/pages/content/index.js +++ b/src/pages/content/index.js @@ -12,7 +12,8 @@ import { useRouter } from 'next/router'; const MenuTab = ({ items, selectedTopic, onTabChange }) => { const router = useRouter(); - const allItems = ['All', ...items]; + // spread the items except for 'document' 'video' and 'course' + const allItems = ['All', ...items.filter(item => item !== 'document' && item !== 'video' && item !== 'course')]; const menuItems = allItems.map((item, index) => { let icon = 'pi pi-tag'; diff --git a/src/pages/details/[slug]/index.js b/src/pages/details/[slug]/index.js index 3f0b25e..f1aa518 100644 --- a/src/pages/details/[slug]/index.js +++ b/src/pages/details/[slug]/index.js @@ -9,6 +9,7 @@ import { useDecryptContent } from "@/hooks/encryption/useDecryptContent"; import { useToast } from "@/hooks/useToast"; import { useRouter } from "next/router"; import { ProgressSpinner } from 'primereact/progressspinner'; +import axios from 'axios'; import ZapThreadsWrapper from '@/components/ZapThreadsWrapper'; import { appConfig } from "@/config/appConfig"; @@ -19,12 +20,25 @@ const Details = () => { const [decryptedContent, setDecryptedContent] = useState(null); const [authorView, setAuthorView] = useState(false); const [loading, setLoading] = useState(true); + const [lessons, setLessons] = useState([]); const { data: session } = useSession(); const { ndk } = useNDKContext(); const { decryptContent } = useDecryptContent(); const router = useRouter(); const { showToast } = useToast(); + useEffect(() => { + axios.get('/api/lessons').then(res => { + if (res.data) { + res.data.forEach(lesson => { + setLessons(prev => [...prev, lesson?.resourceId]); + }); + } + }).catch(err => { + console.log('err', err); + }); + }, []); + const fetchAuthor = useCallback(async (pubkey) => { if (!pubkey) return; const author = await ndk.getUser({ pubkey }); @@ -140,6 +154,7 @@ const Details = () => { price={event.price} author={author} paidResource={!!event.price} + isLesson={lessons.includes(event.d)} nAddress={nAddress} decryptedContent={decryptedContent} handlePaymentSuccess={handlePaymentSuccess}