From 5a882bc06ba2647684348eda5383334be8fd5e3d Mon Sep 17 00:00:00 2001 From: austinkelsay Date: Tue, 13 Aug 2024 16:28:25 -0500 Subject: [PATCH] Update ndk context to not initiate signer, add purchasedListItem for /profile data table --- .../content/courses/CourseDetails.js | 2 +- .../content/courses/DraftCourseDetails.js | 6 ++- src/components/forms/CourseForm.js | 6 ++- src/components/forms/ResourceForm.js | 6 ++- src/components/navbar/Navbar.js | 2 +- src/components/navbar/user/UserAvatar.js | 4 +- src/components/profile/PurchasedListItem.js | 30 ++++++++++++ src/components/profile/UserContent.js | 2 +- src/components/zaps/ZapDisplay.js | 4 +- src/context/NDKContext.js | 47 +++++++++++-------- .../content/useAllContentQuery.js | 2 +- .../nostrQueries/content/useCoursesQuery.js | 2 +- .../nostrQueries/content/useResourcesQuery.js | 2 +- .../nostrQueries/content/useWorkshopsQuery.js | 2 +- src/hooks/nostrQueries/zaps/useZapsQuery.js | 2 +- .../nostrQueries/zaps/useZapsSubscription.js | 7 +-- src/pages/api/auth/[...nextauth].js | 7 +++ src/pages/auth/signin.js | 13 ++--- src/pages/course/[slug]/draft.js | 2 +- src/pages/course/[slug]/index.js | 2 +- src/pages/details/[slug]/edit.js | 2 +- src/pages/details/[slug]/index.js | 2 +- src/pages/draft/[slug]/index.js | 6 ++- src/pages/profile.js | 40 +++++++++++----- src/utils/time.js | 18 +++++++ 25 files changed, 154 insertions(+), 64 deletions(-) create mode 100644 src/components/profile/PurchasedListItem.js diff --git a/src/components/content/courses/CourseDetails.js b/src/components/content/courses/CourseDetails.js index be4b154..26a8bf0 100644 --- a/src/components/content/courses/CourseDetails.js +++ b/src/components/content/courses/CourseDetails.js @@ -38,7 +38,7 @@ export default function CourseDetails({ processedEvent }) { const { returnImageProxy } = useImageProxy(); const { data: session, status } = useSession(); const router = useRouter(); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { if (session) { diff --git a/src/components/content/courses/DraftCourseDetails.js b/src/components/content/courses/DraftCourseDetails.js index 12ee3b5..0b4125b 100644 --- a/src/components/content/courses/DraftCourseDetails.js +++ b/src/components/content/courses/DraftCourseDetails.js @@ -29,7 +29,7 @@ export default function DraftCourseDetails({ processedEvent, lessons }) { const { returnImageProxy } = useImageProxy(); const { data: session, status } = useSession(); const router = useRouter(); - const ndk = useNDKContext(); + const { ndk, addSigner } = useNDKContext(); const fetchAuthor = useCallback(async (pubkey) => { if (!pubkey) return; @@ -64,6 +64,10 @@ export default function DraftCourseDetails({ processedEvent, lessons }) { const processedLessons = []; try { + // Step 0: Add signer if not already added + if (!ndk.signer) { + await addSigner(); + } // Step 1: Process lessons for (const lesson of lessons) { processedLessons.push({ diff --git a/src/components/forms/CourseForm.js b/src/components/forms/CourseForm.js index 0a54f8a..b1c1c64 100644 --- a/src/components/forms/CourseForm.js +++ b/src/components/forms/CourseForm.js @@ -34,7 +34,7 @@ const CourseForm = () => { const { drafts, draftsLoading, draftsError } = useDraftsQuery(); const { data: session, status } = useSession(); const [user, setUser] = useState(null); - const ndk = useNDKContext(); + const { ndk, addSigner } = useNDKContext(); const router = useRouter(); const { showToast } = useToast(); @@ -51,6 +51,10 @@ const CourseForm = () => { const handleDraftSubmit = async (e) => { e.preventDefault(); + if (!ndk.signer) { + await addSigner(); + } + // Prepare the lessons from selected lessons const resources = await Promise.all(selectedLessons.map(async (lesson) => { // if .type is present than this lesson is a draft we need to publish diff --git a/src/components/forms/ResourceForm.js b/src/components/forms/ResourceForm.js index b7d95bd..73aa42f 100644 --- a/src/components/forms/ResourceForm.js +++ b/src/components/forms/ResourceForm.js @@ -31,7 +31,7 @@ const ResourceForm = ({ draft = null, isPublished = false }) => { const { data: session, status } = useSession(); const { showToast } = useToast(); const router = useRouter(); - const ndk = useNDKContext(); + const { ndk, addSigner } = useNDKContext(); useEffect(() => { console.log('isPublished', isPublished); @@ -108,6 +108,10 @@ const ResourceForm = ({ draft = null, isPublished = false }) => { console.log('event', event); try { + if (!ndk.signer) { + await addSigner(); + } + await ndk.connect(); const published = await ndk.publish(event); diff --git a/src/components/navbar/Navbar.js b/src/components/navbar/Navbar.js index 5b18cf7..f4c2539 100644 --- a/src/components/navbar/Navbar.js +++ b/src/components/navbar/Navbar.js @@ -73,7 +73,7 @@ const Navbar = () => { onClick={(e) => menu.current.toggle(e)}> */} -
router.push('/')} className="flex flex-row items-center justify-center cursor-pointer"> +
router.push('/').then(() => window.location.reload())} className="flex flex-row items-center justify-center cursor-pointer"> logo { const menu = useRef(null); - const handleLogout = () => { - signOut(); + const handleLogout = async () => { + await signOut({ redirect: false }); // Wait for the sign-out to complete router.push('/').then(() => window.location.reload()); } diff --git a/src/components/profile/PurchasedListItem.js b/src/components/profile/PurchasedListItem.js new file mode 100644 index 0000000..1148e11 --- /dev/null +++ b/src/components/profile/PurchasedListItem.js @@ -0,0 +1,30 @@ +import React, { useEffect, useState } from "react"; +import { useNDKContext } from "@/context/NDKContext"; +import { parseEvent } from "@/utils/nostr"; +import { ProgressSpinner } from "primereact/progressspinner"; + +const PurchasedListItem = ({ eventId, category }) => { + const { ndk } = useNDKContext(); + const [event, setEvent] = useState(null); + + useEffect(() => { + const fetchEvent = async () => { + if (!eventId) return; + + try { + await ndk.connect(); + const event = await ndk.fetchEvent(eventId); + if (event) { + setEvent(parseEvent(event)); + } + } catch (error) { + console.error("Error fetching event:", error); + } + } + fetchEvent(); + }, [eventId, ndk]); + + return !event || !ndk ? : ({event.title}); +} + +export default PurchasedListItem; \ No newline at end of file diff --git a/src/components/profile/UserContent.js b/src/components/profile/UserContent.js index 6950ed8..6f7962d 100644 --- a/src/components/profile/UserContent.js +++ b/src/components/profile/UserContent.js @@ -25,7 +25,7 @@ const UserContent = () => { const [user, setUser] = useState(null); const router = useRouter(); const { showToast } = useToast(); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); const { courses, coursesLoading, coursesError } = useCoursesQuery(); const { resources, resourcesLoading, resourcesError } = useResourcesQuery(); const { workshops, workshopsLoading, workshopsError } = useWorkshopsQuery(); diff --git a/src/components/zaps/ZapDisplay.js b/src/components/zaps/ZapDisplay.js index f6cec51..fd53303 100644 --- a/src/components/zaps/ZapDisplay.js +++ b/src/components/zaps/ZapDisplay.js @@ -19,8 +19,8 @@ const ZapDisplay = ({ zapAmount, event, zapsLoading }) => { return ( <> op.current.toggle(e)}> - - + + {zapsLoading || zapAmount === null || extraLoading ? ( ) : ( diff --git a/src/context/NDKContext.js b/src/context/NDKContext.js index 710a3d8..4b83678 100644 --- a/src/context/NDKContext.js +++ b/src/context/NDKContext.js @@ -4,31 +4,38 @@ import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk"; const NDKContext = createContext(null); const relayUrls = [ - "wss://nos.lol/", - "wss://relay.damus.io/", - "wss://relay.snort.social/", - "wss://relay.nostr.band/", - "wss://nostr.mutinywallet.com/", - "wss://relay.mutinywallet.com/", - "wss://relay.primal.net/" + "wss://nos.lol/", + "wss://relay.damus.io/", + "wss://relay.snort.social/", + "wss://relay.nostr.band/", + "wss://nostr.mutinywallet.com/", + "wss://relay.mutinywallet.com/", + "wss://relay.primal.net/" ]; export const NDKProvider = ({ children }) => { - const [ndk, setNdk] = useState(null); + const [ndk, setNdk] = useState(null); - useEffect(() => { - const nip07signer = new NDKNip07Signer(); - const instance = new NDK({ explicitRelayUrls: relayUrls, signer: nip07signer }); - setNdk(instance); - }, []); + useEffect(() => { + const instance = new NDK({ explicitRelayUrls: relayUrls }); + setNdk(instance); + }, []); - return ( - - {children} - - ); + const addSigner = async () => { + if (ndk) { + const nip07signer = new NDKNip07Signer(); + await ndk.signer?.user(); + ndk.signer = nip07signer; + } + }; + + return ( + + {children} + + ); }; export const useNDKContext = () => { - return useContext(NDKContext); -}; + return useContext(NDKContext); +}; \ No newline at end of file diff --git a/src/hooks/nostrQueries/content/useAllContentQuery.js b/src/hooks/nostrQueries/content/useAllContentQuery.js index 9ea7fcb..1526d07 100644 --- a/src/hooks/nostrQueries/content/useAllContentQuery.js +++ b/src/hooks/nostrQueries/content/useAllContentQuery.js @@ -4,7 +4,7 @@ import { useNDKContext } from '@/context/NDKContext'; export function useAllContentQuery({ids}) { const [isClient, setIsClient] = useState(false); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { setIsClient(true); diff --git a/src/hooks/nostrQueries/content/useCoursesQuery.js b/src/hooks/nostrQueries/content/useCoursesQuery.js index 4bd55b1..52b2e03 100644 --- a/src/hooks/nostrQueries/content/useCoursesQuery.js +++ b/src/hooks/nostrQueries/content/useCoursesQuery.js @@ -7,7 +7,7 @@ const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY; export function useCoursesQuery() { const [isClient, setIsClient] = useState(false); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { setIsClient(true); diff --git a/src/hooks/nostrQueries/content/useResourcesQuery.js b/src/hooks/nostrQueries/content/useResourcesQuery.js index 4f3ba70..e536a1f 100644 --- a/src/hooks/nostrQueries/content/useResourcesQuery.js +++ b/src/hooks/nostrQueries/content/useResourcesQuery.js @@ -7,7 +7,7 @@ const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY; export function useResourcesQuery() { const [isClient, setIsClient] = useState(false); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { setIsClient(true); diff --git a/src/hooks/nostrQueries/content/useWorkshopsQuery.js b/src/hooks/nostrQueries/content/useWorkshopsQuery.js index af59866..91571a5 100644 --- a/src/hooks/nostrQueries/content/useWorkshopsQuery.js +++ b/src/hooks/nostrQueries/content/useWorkshopsQuery.js @@ -7,7 +7,7 @@ const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY; export function useWorkshopsQuery() { const [isClient, setIsClient] = useState(false); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { setIsClient(true); diff --git a/src/hooks/nostrQueries/zaps/useZapsQuery.js b/src/hooks/nostrQueries/zaps/useZapsQuery.js index d7aad8b..f20c153 100644 --- a/src/hooks/nostrQueries/zaps/useZapsQuery.js +++ b/src/hooks/nostrQueries/zaps/useZapsQuery.js @@ -4,7 +4,7 @@ import { useNDKContext } from '@/context/NDKContext'; export function useZapsQuery({ event, type }) { const [isClient, setIsClient] = useState(false); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { setIsClient(true); diff --git a/src/hooks/nostrQueries/zaps/useZapsSubscription.js b/src/hooks/nostrQueries/zaps/useZapsSubscription.js index 93c73b3..60d4052 100644 --- a/src/hooks/nostrQueries/zaps/useZapsSubscription.js +++ b/src/hooks/nostrQueries/zaps/useZapsSubscription.js @@ -5,7 +5,7 @@ export function useZapsSubscription({event}) { const [zaps, setZaps] = useState([]); const [zapsLoading, setZapsLoading] = useState(true); const [zapsError, setZapsError] = useState(null); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); useEffect(() => { let subscription; @@ -19,12 +19,9 @@ export function useZapsSubscription({event}) { { kinds: [9735], "#a": [`${event.kind}:${event.id}:${event.d}`] } ]; await ndk.connect(); - console.log("filters", filters); subscription = ndk.subscribe(filters); - subscription.on('event', (zapEvent) => { - console.log("event", zapEvent); - + subscription.on('event', (zapEvent) => { // Check if we've already seen this zap if (!zapIds.has(zapEvent.id)) { zapIds.add(zapEvent.id); diff --git a/src/pages/api/auth/[...nextauth].js b/src/pages/api/auth/[...nextauth].js index f23a949..a853c1e 100644 --- a/src/pages/api/auth/[...nextauth].js +++ b/src/pages/api/auth/[...nextauth].js @@ -75,6 +75,7 @@ export default NextAuth({ ], callbacks: { async jwt({ token, trigger, user }) { + console.log('TRIGGER', trigger); if (trigger === "update") { // if we trigger an update call the authorize function again const newUser = await authorize(token.user.pubkey); @@ -95,6 +96,12 @@ export default NextAuth({ async redirect({ url, baseUrl }) { return baseUrl; }, + async signOut({ token, session }) { + console.log('signOut', token, session); + token = {} + session = {} + return true + }, }, secret: process.env.NEXTAUTH_SECRET, session: { strategy: "jwt" }, diff --git a/src/pages/auth/signin.js b/src/pages/auth/signin.js index 647d430..0a2e943 100644 --- a/src/pages/auth/signin.js +++ b/src/pages/auth/signin.js @@ -2,13 +2,14 @@ import { signIn, useSession } from "next-auth/react" import { useState, useEffect } from "react" import { useNDKContext } from "@/context/NDKContext"; import { Button } from 'primereact/button'; -import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk"; export default function SignIn() { const [email, setEmail] = useState("") const [nostrPubkey, setNostrPubkey] = useState("") const [nostrPrivkey, setNostrPrivkey] = useState("") + const {ndk, addSigner} = useNDKContext(); + const { data: session, status } = useSession(); // Get the current session's data and status useEffect(() => { @@ -22,14 +23,14 @@ export default function SignIn() { const handleNostrSignIn = async (e) => { e.preventDefault() - - const nip07signer = new NDKNip07Signer(); - const ndk = new NDK({ signer: nip07signer }); - await ndk.connect() + if (!ndk.signer) { + await addSigner(); + } + try { - const user = await nip07signer.user() + const user = await ndk.signer.user() const pubkey = user?._pubkey signIn("nostr", { pubkey }) diff --git a/src/pages/course/[slug]/draft.js b/src/pages/course/[slug]/draft.js index 22755d4..5188069 100644 --- a/src/pages/course/[slug]/draft.js +++ b/src/pages/course/[slug]/draft.js @@ -20,7 +20,7 @@ const DraftCourse = () => { const [lessonsWithAuthors, setLessonsWithAuthors] = useState([]); const router = useRouter(); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); const fetchAuthor = useCallback(async (pubkey) => { if (!pubkey) return; diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js index 19ef294..954a607 100644 --- a/src/pages/course/[slug]/index.js +++ b/src/pages/course/[slug]/index.js @@ -19,7 +19,7 @@ const Course = () => { const [lessons, setLessons] = useState([]); const router = useRouter(); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); const fetchAuthor = useCallback(async (pubkey) => { const author = await ndk.getUser({ pubkey }); diff --git a/src/pages/details/[slug]/edit.js b/src/pages/details/[slug]/edit.js index e265b7f..b5919ff 100644 --- a/src/pages/details/[slug]/edit.js +++ b/src/pages/details/[slug]/edit.js @@ -10,7 +10,7 @@ import { useToast } from "@/hooks/useToast"; export default function Edit() { const [event, setEvent] = useState(null); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); const router = useRouter(); const { showToast } = useToast(); diff --git a/src/pages/details/[slug]/index.js b/src/pages/details/[slug]/index.js index a9dae9e..1b8dd41 100644 --- a/src/pages/details/[slug]/index.js +++ b/src/pages/details/[slug]/index.js @@ -31,7 +31,7 @@ export default function Details() { const [decryptedContent, setDecryptedContent] = useState(null); const [authorView, setAuthorView] = useState(false); - const ndk = useNDKContext(); + const {ndk, addSigner} = useNDKContext(); const { data: session, update } = useSession(); const [user, setUser] = useState(null); const { showToast } = useToast(); diff --git a/src/pages/draft/[slug]/index.js b/src/pages/draft/[slug]/index.js index 1a8828d..2bae292 100644 --- a/src/pages/draft/[slug]/index.js +++ b/src/pages/draft/[slug]/index.js @@ -49,7 +49,7 @@ export default function Draft() { const { width, height } = useResponsiveImageDimensions(); const router = useRouter(); const { showToast } = useToast(); - const ndk = useNDKContext(); + const { ndk, addSigner } = useNDKContext(); useEffect(() => { if (session) { @@ -74,6 +74,10 @@ export default function Draft() { const handleSubmit = async () => { try { + if (!ndk.signer) { + await addSigner(); + } + if (draft) { const { unsignedEvent, type } = await buildEvent(draft); diff --git a/src/pages/profile.js b/src/pages/profile.js index 319f7b5..8dcacdb 100644 --- a/src/pages/profile.js +++ b/src/pages/profile.js @@ -5,6 +5,10 @@ import { Menu } from "primereact/menu"; import { Column } from "primereact/column"; import { useImageProxy } from "@/hooks/useImageProxy"; import { useSession } from 'next-auth/react'; +import { ProgressSpinner } from "primereact/progressspinner"; +import PurchasedListItem from "@/components/profile/PurchasedListItem"; +import { useNDKContext } from "@/context/NDKContext"; +import { formatDateTime } from "@/utils/time"; import UserContent from "@/components/profile/UserContent"; import Image from "next/image"; import BitcoinConnectButton from "@/components/bitcoinConnect/BitcoinConnect"; @@ -15,6 +19,7 @@ const Profile = () => { const { data: session, status } = useSession(); const { returnImageProxy } = useImageProxy(); + const { ndk } = useNDKContext(); const menu = useRef(null); useEffect(() => { @@ -33,8 +38,6 @@ const Profile = () => { } }, [session]); - const purchases = []; - const menuItems = [ { label: "Edit", @@ -96,17 +99,28 @@ const Profile = () => { />
- - - - - - + {!session || !session?.user || !ndk ? ( + + ) : ( + + + { + console.log("rowData", rowData); + return + }} + header="Name" + > + item.courseId) ? "course" : "resource"} header="Category"> + formatDateTime(rowData?.createdAt)} header="Date"> + + + )} ) diff --git a/src/utils/time.js b/src/utils/time.js index e0cb53c..0abe0c7 100644 --- a/src/utils/time.js +++ b/src/utils/time.js @@ -3,6 +3,24 @@ export const formatUnixTimestamp = (time) => { return date.toDateString(); } +export const formatDateTime = (isoDate) => { + const date = new Date(isoDate); + + // Example: Format to a more readable string + const formattedDate = date.toLocaleString("en-US", { + timeZone: "UTC", // Optional: You can change this to the user's time zone if needed + weekday: "long", // "long" for full name, "short" for abbreviated + year: "numeric", + month: "long", // "long" for full name, "short" for abbreviated + day: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + }); + + return formattedDate; +} + export const formatTimestampToHowLongAgo = (time) => { const date = new Date(time * 1000); const now = new Date();