diff --git a/src/components/content/carousels/CoursesCarousel.js b/src/components/content/carousels/CoursesCarousel.js index 5cdbfb4..d4289c8 100644 --- a/src/components/content/carousels/CoursesCarousel.js +++ b/src/components/content/carousels/CoursesCarousel.js @@ -3,7 +3,7 @@ import { Carousel } from 'primereact/carousel'; import { parseCourseEvent } from '@/utils/nostr'; import CourseTemplate from '@/components/content/carousels/templates/CourseTemplate'; import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton'; -import { useCoursesQuery } from '@/hooks/nostrQueries/useCoursesQuery'; +import { useCoursesQuery } from '@/hooks/nostrQueries/content/useCoursesQuery'; const responsiveOptions = [ { @@ -48,21 +48,17 @@ export default function CoursesCarousel() { return
Error: {coursesError.message}
} - if (coursesLoading) { - return
Loading...
- } - return ( <>

Courses

0 ? [{}, {}, {}] : [...processedCourses]} + value={coursesLoading || !processedCourses.length ? [{}, {}, {}] : [...processedCourses]} numVisible={2} itemTemplate={(item) => - processedCourses.length > 0 ? - : - + !processedCourses.length ? + : + } responsiveOptions={responsiveOptions} />
diff --git a/src/components/content/carousels/ResourcesCarousel.js b/src/components/content/carousels/ResourcesCarousel.js index d941a2b..6977fdd 100644 --- a/src/components/content/carousels/ResourcesCarousel.js +++ b/src/components/content/carousels/ResourcesCarousel.js @@ -3,7 +3,7 @@ import { Carousel } from 'primereact/carousel'; import { parseEvent } from '@/utils/nostr'; import ResourceTemplate from '@/components/content/carousels/templates/ResourceTemplate'; import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton'; -import { useResourcesQuery } from '@/hooks/nostrQueries/useResourcesQuery'; +import { useResourcesQuery } from '@/hooks/nostrQueries/content/useResourcesQuery'; const responsiveOptions = [ { @@ -42,10 +42,6 @@ export default function ResourcesCarousel() { fetch(); }, [resources]); - if (resourcesLoading) { - return
Loading...
- } - if (resourcesError) { return
Error: {resourcesError.message}
} @@ -53,14 +49,15 @@ export default function ResourcesCarousel() { return ( <>

Resources

- 0 ? [{}, {}, {}] : [...processedResources]} - numVisible={2} - itemTemplate={(item) => + processedResources.length > 0 ? : - } - responsiveOptions={responsiveOptions} /> + } + responsiveOptions={responsiveOptions} /> ); } diff --git a/src/components/content/carousels/WorkshopsCarousel.js b/src/components/content/carousels/WorkshopsCarousel.js index b9e65a0..94c1a13 100644 --- a/src/components/content/carousels/WorkshopsCarousel.js +++ b/src/components/content/carousels/WorkshopsCarousel.js @@ -3,7 +3,7 @@ import { Carousel } from 'primereact/carousel'; import { parseEvent } from '@/utils/nostr'; import WorkshopTemplate from '@/components/content/carousels/templates/WorkshopTemplate'; import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton'; -import { useWorkshopsQuery } from '@/hooks/nostrQueries/useWorkshopsQuery'; +import { useWorkshopsQuery } from '@/hooks/nostrQueries/content/useWorkshopsQuery'; const responsiveOptions = [ { @@ -24,15 +24,14 @@ const responsiveOptions = [ ]; export default function WorkshopsCarousel() { - const [processedWorkshops, setProcessedWorkshops] = useState([]) - const { workshops, workshopsLoading, workshopsError, refetchWorkshops } = useWorkshopsQuery() + const [processedWorkshops, setProcessedWorkshops] = useState([]); + const { workshops, workshopsLoading, workshopsError, refetchWorkshops } = useWorkshopsQuery(); useEffect(() => { const fetch = async () => { try { if (workshops && workshops.length > 0) { const processedWorkshops = workshops.map(workshop => parseEvent(workshop)); - setProcessedWorkshops(processedWorkshops); } else { console.log('No workshops fetched or empty array returned'); @@ -40,24 +39,25 @@ export default function WorkshopsCarousel() { } catch (error) { console.error('Error fetching workshops:', error); } - }; + }; fetch(); }, [workshops]); - if (workshopsLoading) return
Loading...
; if (workshopsError) return
Error: {workshopsError}
; return ( <>

Workshops

- 0 ? [{}, {}, {}] : [...processedWorkshops]} - numVisible={2} - itemTemplate={(item) => - processedWorkshops.length > 0 ? - : - - } - responsiveOptions={responsiveOptions} /> + + !processedWorkshops.length ? + : + + } + responsiveOptions={responsiveOptions} + /> ); } diff --git a/src/components/content/carousels/templates/CourseTemplate.js b/src/components/content/carousels/templates/CourseTemplate.js index 1c451c4..f6f0e0a 100644 --- a/src/components/content/carousels/templates/CourseTemplate.js +++ b/src/components/content/carousels/templates/CourseTemplate.js @@ -5,7 +5,7 @@ import { formatTimestampToHowLongAgo } from "@/utils/time"; import { useImageProxy } from "@/hooks/useImageProxy"; import { getSatAmountFromInvoice } from "@/utils/lightning"; import ZapDisplay from "@/components/zaps/ZapDisplay"; -import { useCoursesZapsQuery } from "@/hooks/nostrQueries/useCoursesZapsQuery"; +import { useCoursesZapsQuery } from "@/hooks/nostrQueries/zaps/useCoursesZapsQuery"; const CourseTemplate = ({ course }) => { const [zapAmount, setZapAmount] = useState(0); @@ -31,7 +31,6 @@ const CourseTemplate = ({ course }) => { setZapAmount(total); }, [course, zaps, zapsLoading, zapsError]); - if (zapsLoading) return
Loading...
; if (zapsError) return
Error: {zapsError}
; return ( @@ -65,7 +64,7 @@ const CourseTemplate = ({ course }) => { formatTimestampToHowLongAgo(course.created_at) )}

