import React, { useState, useRef, useEffect } from 'react'; import { useSession } from 'next-auth/react'; import { useRouter } from 'next/router'; import Image from 'next/image'; import NostrIcon from '../../public/images/nostr.png'; import { Card } from 'primereact/card'; import { useToast } from "@/hooks/useToast"; import useWindowWidth from "@/hooks/useWindowWidth"; import GenericButton from '@/components/buttons/GenericButton'; import InteractivePromotionalCarousel from '@/components/content/carousels/InteractivePromotionalCarousel'; import axios from 'axios'; import { Menu } from "primereact/menu"; import { ProgressSpinner } from 'primereact/progressspinner'; import SubscriptionPaymentButtons from '@/components/bitcoinConnect/SubscriptionPaymentButton'; import CalendlyEmbed from '@/components/profile/subscription/CalendlyEmbed'; import CancelSubscription from '@/components/profile/subscription/CancelSubscription'; import RenewSubscription from '@/components/profile/subscription/RenewSubscription'; import Nip05Form from '@/components/profile/subscription/Nip05Form'; import LightningAddressForm from '@/components/profile/subscription/LightningAddressForm'; import MoreInfo from '@/components/MoreInfo'; const AboutPage = () => { const { data: session, update } = useSession(); const { showToast } = useToast(); const router = useRouter(); const windowWidth = useWindowWidth(); const menu = useRef(null); const [user, setUser] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const [subscribed, setSubscribed] = useState(false); const [subscribedUntil, setSubscribedUntil] = useState(null); const [subscriptionExpiredAt, setSubscriptionExpiredAt] = useState(null); const [calendlyVisible, setCalendlyVisible] = useState(false); const [lightningAddressVisible, setLightningAddressVisible] = useState(false); const [nip05Visible, setNip05Visible] = useState(false); const [cancelSubscriptionVisible, setCancelSubscriptionVisible] = useState(false); const [renewSubscriptionVisible, setRenewSubscriptionVisible] = useState(false); const isTabView = windowWidth <= 1160; const isMobile = windowWidth < 668; // FAQ content for the modal const faqContent = (

How does the subscription work?

Think of the subscriptions as a Patreon-type model. You pay a monthly fee and in return you get access to premium features and all of the paid content. You can cancel at any time.

What are the benefits of a subscription?

The subscription gives you access to all of the premium features and all of the paid content. You can cancel at any time.

How much does the subscription cost?

The subscription is 50,000 sats per month.

How do I Subscribe? (Pay as you go)

The pay as you go subscription is a one-time payment that gives you access to all of the premium features for one month. You will need to manually renew your subscription every month.

How do I Subscribe? (Recurring)

The recurring subscription option allows you to submit a Nostr Wallet Connect URI that will be used to automatically send the subscription fee every month. You can cancel at any time.

Can I cancel my subscription?

Yes, you can cancel your subscription at any time. Your access will remain active until the end of the current billing period.

What happens if I don't renew my subscription?

If you don't renew your subscription, your access to 1:1 calendar and paid content will be removed. However, you will still have access to your PlebDevs Lightning Address, NIP-05, and any content that you paid for.

What is Nostr Wallet Connect?

Nostr Wallet Connect is a Nostr-based authentication method that allows you to connect your Nostr wallet to the PlebDevs platform. This will allow you to subscribe to the platform in an auto recurring manner which still gives you full control over your wallet and the ability to cancel at any time from your wallet.

); useEffect(() => { if (session && session?.user) { setUser(session.user); } }, [session]) useEffect(() => { if (user && user.role) { setSubscribed(user.role.subscribed); const subscribedAt = new Date(user.role.lastPaymentAt); const subscribedUntil = new Date(subscribedAt.getTime() + 31 * 24 * 60 * 60 * 1000); setSubscribedUntil(subscribedUntil); if (user.role.subscriptionExpiredAt) { const expiredAt = new Date(user.role.subscriptionExpiredAt) setSubscriptionExpiredAt(expiredAt); } } }, [user]); const handleSubscriptionSuccess = async (response) => { setIsProcessing(true); try { const apiResponse = await axios.put('/api/users/subscription', { userId: session.user.id, isSubscribed: true, }); if (apiResponse.data) { await update(); showToast('success', 'Subscription Successful', 'Your subscription has been activated.'); } else { throw new Error('Failed to update subscription status'); } } catch (error) { console.error('Subscription update error:', error); showToast('error', 'Subscription Update Failed', `Error: ${error.message}`); } finally { setIsProcessing(false); } }; const handleSubscriptionError = (error) => { console.error('Subscription error:', error); showToast('error', 'Subscription Failed', `An error occurred: ${error.message}`); setIsProcessing(false); }; const handleRecurringSubscriptionSuccess = async () => { setIsProcessing(true); try { await update(); showToast('success', 'Recurring Subscription Activated', 'Your recurring subscription has been set up successfully.'); } catch (error) { console.error('Session update error:', error); showToast('error', 'Session Update Failed', `Error: ${error.message}`); } finally { setIsProcessing(false); } }; const menuItems = [ { label: "Schedule 1:1", icon: "pi pi-calendar", command: () => setCalendlyVisible(true), }, { label: session?.user?.platformLightningAddress ? "Update PlebDevs Lightning Address" : "Claim PlebDevs Lightning Address", icon: "pi pi-bolt", command: () => setLightningAddressVisible(true), }, { label: session?.user?.platformNip05?.name ? "Update PlebDevs Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05", icon: "pi pi-at", command: () => setNip05Visible(true), }, { label: "Renew Subscription", icon: "pi pi-sync", command: () => setRenewSubscriptionVisible(true), }, { label: "Cancel Subscription", icon: "pi pi-trash", command: () => setCancelSubscriptionVisible(true), }, ]; const copyToClipboard = async (text) => { try { await navigator.clipboard.writeText(text); showToast("success", "Copied", "Copied Lightning Address to clipboard"); if (window && window?.webln && window?.webln?.lnurl) { await window.webln.enable(); const result = await window.webln.lnurl("austin@bitcoinpleb.dev"); if (result && result?.preimage) { showToast("success", "Payment Sent", "Thank you for your donation!"); } } } catch (err) { console.error('Failed to copy:', err); } }; return (
{/* For non-logged in users */} {!session?.user && ( <>

The PlebDevs subscription unlocks all paid content, grants access to our 1:1 calendar for tutoring, support, and mentorship, and grants you your own personal plebdevs.com Lightning Address and Nostr NIP-05 identity.

Subscribe monthly with a pay-as-you-go option or set up an auto-recurring subscription using Nostr Wallet Connect.

Login to start your subscription!

router.push('/auth/signin')} className='text-[#f8f8ff] w-fit' rounded icon="pi pi-user" />
)} {/* Subscription Card */}

