diff --git a/src/components/course/CourseDetails.js b/src/components/course/CourseDetails.js index 2bbb294..9cfa11e 100644 --- a/src/components/course/CourseDetails.js +++ b/src/components/course/CourseDetails.js @@ -1,8 +1,5 @@ -"use client"; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { useRouter } from 'next/router'; -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'; @@ -12,7 +9,11 @@ import { useLocalStorageWithEffect } from '@/hooks/useLocalStorage'; import Image from 'next/image'; import dynamic from 'next/dynamic'; import ZapThreadsWrapper from '@/components/ZapThreadsWrapper'; +import { useNDKContext } from "@/context/NDKContext"; +import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscription'; +import { findKind0Fields } from '@/utils/nostr'; import 'primeicons/primeicons.css'; + const MDDisplay = dynamic( () => import("@uiw/react-markdown-preview"), { @@ -27,25 +28,36 @@ const BitcoinConnectPayButton = dynamic( } ); -export default function CourseDetails({processedEvent}) { +export default function CourseDetails({ processedEvent }) { const [author, setAuthor] = useState(null); const [bitcoinConnect, setBitcoinConnect] = useState(false); - const [nAddress, setNAddress] = useState(null); - const [user] = useLocalStorageWithEffect('user', {}); - const [zaps, setZaps] = useState([]); + const [nAddress, setNAddress] = useState(null); const [zapAmount, setZapAmount] = useState(0); - const { returnImageProxy } = useImageProxy(); - const { fetchKind0, zapEvent, fetchZapsForEvent } = useNostr(); + const [user] = useLocalStorageWithEffect('user', {}); + const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); + const { returnImageProxy } = useImageProxy(); const router = useRouter(); + const ndk = useNDKContext(); const handleZapEvent = async () => { if (!processedEvent) return; + // Update zap event logic if necessary for NDK const response = await zapEvent(processedEvent); console.log('zap response:', response); - } + }; + + const fetchAuthor = useCallback(async (pubkey) => { + const author = await ndk.getUser({ pubkey }); + const profile = await author.fetchProfile(); + const fields = await findKind0Fields(profile); + console.log('fields:', fields); + if (fields) { + setAuthor(fields); + } + }, [ndk]); useEffect(() => { if (typeof window === 'undefined') return; @@ -58,18 +70,10 @@ export default function CourseDetails({processedEvent}) { }, []); useEffect(() => { - const fetchAuthor = async (pubkey) => { - const author = await fetchKind0(pubkey); - const fields = await findKind0Fields(author); - console.log('fields:', fields); - if (fields) { - setAuthor(fields); - } - } if (processedEvent) { fetchAuthor(processedEvent.pubkey); } - }, [fetchKind0, processedEvent]); + }, [fetchAuthor, processedEvent]); useEffect(() => { if (processedEvent?.d) { @@ -84,7 +88,7 @@ export default function CourseDetails({processedEvent}) { useEffect(() => { if (!zaps || zaps.length === 0) return; - + let total = 0; zaps.forEach((zap) => { const bolt11Tag = zap.tags.find(tag => tag[0] === "bolt11"); @@ -97,16 +101,6 @@ export default function CourseDetails({processedEvent}) { setZapAmount(total); }, [zaps]); - useEffect(() => { - const fetchZaps = async () => { - if (processedEvent) { - const zaps = await fetchZapsForEvent(processedEvent); - setZaps(zaps); - } - } - fetchZaps(); - }, [fetchZapsForEvent, processedEvent]); - return (
@@ -118,8 +112,7 @@ export default function CourseDetails({processedEvent}) { processedEvent.topics.map((topic, index) => ( )) - ) - } + )}

{processedEvent?.title}

{processedEvent?.summary}

@@ -134,7 +127,7 @@ export default function CourseDetails({processedEvent}) {

Created by{' '} - {author?.username} + {author?.username || author?.name || author?.pubkey}

@@ -155,7 +148,7 @@ export default function CourseDetails({processedEvent}) { ) : (
- +
)} @@ -180,4 +173,4 @@ export default function CourseDetails({processedEvent}) { ); -} \ No newline at end of file +} diff --git a/src/components/course/CourseLesson.js b/src/components/course/CourseLesson.js index 2de807f..d1d045d 100644 --- a/src/components/course/CourseLesson.js +++ b/src/components/course/CourseLesson.js @@ -2,12 +2,10 @@ 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"; +import { useZapsQuery } from "@/hooks/nostrQueries/zaps/useZapsQuery"; const BitcoinConnectPayButton = dynamic( () => import('@getalby/bitcoin-connect-react').then((mod) => mod.PayButton), @@ -25,10 +23,9 @@ const MDDisplay = dynamic( const CourseLesson = ({ lesson, course }) => { const [bitcoinConnect, setBitcoinConnect] = useState(false); - const [zaps, setZaps] = useState([]); const [zapAmount, setZapAmount] = useState(0); - const { fetchZapsForEvent } = useNostr(); + const { zaps, zapsLoading, zapsError } = useZapsQuery({ event: lesson, type: "lesson" }); const { returnImageProxy } = useImageProxy(); useEffect(() => { @@ -46,29 +43,22 @@ const CourseLesson = ({ lesson, course }) => { } useEffect(() => { - if (!zaps || zaps.length === 0) return; - + if (!zaps || zapsLoading || zapsError) 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; + if (zap.tags.find(tag => tag[0] === "e" && tag[1] === lesson.id) || zap.tags.find(tag => tag[0] === "a" && tag[1] === `${lesson.kind}:${lesson.id}:${lesson.d}`)) { + 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]); + }, [zaps, zapsLoading, zapsError]); - useEffect(() => { - const fetchZaps = async () => { - if (lesson) { - const zaps = await fetchZapsForEvent(lesson); - setZaps(zaps); - } - } - fetchZaps(); - }, [fetchZapsForEvent, lesson]); return (
@@ -79,8 +69,7 @@ const CourseLesson = ({ lesson, course }) => { lesson.topics.map((topic, index) => ( )) - ) - } + )}