- + diff --git a/src/components/content/carousels/templates/ResourceTemplate.js b/src/components/content/carousels/templates/ResourceTemplate.js index 25f3f98..609fd36 100644 --- a/src/components/content/carousels/templates/ResourceTemplate.js +++ b/src/components/content/carousels/templates/ResourceTemplate.js @@ -3,7 +3,7 @@ import Image from "next/image"; import { useRouter } from "next/router"; import { formatTimestampToHowLongAgo } from "@/utils/time"; import { useImageProxy } from "@/hooks/useImageProxy"; -import { useResourceZapsQuery } from "@/hooks/nostrQueries/useResourceZapsQuery"; +import { useResourceZapsQuery } from "@/hooks/nostrQueries/zaps/useResourceZapsQuery"; import { getSatAmountFromInvoice } from "@/utils/lightning"; import ZapDisplay from "@/components/zaps/ZapDisplay"; @@ -32,7 +32,6 @@ const ResourceTemplate = ({ resource }) => { setZapAmount(total); }, [resource, zaps, zapsLoading, zapsError]); - if (zapsLoading) return
Loading...
; if (zapsError) return
Error: {zapsError}
; return ( @@ -63,7 +62,7 @@ const ResourceTemplate = ({ resource }) => {

{formatTimestampToHowLongAgo(resource.published_at)}

- + diff --git a/src/components/content/carousels/templates/WorkshopTemplate.js b/src/components/content/carousels/templates/WorkshopTemplate.js index bc84228..080e0a7 100644 --- a/src/components/content/carousels/templates/WorkshopTemplate.js +++ b/src/components/content/carousels/templates/WorkshopTemplate.js @@ -3,7 +3,7 @@ import Image from "next/image"; import { useRouter } from "next/router"; import { formatTimestampToHowLongAgo } from "@/utils/time"; import { useImageProxy } from "@/hooks/useImageProxy"; -import { useWorkshopsZapsQuery } from "@/hooks/nostrQueries/useWorkshopsZapsQuery"; +import { useWorkshopsZapsQuery } from "@/hooks/nostrQueries/zaps/useWorkshopsZapsQuery"; import { getSatAmountFromInvoice } from "@/utils/lightning"; import ZapDisplay from "@/components/zaps/ZapDisplay"; @@ -30,8 +30,7 @@ const WorkshopTemplate = ({ workshop }) => { }); setZapAmount(total); }, [zaps, workshop, zapsLoading, zapsError]); - - if (zapsLoading) return
Loading...
; + if (zapsError) return
Error: {zapsError}
; return ( @@ -59,7 +58,7 @@ const WorkshopTemplate = ({ workshop }) => {

{formatTimestampToHowLongAgo(workshop.published_at)}

- + diff --git a/src/components/zaps/ZapDisplay.js b/src/components/zaps/ZapDisplay.js index b1a6dc2..7630f3c 100644 --- a/src/components/zaps/ZapDisplay.js +++ b/src/components/zaps/ZapDisplay.js @@ -3,16 +3,16 @@ import { OverlayPanel } from 'primereact/overlaypanel'; import ZapForm from './ZapForm'; import { ProgressSpinner } from 'primereact/progressspinner'; -const ZapDisplay = ({ zapAmount, event }) => { +const ZapDisplay = ({ zapAmount, event, zapsLoading }) => { const op = useRef(null); return ( <> - op.current.toggle(e)}> + op.current.toggle(e)}> - {zapAmount || zapAmount === 0 ? ( - zapAmount + {zapsLoading ? ( + ) : ( - + zapAmount )} diff --git a/src/hooks/nostrQueries/useCoursesQuery.js b/src/hooks/nostrQueries/content/useCoursesQuery.js similarity index 100% rename from src/hooks/nostrQueries/useCoursesQuery.js rename to src/hooks/nostrQueries/content/useCoursesQuery.js diff --git a/src/hooks/nostrQueries/useResourcesQuery.js b/src/hooks/nostrQueries/content/useResourcesQuery.js similarity index 100% rename from src/hooks/nostrQueries/useResourcesQuery.js rename to src/hooks/nostrQueries/content/useResourcesQuery.js diff --git a/src/hooks/nostrQueries/useWorkshopsQuery.js b/src/hooks/nostrQueries/content/useWorkshopsQuery.js similarity index 100% rename from src/hooks/nostrQueries/useWorkshopsQuery.js rename to src/hooks/nostrQueries/content/useWorkshopsQuery.js diff --git a/src/hooks/nostrQueries/useCoursesZapsQuery.js b/src/hooks/nostrQueries/zaps/useCoursesZapsQuery.js similarity index 100% rename from src/hooks/nostrQueries/useCoursesZapsQuery.js rename to src/hooks/nostrQueries/zaps/useCoursesZapsQuery.js diff --git a/src/hooks/nostrQueries/useResourceZapsQuery.js b/src/hooks/nostrQueries/zaps/useResourceZapsQuery.js similarity index 100% rename from src/hooks/nostrQueries/useResourceZapsQuery.js rename to src/hooks/nostrQueries/zaps/useResourceZapsQuery.js diff --git a/src/hooks/nostrQueries/useWorkshopsZapsQuery.js b/src/hooks/nostrQueries/zaps/useWorkshopsZapsQuery.js similarity index 100% rename from src/hooks/nostrQueries/useWorkshopsZapsQuery.js rename to src/hooks/nostrQueries/zaps/useWorkshopsZapsQuery.js diff --git a/src/hooks/nostrQueries/zaps/useZapsSubscription.js b/src/hooks/nostrQueries/zaps/useZapsSubscription.js new file mode 100644 index 0000000..634d216 --- /dev/null +++ b/src/hooks/nostrQueries/zaps/useZapsSubscription.js @@ -0,0 +1,62 @@ +import { useState, useEffect } from 'react'; +import { useNDKContext } from '@/context/NDKContext'; + +export function useZapsSubscription({ event }) { + const [isClient, setIsClient] = useState(false); + const [zaps, setZaps] = useState([]); + const [zapsLoading, setZapsLoading] = useState(true); + const [zapsError, setZapsError] = useState(null); + const ndk = useNDKContext(); + + useEffect(() => { + setIsClient(true); + }, []); + + useEffect(() => { + if (!isClient || !ndk || !event) return; + + let subscription = null; + let isSubscribed = true; + + const fetchZapsFromNDK = async () => { + try { + await ndk.connect(); + const uniqueEvents = new Set(); + + const filters = [ + { kinds: [9735], "#e": [event.id] }, + { kinds: [9735], "#a": [`${event.kind}:${event.id}:${event.d}`] } + ]; + + subscription = ndk.subscribe(filters); + + subscription.on('event', (zap) => { + if (isSubscribed) { + uniqueEvents.add(zap); + setZaps(Array.from(uniqueEvents)); + } + }); + + subscription.on('eose', () => { + setZaps(Array.from(uniqueEvents)); + setZapsLoading(false); + }); + + } catch (error) { + setZapsError('Error fetching zaps from NDK: ' + error); + setZapsLoading(false); + } + }; + + fetchZapsFromNDK(); + + return () => { + isSubscribed = false; + if (subscription) { + subscription.stop(); + } + }; + }, [isClient, ndk, event]); + + return { zaps, zapsLoading, zapsError }; +} diff --git a/src/pages/details/[slug].js b/src/pages/details/[slug].js index 16be264..4b1837d 100644 --- a/src/pages/details/[slug].js +++ b/src/pages/details/[slug].js @@ -11,6 +11,7 @@ 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 'primeicons/primeicons.css'; const MDDisplay = dynamic( () => import("@uiw/react-markdown-preview"), @@ -32,12 +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 ndk = useNDKContext(); const [user] = useLocalStorageWithEffect('user', {}); const { returnImageProxy } = useImageProxy(); + const { zaps, zapsError } = useZapsSubscription({ event: processedEvent }); const router = useRouter(); @@ -135,34 +136,6 @@ export default function Details() { setZapAmount(total); }, [zaps]); - useEffect(() => { - const fetchZaps = async () => { - try { - const processed = parseEvent(event); - await ndk.connect(); - - const filters = [ - { - kinds: [9735], - "#e": [processed.id] - }, - { - kinds: [9734], - "#a": [`${processed.kind}:${processed.id}:${processed.d}`] - } - ] - - const zaps = await ndk.fetchEvents(filters); - setZaps(zaps); - } catch (error) { - console.error('Error fetching zaps:', error); - } - } - if (event && ndk) { - fetchZaps(); - } - }, [ndk, event]); - return (