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 (