diff --git a/src/components/bitcoinConnect/CoursePaymentButton.js b/src/components/bitcoinConnect/CoursePaymentButton.js index cec2000..463c3e4 100644 --- a/src/components/bitcoinConnect/CoursePaymentButton.js +++ b/src/components/bitcoinConnect/CoursePaymentButton.js @@ -58,6 +58,8 @@ const CoursePaymentButton = ({ lnAddress, amount, onSuccess, onError, courseId } amountPaid: parseInt(amount, 10) }; + console.log('purchaseData', purchaseData); + const result = await axios.post('/api/purchase/course', purchaseData); if (result.status === 200) { diff --git a/src/components/content/courses/CourseDetails.js b/src/components/content/courses/CourseDetails.js index f04c2c1..6bf2347 100644 --- a/src/components/content/courses/CourseDetails.js +++ b/src/components/content/courses/CourseDetails.js @@ -4,8 +4,9 @@ import { useImageProxy } from '@/hooks/useImageProxy'; import ZapDisplay from '@/components/zaps/ZapDisplay'; import { getTotalFromZaps } from '@/utils/lightning'; import { Tag } from 'primereact/tag'; -import { nip19, nip04 } from 'nostr-tools'; +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'; @@ -36,26 +37,6 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec const lnAddress = process.env.NEXT_PUBLIC_LIGHTNING_ADDRESS; - useEffect(() => { - console.log("processedEvent", processedEvent); - }, [processedEvent]); - - useEffect(() => { - console.log("lessons", lessons); - }, [lessons]); - - useEffect(() => { - console.log("zaps", zaps); - }, [zaps]); - - useEffect(() => { - console.log("paidCourse", paidCourse); - }, [paidCourse]); - - useEffect(() => { - console.log("decryptionPerformed", decryptionPerformed); - }, [decryptionPerformed]); - useEffect(() => { if (session) { setUser(session.user); @@ -96,6 +77,38 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec } }, [zaps, processedEvent]); + const renderPaymentMessage = () => { + if (paidCourse && !decryptionPerformed) { + return ( + <CoursePaymentButton + lnAddress={lnAddress} + amount={processedEvent.price} + onSuccess={handlePaymentSuccess} + onError={handlePaymentError} + courseId={processedEvent.d} + /> + ); + } + + const coursePurchased = session?.user?.purchased?.some(purchase => + purchase?.courseId === processedEvent.d + ); + + if (paidCourse && decryptionPerformed && author && session?.user?.role?.subscribed && processedEvent?.pubkey !== session?.user?.pubkey) { + return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You are subscribed so you can access all paid content`} icon="pi pi-check" label="Subscribed" severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" /> + } + + if (paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey) { + return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You created this paid course, users must pay ${processedEvent.price} sats to access it`} icon="pi pi-check" label={`Price ${processedEvent.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" /> + } + + if (paidCourse && decryptionPerformed && author && coursePurchased) { + return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You have purchased this course`} icon="pi pi-check" label="Purchased" severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" /> + } + + return null; + }; + if (!processedEvent || !author) { return ( <div className="flex justify-center items-center h-screen"> @@ -146,21 +159,7 @@ export default function CourseDetails({ processedEvent, paidCourse, lessons, dec className="w-[344px] h-[194px] object-cover object-top rounded-lg" /> <div className='w-full flex justify-between items-center'> - {paidCourse && !decryptionPerformed && ( - <CoursePaymentButton - lnAddress={lnAddress} - amount={processedEvent.price} - onSuccess={handlePaymentSuccess} - onError={handlePaymentError} - resourceId={processedEvent.d} - /> - )} - {paidCourse && decryptionPerformed && author && processedEvent?.pubkey !== session?.user?.pubkey && ( - <p className='text-green-500'>Paid {processedEvent.price} sats</p> - )} - {paidCourse && author && processedEvent?.pubkey === session?.user?.pubkey && ( - <p className='text-green-500'>Price {processedEvent.price} sats</p> - )} + {renderPaymentMessage()} <ZapDisplay zapAmount={zapAmount} event={processedEvent} diff --git a/src/components/content/courses/CourseLesson.js b/src/components/content/courses/CourseLesson.js index 9d510d3..b1f5643 100644 --- a/src/components/content/courses/CourseLesson.js +++ b/src/components/content/courses/CourseLesson.js @@ -14,19 +14,11 @@ const MDDisplay = dynamic( } ); -const CourseLesson = ({ lesson, course }) => { +const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid }) => { const [zapAmount, setZapAmount] = useState(0); - const [paidResource, setPaidResource] = useState(false); - const [decryptedContent, setDecryptedContent] = useState(false); const { zaps, zapsLoading, zapsError } = useZapsQuery({ event: lesson, type: "lesson" }); const { returnImageProxy } = useImageProxy(); - useEffect(() => { - if (course.price) { - setPaidResource(true); - } - }, [course]); - useEffect(() => { if (!zaps || zapsLoading || zapsError) return; @@ -36,10 +28,10 @@ const CourseLesson = ({ lesson, course }) => { }, [zaps, zapsLoading, zapsError, lesson]); const renderContent = () => { - if (decryptedContent) { - return <MDDisplay className='p-4 rounded-lg w-full' source={decryptedContent} />; + if (isPaid && decryptionPerformed) { + return <MDDisplay className='p-4 rounded-lg w-full' source={lesson.content} />; } - if (paidResource && !decryptedContent) { + if (isPaid && !decryptionPerformed) { return <p className="text-center text-xl text-red-500">This content is paid and needs to be purchased before viewing.</p>; } if (lesson?.content) { diff --git a/src/components/content/resources/ResourceDetails.js b/src/components/content/resources/ResourceDetails.js index 15525d9..e39af99 100644 --- a/src/components/content/resources/ResourceDetails.js +++ b/src/components/content/resources/ResourceDetails.js @@ -4,6 +4,7 @@ import Image from "next/image"; import { useRouter } from "next/router"; import ResourcePaymentButton from "@/components/bitcoinConnect/ResourcePaymentButton"; import ZapDisplay from "@/components/zaps/ZapDisplay"; +import GenericButton from "@/components/buttons/GenericButton"; import { useImageProxy } from "@/hooks/useImageProxy"; import { useZapsSubscription } from "@/hooks/nostrQueries/zaps/useZapsSubscription"; import { getTotalFromZaps } from "@/utils/lightning"; @@ -26,6 +27,22 @@ const ResourceDetails = ({processedEvent, topics, title, summary, image, price, } }, [zaps, processedEvent]); + const renderPaymentMessage = () => { + if (session?.user && session.user?.role?.subscribed && decryptedContent) { + return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You are subscribed so you can access all paid content`} icon="pi pi-check" label="Subscribed" severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" /> + } + + if (paidResource && decryptedContent && author && processedEvent?.pubkey !== session?.user?.pubkey && !session?.user?.role?.subscribed) { + return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`Pay ${processedEvent.price} sats to access this content or subscribe to get access to all content`} icon="pi pi-check" label={`Paid ${processedEvent.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" /> + } + + if (paidResource && author && processedEvent?.pubkey === session?.user?.pubkey) { + return <GenericButton tooltipOptions={{position: 'top'}} tooltip={`You created this paid content, users must pay ${processedEvent.price} sats to access it`} icon="pi pi-check" label={`Price ${processedEvent.price} sats`} severity="success" outlined size="small" className="cursor-default hover:opacity-100 hover:bg-transparent focus:ring-0" /> + } + + return null; + }; + return ( <div className='w-full flex flex-row justify-between max-tab:flex-col max-mob:flex-col'> <i className='pi pi-arrow-left pr-8 cursor-pointer hover:opacity-75 max-tab:pl-2 max-tab:my-4' onClick={() => router.push('/')} /> @@ -76,11 +93,7 @@ const ResourceDetails = ({processedEvent, topics, title, summary, image, price, resourceId={processedEvent.d} />} - {/* if the resource has been paid for show a green paid x sats text */} - {paidResource && decryptedContent && author && !processedEvent?.pubkey === session?.user?.pubkey && <p className='text-green-500'>Paid {processedEvent.price} sats</p>} - - {/* if this is the author of the resource show a zap button */} - {paidResource && author && processedEvent?.pubkey === session?.user?.pubkey && <p className='text-green-500'>Price {processedEvent.price} sats</p>} + {renderPaymentMessage()} <ZapDisplay zapAmount={zapAmount} diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js index 5eb8a03..235cd52 100644 --- a/src/pages/course/[slug]/index.js +++ b/src/pages/course/[slug]/index.js @@ -129,6 +129,8 @@ const Course = () => { session.user?.role?.subscribed || session.user?.pubkey === course?.pubkey; + console.log('canAccess', canAccess); + if (canAccess && lessons.length > 0) { try { const decryptedLessons = await Promise.all(lessons.map(async (lesson) => { @@ -154,11 +156,12 @@ const Course = () => { } }, [course, lessons, paidCourse, decryptionPerformed]); - const handlePaymentSuccess = async (response, newCourse) => { + const handlePaymentSuccess = async (response) => { if (response && response?.preimage) { - console.log("newCourse", newCourse); const updated = await update(); console.log("session after update", updated); + showToast('success', 'Payment Success', 'You have successfully purchased this course'); + router.reload(); } else { showToast('error', 'Error', 'Failed to purchase course. Please try again.'); } @@ -187,7 +190,7 @@ const Course = () => { handlePaymentError={handlePaymentError} /> {lessons.length > 0 && lessons.map((lesson, index) => ( - <CourseLesson key={index} lesson={lesson} course={course} /> + <CourseLesson key={index} lesson={lesson} course={course} decryptionPerformed={decryptionPerformed} isPaid={paidCourse} /> ))} <div className="mx-auto my-6"> {course?.content && <MDDisplay className='p-4 rounded-lg' source={course.content} />}