{lesson?.title}

{lesson?.summary}

@@ -95,7 +84,7 @@ const CourseLesson = ({ lesson, course }) => {

Created by{' '} - {lesson.author?.username} + {lesson.author?.username || lesson.author?.name || lesson.author?.pubkey}

@@ -116,7 +105,7 @@ const CourseLesson = ({ lesson, course }) => { ) : (
- +
)} @@ -133,4 +122,4 @@ const CourseLesson = ({ lesson, course }) => { ) } -export default CourseLesson; \ No newline at end of file +export default CourseLesson; diff --git a/src/hooks/nostrQueries/zaps/useZapsQuery.js b/src/hooks/nostrQueries/zaps/useZapsQuery.js index c23bced..d7aad8b 100644 --- a/src/hooks/nostrQueries/zaps/useZapsQuery.js +++ b/src/hooks/nostrQueries/zaps/useZapsQuery.js @@ -20,9 +20,6 @@ export function useZapsQuery({ event, type }) { ]; const events = await ndk.fetchEvents(filters, { closeOnEose: true }); - if (event.id === "f679183f0e66878142186cf7d0ae44ab137dabed1dfcb28a609472afbb7c8d51") { - console.log('events', events); - } return events; } catch (error) { console.error('Error fetching zaps from NDK:', error); diff --git a/src/pages/course/[slug].js b/src/pages/course/[slug].js index 16365d0..eee777c 100644 --- a/src/pages/course/[slug].js +++ b/src/pages/course/[slug].js @@ -1,10 +1,11 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import { useRouter } from "next/router"; -import { useNostr } from "@/hooks/useNostr"; import { parseCourseEvent, parseEvent, findKind0Fields } from "@/utils/nostr"; import CourseDetails from "@/components/course/CourseDetails"; import CourseLesson from "@/components/course/CourseLesson"; import dynamic from 'next/dynamic'; +import { useNDKContext } from "@/context/NDKContext"; + const MDDisplay = dynamic( () => import("@uiw/react-markdown-preview"), { @@ -18,58 +19,81 @@ const Course = () => { const [lessons, setLessons] = useState([]); const router = useRouter(); - const { fetchSingleEvent, fetchSingleNaddrEvent, fetchKind0 } = useNostr(); + const ndk = useNDKContext(); - const { slug } = router.query; - - const fetchAuthor = async (pubkey) => { - const author = await fetchKind0(pubkey); - const fields = await findKind0Fields(author); + 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(() => { - const getCourse = async () => { - if (slug) { - const fetchedCourse = await fetchSingleEvent(slug); - const formattedCourse = parseCourseEvent(fetchedCourse); - const aTags = formattedCourse.tags.filter(tag => tag[0] === 'a'); - setCourse(formattedCourse); - if (aTags.length > 0) { - const lessonIds = aTags.map(tag => tag[1]); - setLessonIds(lessonIds); - } - } - }; + if (router.isReady) { + const { slug } = router.query; - if (slug && !course) { - getCourse(); + const fetchCourse = async (slug) => { + try { + await ndk.connect(); + + const filter = { + ids: [slug] + } + + 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 + }; + setCourse(parsedCourse); + } + } catch (error) { + console.error('Error fetching event:', error); + } + }; + if (ndk) { + fetchCourse(slug); + } } - }, [slug]); + }, [router.isReady, router.query, ndk, fetchAuthor]); useEffect(() => { if (lessonIds.length > 0) { const fetchLesson = async (lessonId) => { try { - const l = await fetchSingleNaddrEvent(lessonId.split(':')[2]); - const author = await fetchAuthor(l.pubkey); - const parsedLesson = parseEvent(l); - const lessonObj = { - ...parsedLesson, - author + await ndk.connect(); + + const filter = { + "#d": [lessonId] + } + + const event = await ndk.fetchEvent(filter); + + if (event) { + const author = await fetchAuthor(event.pubkey); + const parsedLesson = { + ...parseEvent(event), + author + }; + setLessons(prev => [...prev, parsedLesson]); } - setLessons(prev => [...prev, lessonObj]); } catch (error) { - console.error('Error fetching lesson:', error); + console.error('Error fetching event:', error); } - } + }; lessonIds.forEach(lessonId => fetchLesson(lessonId)); } - }, [lessonIds]); + }, [lessonIds, ndk, fetchAuthor]); return ( <> diff --git a/src/pages/details/[slug].js b/src/pages/details/[slug].js index b0cfd1a..13464e3 100644 --- a/src/pages/details/[slug].js +++ b/src/pages/details/[slug].js @@ -38,7 +38,6 @@ export default function Details() { const [zapAmount, setZapAmount] = useState(null); const [paidResource, setPaidResource] = useState(false); const [decryptedContent, setDecryptedContent] = useState(null); - // const [user, setUser] = useState(null); const ndk = useNDKContext(); const [user] = useLocalStorageWithEffect('user', {}); @@ -78,7 +77,7 @@ export default function Details() { } } decryptContent(); - }, [user, paidResource]); + }, [user, paidResource, processedEvent]); useEffect(() => { if (router.isReady) {