Subscribe to PlebDevs

} > {!isProcessing ? (
{/* Only show premium benefits when not subscribed or session doesn't exist */} {(!session?.user || (session?.user && !subscribed)) && ( <>

Unlock Premium Benefits

Subscribe now and elevate your development journey!

Access ALL current and future PlebDevs content
Personal mentorship & guidance and access to exclusive 1:1 booking calendar
Claim your own personal plebdevs.com Lightning Address
Nostr Claim your own personal plebdevs.com Nostr NIP-05 identity
)}
{/* Status Messages */} {session && session?.user ? ( <> {subscribed && !user?.role?.nwc && (
Subscribed!

Thank you for your support 🎉

Pay-as-you-go subscription must be manually renewed on {subscribedUntil?.toLocaleDateString()}

)} {subscribed && user?.role?.nwc && (
Subscribed!

Thank you for your support 🎉

Recurring subscription will AUTO renew on {subscribedUntil?.toLocaleDateString()}

)} {(!subscribed && !subscriptionExpiredAt) && (
You currently have no active subscription

Subscribe below to unlock all premium features and content.

)} {subscriptionExpiredAt && (
Your subscription expired on {subscriptionExpiredAt.toLocaleDateString()}

Renew below to continue enjoying all premium benefits.

)} ) : (
Login to manage your subscription

Sign in to access subscription features and management.

)}
{/* Payment Buttons */} {(!session?.user || (session?.user && !subscribed)) && ( )}
) : (
Processing subscription...
)} {/* Subscription Management */} {session?.user && subscribed && ( <>
setCalendlyVisible(true)} /> setNip05Visible(true)} /> } onClick={() => setLightningAddressVisible(true)} />
setRenewSubscriptionVisible(true)} /> setCancelSubscriptionVisible(true)} />
)}

