diff --git a/src/components/content/carousels/CoursesCarousel.js b/src/components/content/carousels/CoursesCarousel.js index 6ce793f..4e29689 100644 --- a/src/components/content/carousels/CoursesCarousel.js +++ b/src/components/content/carousels/CoursesCarousel.js @@ -71,7 +71,11 @@ export default function CoursesCarousel() { 0 ? [{}, {}, {}] : [...processedCourses]} numVisible={2} - itemTemplate={!processedCourses.length > 0 ? TemplateSkeleton : CourseTemplate} + itemTemplate={(item) => + processedCourses.length > 0 ? + : + + } responsiveOptions={responsiveOptions} /> diff --git a/src/components/content/carousels/ResourcesCarousel.js b/src/components/content/carousels/ResourcesCarousel.js index cb91624..0d0d0e2 100644 --- a/src/components/content/carousels/ResourcesCarousel.js +++ b/src/components/content/carousels/ResourcesCarousel.js @@ -66,7 +66,11 @@ export default function ResourcesCarousel() {

Resources

0 ? [{}, {}, {}] : [...processedResources]} numVisible={2} - itemTemplate={!processedResources.length > 0 ? TemplateSkeleton : ResourceTemplate} + itemTemplate={(item) => + processedResources.length > 0 ? + : + + } responsiveOptions={responsiveOptions} /> ); diff --git a/src/components/content/carousels/WorkshopsCarousel.js b/src/components/content/carousels/WorkshopsCarousel.js index 651967f..efa0007 100644 --- a/src/components/content/carousels/WorkshopsCarousel.js +++ b/src/components/content/carousels/WorkshopsCarousel.js @@ -68,7 +68,11 @@ export default function WorkshopsCarousel() {

Workshops

0 ? [{}, {}, {}] : [...processedWorkshops]} numVisible={2} - itemTemplate={!processedWorkshops.length > 0 ? TemplateSkeleton : WorkshopTemplate} + itemTemplate={(item) => + processedWorkshops.length > 0 ? + : + + } responsiveOptions={responsiveOptions} /> ); diff --git a/src/components/content/carousels/templates/CourseTemplate.js b/src/components/content/carousels/templates/CourseTemplate.js index be454f6..ab153f8 100644 --- a/src/components/content/carousels/templates/CourseTemplate.js +++ b/src/components/content/carousels/templates/CourseTemplate.js @@ -7,7 +7,7 @@ import { useNostr } from "@/hooks/useNostr"; import { getSatAmountFromInvoice } from "@/utils/lightning"; import ZapDisplay from "@/components/zaps/ZapDisplay"; -const CourseTemplate = (course) => { +const CourseTemplate = ({course}) => { const [zapAmount, setZapAmount] = useState(null); const router = useRouter(); const { returnImageProxy } = useImageProxy(); diff --git a/src/components/content/carousels/templates/ResourceTemplate.js b/src/components/content/carousels/templates/ResourceTemplate.js index 58f362e..9ae951c 100644 --- a/src/components/content/carousels/templates/ResourceTemplate.js +++ b/src/components/content/carousels/templates/ResourceTemplate.js @@ -6,7 +6,7 @@ import { useImageProxy } from "@/hooks/useImageProxy"; import { useNostr } from "@/hooks/useNostr"; import { getSatAmountFromInvoice } from "@/utils/lightning"; -const ResourceTemplate = (resource) => { +const ResourceTemplate = ({resource}) => { const [zapAmount, setZapAmount] = useState(null); const router = useRouter(); const { returnImageProxy } = useImageProxy(); diff --git a/src/components/content/carousels/templates/WorkshopTemplate.js b/src/components/content/carousels/templates/WorkshopTemplate.js index 2a3417e..bda65cd 100644 --- a/src/components/content/carousels/templates/WorkshopTemplate.js +++ b/src/components/content/carousels/templates/WorkshopTemplate.js @@ -6,7 +6,7 @@ import { useImageProxy } from "@/hooks/useImageProxy"; import { useNostr } from "@/hooks/useNostr"; import { getSatAmountFromInvoice } from "@/utils/lightning"; -const WorkshopTemplate = (workshop) => { +const WorkshopTemplate = ({workshop}) => { const [zapAmount, setZapAmount] = useState(null); const router = useRouter(); const { returnImageProxy } = useImageProxy(); diff --git a/src/components/CourseDetails.js b/src/components/course/CourseDetails.js similarity index 88% rename from src/components/CourseDetails.js rename to src/components/course/CourseDetails.js index 9234fa2..2bbb294 100644 --- a/src/components/CourseDetails.js +++ b/src/components/course/CourseDetails.js @@ -5,6 +5,7 @@ import { useNostr } from '@/hooks/useNostr'; import { findKind0Fields } from '@/utils/nostr'; import { useImageProxy } from '@/hooks/useImageProxy'; import ZapDisplay from '@/components/zaps/ZapDisplay'; +import { getSatAmountFromInvoice } from '@/utils/lightning'; import { Tag } from 'primereact/tag'; import { nip19 } from 'nostr-tools'; import { useLocalStorageWithEffect } from '@/hooks/useLocalStorage'; @@ -31,9 +32,10 @@ export default function CourseDetails({processedEvent}) { const [bitcoinConnect, setBitcoinConnect] = useState(false); const [nAddress, setNAddress] = useState(null); const [user] = useLocalStorageWithEffect('user', {}); + const [zaps, setZaps] = useState([]); const [zapAmount, setZapAmount] = useState(0); const { returnImageProxy } = useImageProxy(); - const { fetchKind0, zapEvent } = useNostr(); + const { fetchKind0, zapEvent, fetchZapsForEvent } = useNostr(); const router = useRouter(); @@ -80,6 +82,31 @@ export default function CourseDetails({processedEvent}) { } }, [processedEvent]); + useEffect(() => { + if (!zaps || zaps.length === 0) return; + + let total = 0; + zaps.forEach((zap) => { + const bolt11Tag = zap.tags.find(tag => tag[0] === "bolt11"); + const invoice = bolt11Tag ? bolt11Tag[1] : null; + if (invoice) { + const amount = getSatAmountFromInvoice(invoice); + total += amount; + } + }); + setZapAmount(total); + }, [zaps]); + + useEffect(() => { + const fetchZaps = async () => { + if (processedEvent) { + const zaps = await fetchZapsForEvent(processedEvent); + setZaps(zaps); + } + } + fetchZaps(); + }, [fetchZapsForEvent, processedEvent]); + return (
diff --git a/src/components/course/CourseLesson.js b/src/components/course/CourseLesson.js new file mode 100644 index 0000000..2de807f --- /dev/null +++ b/src/components/course/CourseLesson.js @@ -0,0 +1,136 @@ +import React, { useEffect, useState } from "react"; +import { Tag } from "primereact/tag"; +import Image from "next/image"; +import { useImageProxy } from "@/hooks/useImageProxy"; +import { useNostr } from "@/hooks/useNostr"; +import { getSatAmountFromInvoice } from "@/utils/lightning"; +import { parseEvent } from "@/utils/nostr"; +import { useLocalStorageWithEffect } from "@/hooks/useLocalStorage"; +import ZapDisplay from "@/components/zaps/ZapDisplay"; +import dynamic from "next/dynamic"; + +const BitcoinConnectPayButton = dynamic( + () => import('@getalby/bitcoin-connect-react').then((mod) => mod.PayButton), + { + ssr: false, + } +); + +const MDDisplay = dynamic( + () => import("@uiw/react-markdown-preview"), + { + ssr: false, + } +); + +const CourseLesson = ({ lesson, course }) => { + const [bitcoinConnect, setBitcoinConnect] = useState(false); + const [zaps, setZaps] = useState([]); + const [zapAmount, setZapAmount] = useState(0); + + const { fetchZapsForEvent } = useNostr(); + const { returnImageProxy } = useImageProxy(); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const bitcoinConnectConfig = window.localStorage.getItem('bc:config'); + + if (bitcoinConnectConfig) { + setBitcoinConnect(true); + } + }, []); + + const handleZapEvent = async () => { + return; + } + + useEffect(() => { + if (!zaps || zaps.length === 0) return; + + let total = 0; + zaps.forEach((zap) => { + const bolt11Tag = zap.tags.find(tag => tag[0] === "bolt11"); + const invoice = bolt11Tag ? bolt11Tag[1] : null; + if (invoice) { + const amount = getSatAmountFromInvoice(invoice); + total += amount; + } + }); + setZapAmount(total); + }, [zaps]); + + useEffect(() => { + const fetchZaps = async () => { + if (lesson) { + const zaps = await fetchZapsForEvent(lesson); + setZaps(zaps); + } + } + fetchZaps(); + }, [fetchZapsForEvent, lesson]); + return ( +
+
+
+
+
+ {lesson && lesson.topics && lesson.topics.length > 0 && ( + lesson.topics.map((topic, index) => ( + + )) + ) + } +
+

{lesson?.title}

+

{lesson?.summary}

+
+ avatar thumbnail +

+ Created by{' '} + + {lesson.author?.username} + +

+
+
+
+ {lesson && ( +
+ resource thumbnail + {bitcoinConnect ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+ )} +
+
+
+
+ { + lesson?.content && + } +
+
+ ) +} + +export default CourseLesson; \ No newline at end of file diff --git a/src/hooks/useNostr.js b/src/hooks/useNostr.js index bcc4b7a..ed9a336 100644 --- a/src/hooks/useNostr.js +++ b/src/hooks/useNostr.js @@ -24,9 +24,6 @@ export function useNostr() { const lastSubscriptionTime = useRef(0); const throttleDelay = 2000; - // ref to keep track of active subscriptions - const activeSubscriptions = useRef([]); - const processSubscriptionQueue = useCallback(() => { if (subscriptionQueue.current.length === 0) return; @@ -48,23 +45,7 @@ export function useNostr() { if (!pool) return; const subscriptionFn = () => { - // Create the subscription - const sub = pool.subscribeMany(defaultRelays, filters, { - ...opts, - oneose: () => { - // Call the original oneose if it exists - opts.oneose?.(); - // Close the subscription after EOSE - sub.close(); - // Remove this subscription from activeSubscriptions - activeSubscriptions.current = activeSubscriptions.current.filter(s => s !== sub); - } - }); - - // Add this subscription to activeSubscriptions - activeSubscriptions.current.push(sub); - - return sub; + return pool.subscribeMany(defaultRelays, filters, opts); }; subscriptionQueue.current.push(subscriptionFn); @@ -73,19 +54,6 @@ export function useNostr() { [pool, processSubscriptionQueue] ); - // Add this new function to close all active subscriptions - const closeAllSubscriptions = useCallback(() => { - activeSubscriptions.current.forEach(sub => sub.close()); - activeSubscriptions.current = []; - }, []); - - // Use an effect to close all subscriptions when the component unmounts - useEffect(() => { - return () => { - closeAllSubscriptions(); - }; - }, [closeAllSubscriptions]); - const publish = useCallback( async (event) => { if (!pool) return; diff --git a/src/pages/course/[slug].js b/src/pages/course/[slug].js index 3faf8d7..16365d0 100644 --- a/src/pages/course/[slug].js +++ b/src/pages/course/[slug].js @@ -2,12 +2,8 @@ import React, { useEffect, useState } from "react"; import { useRouter } from "next/router"; import { useNostr } from "@/hooks/useNostr"; import { parseCourseEvent, parseEvent, findKind0Fields } from "@/utils/nostr"; -import { useImageProxy } from "@/hooks/useImageProxy"; -import { Button } from "primereact/button"; -import { Tag } from "primereact/tag"; -import Image from "next/image"; -import CourseDetails from "@/components/CourseDetails"; -import { nip19 } from "nostr-tools"; +import CourseDetails from "@/components/course/CourseDetails"; +import CourseLesson from "@/components/course/CourseLesson"; import dynamic from 'next/dynamic'; const MDDisplay = dynamic( () => import("@uiw/react-markdown-preview"), @@ -15,22 +11,14 @@ const MDDisplay = dynamic( ssr: false, } ); -const BitcoinConnectPayButton = dynamic( - () => import('@getalby/bitcoin-connect-react').then((mod) => mod.PayButton), - { - ssr: false, - } -); const Course = () => { const [course, setCourse] = useState(null); const [lessonIds, setLessonIds] = useState([]); const [lessons, setLessons] = useState([]); - const [bitcoinConnect, setBitcoinConnect] = useState(false); const router = useRouter(); const { fetchSingleEvent, fetchSingleNaddrEvent, fetchKind0 } = useNostr(); - const { returnImageProxy } = useImageProxy(); const { slug } = router.query; @@ -42,24 +30,6 @@ const Course = () => { } } - const handleZapEvent = async () => { - if (!event) return; - - const response = await zapEvent(event); - - console.log('zap response:', response); - } - - useEffect(() => { - if (typeof window === 'undefined') return; - - const bitcoinConnectConfig = window.localStorage.getItem('bc:config'); - - if (bitcoinConnectConfig) { - setBitcoinConnect(true); - } - }, []); - useEffect(() => { const getCourse = async () => { if (slug) { @@ -70,7 +40,6 @@ const Course = () => { if (aTags.length > 0) { const lessonIds = aTags.map(tag => tag[1]); setLessonIds(lessonIds); - console.log("LESSON IDS", lessonIds); } } }; @@ -102,89 +71,12 @@ const Course = () => { } }, [lessonIds]); - useEffect(() => { - console.log("AHHHHH", lessons); - }, [lessons]) - return ( <> {lessons.length > 0 && lessons.map((lesson, index) => ( -
-
-
-
-
- {lesson && lesson.topics && lesson.topics.length > 0 && ( - lesson.topics.map((topic, index) => ( - - )) - ) - } -
-

{lesson?.title}

-

{lesson?.summary}

-
- avatar thumbnail -

- Created by{' '} - - {lesson.author?.username} - -

-
-
-
- {lesson && ( -
- resource thumbnail - {bitcoinConnect ? ( -
- -
- ) : ( -
-
- )} -
- )} -
-
-
-
- { - lesson?.content && - } -
-
- )) - } + + ))}
{ course?.content && diff --git a/src/pages/details/[slug].js b/src/pages/details/[slug].js index 3a6705a..72e78e4 100644 --- a/src/pages/details/[slug].js +++ b/src/pages/details/[slug].js @@ -4,7 +4,8 @@ import { useRouter } from 'next/router'; import { useNostr } from '@/hooks/useNostr'; import { parseEvent, findKind0Fields, hexToNpub } from '@/utils/nostr'; import { useImageProxy } from '@/hooks/useImageProxy'; -import { Button } from 'primereact/button'; +import { getSatAmountFromInvoice } from '@/utils/lightning'; +import ZapDisplay from '@/components/zaps/ZapDisplay'; import { Tag } from 'primereact/tag'; import { nip19 } from 'nostr-tools'; import { useLocalStorageWithEffect } from '@/hooks/useLocalStorage'; @@ -32,10 +33,12 @@ export default function Details() { const [author, setAuthor] = useState(null); const [bitcoinConnect, setBitcoinConnect] = useState(false); const [nAddress, setNAddress] = useState(null); + const [zaps, setZaps] = useState([]); + const [zapAmount, setZapAmount] = useState(0); const [user] = useLocalStorageWithEffect('user', {}); console.log('user:', user); const { returnImageProxy } = useImageProxy(); - const { fetchSingleEvent, fetchKind0, zapEvent } = useNostr(); + const { fetchSingleEvent, fetchKind0, zapEvent, fetchZapsForEvent } = useNostr(); const router = useRouter(); @@ -105,6 +108,31 @@ export default function Details() { } }, [processedEvent]); + useEffect(() => { + if (!zaps || zaps.length === 0) return; + + let total = 0; + zaps.forEach((zap) => { + const bolt11Tag = zap.tags.find(tag => tag[0] === "bolt11"); + const invoice = bolt11Tag ? bolt11Tag[1] : null; + if (invoice) { + const amount = getSatAmountFromInvoice(invoice); + total += amount; + } + }); + setZapAmount(total); + }, [zaps]); + + useEffect(() => { + const fetchZaps = async () => { + if (event) { + const zaps = await fetchZapsForEvent(event); + setZaps(zaps); + } + } + fetchZaps(); + }, [fetchZapsForEvent, event]); + return (
@@ -152,21 +180,8 @@ export default function Details() {
) : ( -
-