diff --git a/src/components/content/courses/CourseDetails.js b/src/components/content/courses/CourseDetails.js index 580e7ef..cd064a2 100644 --- a/src/components/content/courses/CourseDetails.js +++ b/src/components/content/courses/CourseDetails.js @@ -1,49 +1,42 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { useRouter } from 'next/router'; -import { useImageProxy } from '@/hooks/useImageProxy'; -import ZapDisplay from '@/components/zaps/ZapDisplay'; -import { getTotalFromZaps } from '@/utils/lightning'; +import axios from 'axios'; +import { useToast } from "@/hooks/useToast"; import { Tag } from 'primereact/tag'; -import { nip19 } from 'nostr-tools'; -import { useSession } from 'next-auth/react'; -import GenericButton from '@/components/buttons/GenericButton'; 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'; +import { useRouter } from 'next/router'; import CoursePaymentButton from "@/components/bitcoinConnect/CoursePaymentButton"; -import { ProgressSpinner } from 'primereact/progressspinner'; +import ZapDisplay from '@/components/zaps/ZapDisplay'; +import GenericButton from '@/components/buttons/GenericButton'; +import { nip19 } from 'nostr-tools'; +import { useImageProxy } from '@/hooks/useImageProxy'; +import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscription'; +import { getTotalFromZaps } from '@/utils/lightning'; +import { useSession } from 'next-auth/react'; +import useWindowWidth from "@/hooks/useWindowWidth"; +import { useNDKContext } from "@/context/NDKContext"; +import { findKind0Fields } from '@/utils/nostr'; import appConfig from "@/config/appConfig"; -import useWindowWidth from '@/hooks/useWindowWidth'; - -const MDDisplay = dynamic( - () => import("@uiw/react-markdown-preview"), - { - ssr: false, - } -); +import useTrackCourse from '@/hooks/tracking/useTrackCourse'; +import { ProgressSpinner } from 'primereact/progressspinner'; export default function CourseDetails({ processedEvent, paidCourse, lessons, decryptionPerformed, handlePaymentSuccess, handlePaymentError }) { - const [author, setAuthor] = useState(null); - const [nAddress, setNAddress] = useState(null); const [zapAmount, setZapAmount] = useState(0); - const [user, setUser] = useState(null); - const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); - const { returnImageProxy } = useImageProxy(); - const { data: session, status } = useSession(); + const [author, setAuthor] = useState(null); + const [nAddress, setNAddress] = useState(null); const router = useRouter(); - const {ndk, addSigner} = useNDKContext(); + const { returnImageProxy } = useImageProxy(); + const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); + const { data: session, status } = useSession(); + const { showToast } = useToast(); const windowWidth = useWindowWidth(); const isMobileView = windowWidth <= 768; + const { ndk } = useNDKContext(); - useEffect(() => { - if (session) { - setUser(session.user); - } - }, [session]); + const { isCompleted } = useTrackCourse({ + courseId: processedEvent?.d, + paidCourse, + decryptionPerformed + }); const fetchAuthor = useCallback(async (pubkey) => { if (!pubkey) return; @@ -57,12 +50,6 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec useEffect(() => { if (processedEvent) { - fetchAuthor(processedEvent.pubkey); - } - }, [fetchAuthor, processedEvent]); - - useEffect(() => { - if (processedEvent?.d) { const naddr = nip19.naddrEncode({ pubkey: processedEvent.pubkey, kind: processedEvent.kind, @@ -73,6 +60,12 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec } }, [processedEvent]); + useEffect(() => { + if (processedEvent) { + fetchAuthor(processedEvent.pubkey); + } + }, [fetchAuthor, processedEvent]); + useEffect(() => { if (zaps.length > 0) { const total = getTotalFromZaps(zaps, processedEvent); @@ -80,12 +73,35 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec } }, [zaps, processedEvent]); + const handleDelete = async () => { + try { + const response = await axios.delete(`/api/courses/${processedEvent.d}`); + if (response.status === 204) { + showToast('success', 'Success', 'Course deleted successfully.'); + router.push('/'); + } + } catch (error) { + showToast('error', 'Error', 'Failed to delete course. Please try again.'); + } + } + const renderPaymentMessage = () => { + if (session?.user && session.user?.role?.subscribed && decryptionPerformed) { + return + } + + if (paidCourse && decryptionPerformed && author && processedEvent?.pubkey !== session?.user?.pubkey && !session?.user?.role?.subscribed) { + return + } + + if (paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey) { + return + } + if (paidCourse && !decryptionPerformed) { return ( - purchase?.courseId === processedEvent.d - ); - - if (paidCourse && decryptionPerformed && author && session?.user?.role?.subscribed && processedEvent?.pubkey !== session?.user?.pubkey) { - return - } - - if (paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey) { - return - } - - if (paidCourse && decryptionPerformed && author && coursePurchased) { - return - } - return null; }; if (!processedEvent || !author) { - return ( -
- ); + return
; } return ( -
-
- router.push('/')} /> -
-
-
- {processedEvent && processedEvent.topics && processedEvent.topics.length > 0 && ( +
+
+ course image +
+
+
+ router.push('/')} /> +
+ {isCompleted && } +
+

{processedEvent.name}

+
+ {processedEvent.topics && processedEvent.topics.length > 0 && ( processedEvent.topics.map((topic, index) => ( - + )) )}
-

{processedEvent?.name}

-

{processedEvent?.description}

-
+
+
{processedEvent.description && ( + processedEvent.description.split('\n').map((line, index) => ( +

{line}

+ )) + )} +
+ -
- {processedEvent && ( -
- course thumbnail -
- {renderPaymentMessage()} - -
+
+ {renderPaymentMessage()} + {processedEvent?.pubkey === session?.user?.pubkey ? ( +
+ router.push(`/details/${processedEvent.id}/edit`)} label="Edit" severity='warning' outlined /> + + window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={isMobileView ? null : "View Nostr Event"} tooltipOptions={{ position: paidCourse ? 'left' : 'right' }} /> +
+ ) : ( +
+ window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={isMobileView ? null : "View Nostr Event"} tooltipOptions={{ position: paidCourse ? 'left' : 'right' }} />
)}
- {typeof window !== 'undefined' && nAddress !== null && ( -
- -
- )} -
- { - processedEvent?.content && - } -
); } \ No newline at end of file diff --git a/src/components/content/courses/CourseDetailsNew.js b/src/components/content/courses/CourseDetailsNew.js deleted file mode 100644 index 810b9a3..0000000 --- a/src/components/content/courses/CourseDetailsNew.js +++ /dev/null @@ -1,192 +0,0 @@ -import React, { useEffect, useState, useCallback } from 'react'; -import axios from 'axios'; -import { useToast } from "@/hooks/useToast"; -import { Tag } from 'primereact/tag'; -import Image from 'next/image'; -import { useRouter } from 'next/router'; -import CoursePaymentButton from "@/components/bitcoinConnect/CoursePaymentButton"; -import ZapDisplay from '@/components/zaps/ZapDisplay'; -import GenericButton from '@/components/buttons/GenericButton'; -import { nip19 } from 'nostr-tools'; -import { useImageProxy } from '@/hooks/useImageProxy'; -import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscription'; -import { getTotalFromZaps } from '@/utils/lightning'; -import { useSession } from 'next-auth/react'; -import useWindowWidth from "@/hooks/useWindowWidth"; -import { useNDKContext } from "@/context/NDKContext"; -import { findKind0Fields } from '@/utils/nostr'; -import appConfig from "@/config/appConfig"; -import useTrackCourse from '@/hooks/tracking/useTrackCourse'; -import { ProgressSpinner } from 'primereact/progressspinner'; - -export default function CourseDetailsNew({ processedEvent, paidCourse, lessons, decryptionPerformed, handlePaymentSuccess, handlePaymentError }) { - const [zapAmount, setZapAmount] = useState(0); - const [author, setAuthor] = useState(null); - const [nAddress, setNAddress] = useState(null); - const router = useRouter(); - const { returnImageProxy } = useImageProxy(); - const { zaps, zapsLoading, zapsError } = useZapsSubscription({ event: processedEvent }); - const { data: session, status } = useSession(); - const { showToast } = useToast(); - const windowWidth = useWindowWidth(); - const isMobileView = windowWidth <= 768; - const { ndk } = useNDKContext(); - - const { isCompleted } = useTrackCourse({ - courseId: processedEvent?.d, - paidCourse, - decryptionPerformed - }); - - const fetchAuthor = useCallback(async (pubkey) => { - if (!pubkey) return; - const author = await ndk.getUser({ pubkey }); - const profile = await author.fetchProfile(); - const fields = await findKind0Fields(profile); - if (fields) { - setAuthor(fields); - } - }, [ndk]); - - useEffect(() => { - if (processedEvent) { - const naddr = nip19.naddrEncode({ - pubkey: processedEvent.pubkey, - kind: processedEvent.kind, - identifier: processedEvent.d, - relayUrls: appConfig.defaultRelayUrls - }); - setNAddress(naddr); - } - }, [processedEvent]); - - useEffect(() => { - if (processedEvent) { - fetchAuthor(processedEvent.pubkey); - } - }, [fetchAuthor, processedEvent]); - - useEffect(() => { - if (zaps.length > 0) { - const total = getTotalFromZaps(zaps, processedEvent); - setZapAmount(total); - } - }, [zaps, processedEvent]); - - const handleDelete = async () => { - try { - const response = await axios.delete(`/api/courses/${processedEvent.d}`); - if (response.status === 204) { - showToast('success', 'Success', 'Course deleted successfully.'); - router.push('/'); - } - } catch (error) { - showToast('error', 'Error', 'Failed to delete course. Please try again.'); - } - } - - const renderPaymentMessage = () => { - if (session?.user && session.user?.role?.subscribed && decryptionPerformed) { - return - } - - if (paidCourse && decryptionPerformed && author && processedEvent?.pubkey !== session?.user?.pubkey && !session?.user?.role?.subscribed) { - return - } - - if (paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey) { - return - } - - if (paidCourse && !decryptionPerformed) { - return ( - - ); - } - - return null; - }; - - if (!processedEvent || !author) { - return
; - } - - return ( -
-
- course image -
-
-
- router.push('/')} /> -
- {isCompleted && } -
-

{processedEvent.name}

-
- {processedEvent.topics && processedEvent.topics.length > 0 && ( - processedEvent.topics.map((topic, index) => ( - - )) - )} -
-
-
{processedEvent.description && ( - processedEvent.description.split('\n').map((line, index) => ( -

{line}

- )) - )} -
- -
- {renderPaymentMessage()} - {processedEvent?.pubkey === session?.user?.pubkey ? ( -
- router.push(`/details/${processedEvent.id}/edit`)} label="Edit" severity='warning' outlined /> - - window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={isMobileView ? null : "View Nostr Event"} tooltipOptions={{ position: paidCourse ? 'left' : 'right' }} /> -
- ) : ( -
- window.open(`https://nostr.band/${nAddress}`, '_blank')} tooltip={isMobileView ? null : "View Nostr Event"} tooltipOptions={{ position: paidCourse ? 'left' : 'right' }} /> -
- )} -
-
-
-
- ); -} \ No newline at end of file diff --git a/src/components/content/documents/DocumentDetails.js b/src/components/content/documents/DocumentDetails.js index dd4a133..6fe0a3e 100644 --- a/src/components/content/documents/DocumentDetails.js +++ b/src/components/content/documents/DocumentDetails.js @@ -99,8 +99,7 @@ const DocumentDetails = ({ processedEvent, topics, title, summary, image, price,

{ if (dbUser) { const fields = await findKind0Fields(profile); - // See if any of the fields values have changed compared to dbUser - const updatedFields = Object.keys(fields).reduce((acc, key) => { - if (fields[key] !== dbUser[key] && key !== "lud16") { + // Only update 'avatar' or 'username' if they are different from kind0 fields on the dbUser + const updatedFields = ['avatar', 'username'].reduce((acc, key) => { + if (fields[key] !== dbUser[key]) { acc[key] = fields[key]; } return acc; @@ -42,7 +42,7 @@ const authorize = async (pubkey) => { } // Combine user object with kind0Fields, giving priority to kind0Fields - const combinedUser = { ...dbUser, ...fields }; + const combinedUser = { ...dbUser, kind0: fields }; return combinedUser; } else { @@ -73,10 +73,10 @@ const authorize = async (pubkey) => { const fullUser = await getUserByPubkey(pubkey); - return fullUser; + return { ...fullUser, kind0: fields }; } else { dbUser = await createUser(payload); - return dbUser; + return { ...dbUser, kind0: fields }; } } } @@ -132,41 +132,14 @@ export const authOptions = { console.error("Failed to update user"); return null; } - token.user = updatedUser; + const fullUser = await getUserByPubkey(pk); + token.user = fullUser; } catch (error) { console.error("Ephemeral key pair generation error:", error); return null; } } - - // // todo this does not work on first login only the second time - // if (user && appConfig.authorPubkeys.includes(user?.pubkey) && !user?.role) { - // console.log("user in appConfig condition", user); - // // create a new author role for this user - // const role = await createRole({ - // userId: user.id, - // admin: true, - // subscribed: false, - // }); - - // console.log("role", role); - - // if (!role) { - // console.error("Failed to create role"); - // return null; - // } - - // console.log("user in appConfig condition", user); - - // const updatedUser = await updateUser(user.id, {role: role.id}); - // if (!updatedUser) { - // console.error("Failed to update user"); - // return null; - // } - // token.user = updatedUser; - // } - - // Add combined user object to the token + if (user) { token.user = user; } diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js index f6b7044..970d96e 100644 --- a/src/pages/course/[slug]/index.js +++ b/src/pages/course/[slug]/index.js @@ -1,7 +1,7 @@ import React, { useEffect, useState, useCallback } from "react"; import { useRouter } from "next/router"; import { parseCourseEvent, parseEvent, findKind0Fields } from "@/utils/nostr"; -import CourseDetailsNew from "@/components/content/courses/CourseDetailsNew"; +import CourseDetails from "@/components/content/courses/CourseDetails"; import VideoLesson from "@/components/content/courses/VideoLesson"; import DocumentLesson from "@/components/content/courses/DocumentLesson"; import { useNDKContext } from "@/context/NDKContext"; @@ -214,7 +214,7 @@ const Course = () => { return ( <> {course && paidCourse !== null && ( -