Content Distribution:

All content is published to Nostr and actively pulled from Nostr relays

  • Nostr: Content is stored on and read from Nostr relays however a database is used for storing metadata and filtering content.
  • Zaps: Zaps are currently initated through Zapper - https://zapper.nostrapps.org
  • Comments: For comments we are leveraging ZapThreads - https://zapthreads.com

Content Types:

High signal, Bitcoin, Lightning, and Nostr educational content.

  • Documents: Markdown documents posted as NIP-23 long-form events on Nostr.
  • Videos: Formatted markdown documents with rich media support, including embedded videos, also saved as NIP-23 events.
  • Courses: Nostr lists (NIP-51) that combines multiple documents and videos into a structured learning path.

Content Monetization:

All content is zappable and some content is PAID requiring a Lightning purchase or Subscription through the platform for permanent access.

  • Free: Free content is available to all users.
    * can be viewed on PlebDevs or any nostr client that supports NIP-23 and NIP-51.
  • Paid: Paid content is available for purchase with Lightning.
    * published to nostr but encrypted with plebdevs private key, currently only viewable on PlebDevs platform.

Subscriptions:

The PlebDevs subscription unlocks all paid content, gives access to our 1:1 calendar for tutoring/help, and grants you a plebdevs.com Lightning Address and Nostr NIP-05 identity.

  • Pay-as-you-go: 50,000 sats - A one-time payment that gives you access to all of the premium features for one month
    * you will need to manually renew your subscription every month.
  • Recurring: 50,000 sats - A subscription option allows you to submit a Nostr Wallet Connect URI that will be used to automatically send the subscription fee every month
    * you can cancel at any time.

Feeds:

All of the current PlebDevs Community channels.

  • Nostr: Public plebdevs nostr chat (Read / Write)
    * this is the only feed that you can write to from the plebdevs platform currently.
  • Discord: PlebDevs Discord server (Read Only)
    * discord was the original home of the PlebDevs community, look at us now!
  • StackerNews: StackerNews ~devs territory (Read Only)
    * a territory is like a 'subreddit' on stackernews, plebdevs owns the ~devs territory.
{/* techstack */}

Tech Stack:

Frontend:

  • Next.js: A React framework for building server-side rendered (SSR) web applications.
  • Tailwind CSS: A utility-first CSS framework for rapidly building custom designs.
  • PrimeReact: A React UI library for building modern, responsive web applications.

Backend:

  • Prisma: A database toolkit for Node.js and TypeScript.
  • PostgreSQL: A powerful, open source object-relational database system.
  • Redis: An in-memory data structure store, used as a database, cache, and message broker.

Infrastructure:

  • Vercel: A cloud platform for building modern web applications.
  • Docker: A platform for building, shipping, and running distributed applications.
  • Digital Ocean (CDN): A cloud platform for building modern web applications.

Open Source Tools:

window.open('https://github.com/austinkelsay/plebdevs', '_blank')} /> window.open('https://x.com/pleb_devs', '_blank')} /> } tooltip="Nostr" onClick={() => window.open('https://nostr.com/plebdevs@plebdevs.com', '_blank')} /> window.open('https://www.youtube.com/@plebdevs', '_blank')} /> copyToClipboard("austin@bitcoinpleb.dev")} />
{/* Dialog Components */} setCalendlyVisible(false)} userId={session?.user?.id} userName={session?.user?.username || user?.kind0?.username} userEmail={session?.user?.email} /> setCancelSubscriptionVisible(false)} /> setRenewSubscriptionVisible(false)} subscribedUntil={subscribedUntil} /> setNip05Visible(false)} /> setLightningAddressVisible(false)} /> ); }; export default AboutPage;