mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 10:51:20 +00:00
Merge pull request #7 from AustinKelsay/feature/refine-user-table
Refine User, Lightning Address, Nip05 tables
This commit is contained in:
commit
62c4070804
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `LightningAddress` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `Nip05` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "LightningAddress" DROP CONSTRAINT "LightningAddress_userId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Nip05" DROP CONSTRAINT "Nip05_userId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "lud16" TEXT,
|
||||
ADD COLUMN "nip05" TEXT;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "LightningAddress";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "Nip05";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PlatformNip05" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"pubkey" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "PlatformNip05_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PlatformLightningAddress" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"allowsNostr" BOOLEAN NOT NULL DEFAULT true,
|
||||
"description" TEXT,
|
||||
"maxSendable" INTEGER NOT NULL DEFAULT 10000000,
|
||||
"minSendable" INTEGER NOT NULL DEFAULT 1,
|
||||
"invoiceMacaroon" TEXT NOT NULL,
|
||||
"lndCert" TEXT,
|
||||
"lndHost" TEXT NOT NULL,
|
||||
"lndPort" TEXT NOT NULL DEFAULT '8080',
|
||||
|
||||
CONSTRAINT "PlatformLightningAddress_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PlatformNip05_userId_key" ON "PlatformNip05"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "PlatformLightningAddress_userId_key" ON "PlatformLightningAddress"("userId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PlatformNip05" ADD CONSTRAINT "PlatformNip05_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PlatformLightningAddress" ADD CONSTRAINT "PlatformLightningAddress_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -0,0 +1,10 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `image` on the `User` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `name` on the `User` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" DROP COLUMN "image",
|
||||
DROP COLUMN "name";
|
@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "PlatformLightningAddress" ALTER COLUMN "maxSendable" SET DEFAULT 10000000000,
|
||||
ALTER COLUMN "maxSendable" SET DATA TYPE BIGINT,
|
||||
ALTER COLUMN "minSendable" SET DEFAULT 1000,
|
||||
ALTER COLUMN "minSendable" SET DATA TYPE BIGINT;
|
@ -13,15 +13,12 @@ generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
// todo name and username?
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
pubkey String? @unique
|
||||
privkey String?
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
username String? @unique
|
||||
avatar String?
|
||||
purchased Purchase[]
|
||||
@ -36,8 +33,10 @@ model User {
|
||||
updatedAt DateTime @updatedAt
|
||||
userLessons UserLesson[]
|
||||
userCourses UserCourse[]
|
||||
nip05 Nip05?
|
||||
lightningAddress LightningAddress?
|
||||
nip05 String?
|
||||
lud16 String?
|
||||
platformNip05 PlatformNip05?
|
||||
platformLightningAddress PlatformLightningAddress?
|
||||
userBadges UserBadge[]
|
||||
}
|
||||
|
||||
@ -226,7 +225,7 @@ model UserCourse {
|
||||
@@unique([userId, courseId])
|
||||
}
|
||||
|
||||
model Nip05 {
|
||||
model PlatformNip05 {
|
||||
id String @id @default(uuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
@ -236,16 +235,15 @@ model Nip05 {
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model LightningAddress {
|
||||
model PlatformLightningAddress {
|
||||
id String @id @default(uuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
name String
|
||||
allowsNostr Boolean @default(true)
|
||||
description String?
|
||||
// todo: change to BigInt to support native millisats
|
||||
maxSendable Int @default(10000000)
|
||||
minSendable Int @default(1)
|
||||
maxSendable BigInt @default(10000000000)
|
||||
minSendable BigInt @default(1000)
|
||||
invoiceMacaroon String
|
||||
lndCert String?
|
||||
lndHost String
|
||||
|
@ -213,7 +213,7 @@ const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
||||
<p className='text-lg text-white'>
|
||||
By{' '}
|
||||
<a rel='noreferrer noopener' target='_blank' className='text-blue-300 hover:underline'>
|
||||
{lesson.author?.username || lesson.author?.name || lesson.author?.pubkey}
|
||||
{lesson.author?.username || lesson.author?.pubkey}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -86,7 +86,7 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid }) => {
|
||||
<p className='text-lg'>
|
||||
Created by{' '}
|
||||
<a rel='noreferrer noopener' target='_blank' className='text-blue-500 hover:underline'>
|
||||
{lesson.author?.username || lesson.author?.name || lesson.author?.pubkey}
|
||||
{lesson.author?.username || lesson.author?.pubkey}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -127,7 +127,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
|
||||
<p className='text-lg text-white'>
|
||||
By{' '}
|
||||
<a rel='noreferrer noopener' target='_blank' className='text-blue-300 hover:underline'>
|
||||
{lesson.author?.username || lesson.author?.name || lesson.author?.pubkey}
|
||||
{lesson.author?.username || lesson.author?.pubkey}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -78,7 +78,7 @@ const DraftCourseLesson = ({ lesson, course }) => {
|
||||
<p className='text-lg'>
|
||||
Created by{' '}
|
||||
<a rel='noreferrer noopener' target='_blank' className='text-blue-500 hover:underline'>
|
||||
{lesson.author?.username || lesson.author?.name || lesson.author?.pubkey}
|
||||
{lesson.author?.username || lesson.author?.pubkey}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -189,7 +189,7 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted
|
||||
<p className='text-lg text-white'>
|
||||
Created by{' '}
|
||||
<a rel='noreferrer noopener' target='_blank' className='text-blue-300 hover:underline'>
|
||||
{lesson.author?.username || lesson.author?.name || lesson.author?.pubkey}
|
||||
{lesson.author?.username || lesson.author?.pubkey}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -56,7 +56,7 @@ const UserAvatar = () => {
|
||||
return null; // Or return a loader/spinner/placeholder
|
||||
} else if (user && Object.keys(user).length > 0) {
|
||||
// User exists, show username or pubkey
|
||||
const displayName = user.username || user?.name || user?.email || user?.pubkey.slice(0, 10) + '...' || "Anon";
|
||||
const displayName = user?.username || user?.email || user?.pubkey?.slice(0, 10) + '...' || "Anon";
|
||||
|
||||
const items = [
|
||||
{
|
||||
|
@ -79,6 +79,9 @@ const LinkAccountsCard = ({ session }) => {
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// clear the local storage of the users ephemeral keys
|
||||
localStorage.removeItem('anonymousPrivkey');
|
||||
localStorage.removeItem('anonymousPubkey');
|
||||
showToast('success', 'Success', 'Nostr account linked successfully');
|
||||
// Refresh the session to get updated user data
|
||||
await update();
|
||||
@ -181,7 +184,7 @@ const LinkAccountsCard = ({ session }) => {
|
||||
icon="pi pi-github"
|
||||
onClick={handleGithubLink}
|
||||
disabled={isGithubLinked}
|
||||
className={`text-[#f8f8ff] w-[250px] mx-auto`}
|
||||
className={`text-[#f8f8ff] w-[250px]`}
|
||||
rounded
|
||||
/>
|
||||
|
||||
@ -190,7 +193,7 @@ const LinkAccountsCard = ({ session }) => {
|
||||
icon={<Image src="/images/nostr-icon-white.png" width={20} height={20} alt="Nostr" className="mr-2" />}
|
||||
onClick={handleNostrLink}
|
||||
disabled={isNostrLinked}
|
||||
className={`text-[#f8f8ff] w-[250px] mx-auto flex items-center justify-center`}
|
||||
className={`text-[#f8f8ff] w-[250px]`}
|
||||
rounded
|
||||
/>
|
||||
|
||||
@ -199,7 +202,7 @@ const LinkAccountsCard = ({ session }) => {
|
||||
icon="pi pi-envelope"
|
||||
onClick={handleEmailLink}
|
||||
disabled={isEmailLinked}
|
||||
className={`text-[#f8f8ff] w-[250px] mx-auto`}
|
||||
className={`text-[#f8f8ff] w-[250px]`}
|
||||
rounded
|
||||
/>
|
||||
</div>
|
||||
|
@ -70,7 +70,7 @@ const UserProfileCard = ({ user }) => {
|
||||
className="rounded-full m-2 mt-0 object-cover max-w-[100px] max-h-[100px]"
|
||||
/>
|
||||
<h3 className="text-center">
|
||||
{user.username || user?.name || user?.email || "Anon"}
|
||||
{user.username || user?.email || "Anon"}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2 justify-center w-full overflow-hidden">
|
||||
{
|
||||
@ -98,13 +98,17 @@ const UserProfileCard = ({ user }) => {
|
||||
</div>
|
||||
<div className='w-full flex flex-row justify-between'>
|
||||
<div className="flex flex-col justify-between gap-4 my-2">
|
||||
{user?.lightningAddress ? (
|
||||
{user?.platformLightningAddress ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">Lightning Address:</span> {user.lightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lightningAddress.name + "@plebdevs.com")} />
|
||||
<span className="font-bold">Lightning Address:</span> {user.platformLightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.platformLightningAddress.name + "@plebdevs.com")} />
|
||||
</h4>
|
||||
) : user?.lud16 ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">Lightning Address:</span> {user.lud16} <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lud16)} />
|
||||
</h4>
|
||||
) : (
|
||||
<div className="flex flex-row justify-between bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<h4 >
|
||||
<h4>
|
||||
<span className="font-bold">Lightning Address:</span> None
|
||||
</h4>
|
||||
<MoreInfo
|
||||
@ -115,9 +119,23 @@ const UserProfileCard = ({ user }) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{user?.nip05 ? (
|
||||
{user?.platformNip05 ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">NIP-05:</span> {user.nip05.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.nip05.name + "@plebdevs.com")} />
|
||||
<span className="font-bold">NIP-05:</span>{' '}
|
||||
{user.platformNip05.name}@plebdevs.com{' '}
|
||||
<i
|
||||
className="pi pi-copy cursor-pointer hover:text-gray-400"
|
||||
onClick={() => copyToClipboard(`${user.platformNip05.name}@plebdevs.com`)}
|
||||
/>
|
||||
</h4>
|
||||
) : user?.nip05 ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">NIP-05:</span>{' '}
|
||||
{user.nip05}{' '}
|
||||
<i
|
||||
className="pi pi-copy cursor-pointer hover:text-gray-400"
|
||||
onClick={() => copyToClipboard(user.nip05)}
|
||||
/>
|
||||
</h4>
|
||||
) : (
|
||||
<div className="flex flex-row justify-between bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
@ -125,9 +143,9 @@ const UserProfileCard = ({ user }) => {
|
||||
<span className="font-bold">NIP-05:</span> None
|
||||
</h4>
|
||||
<MoreInfo
|
||||
tooltip="PlebDevs Custom NIP-05"
|
||||
modalTitle="PlebDevs Custom NIP-05"
|
||||
modalBody="This is a placeholder for your PlebDevs issued NIP-05 (claimable through subscription)"
|
||||
tooltip="NIP-05 Info"
|
||||
modalTitle="What is NIP-05?"
|
||||
modalBody="NIP-05 is a verification standard in Nostr that links your identity to a domain name, similar to how Twitter verifies accounts. It helps prove ownership of your identity."
|
||||
className="text-xs"
|
||||
/>
|
||||
</div>
|
||||
@ -169,7 +187,7 @@ const UserProfileCard = ({ user }) => {
|
||||
/>
|
||||
</div>
|
||||
<h3 className="self-start">
|
||||
{user.username || user?.name || user?.email || "Anon"}
|
||||
{user.username || user?.email || "Anon"}
|
||||
</h3>
|
||||
{
|
||||
user?.pubkey && (
|
||||
@ -193,10 +211,14 @@ const UserProfileCard = ({ user }) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between gap-4 my-2">
|
||||
{user?.lightningAddress ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">Lightning Address:</span> {user.lightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lightningAddress.name + "@plebdevs.com")} />
|
||||
<div className="flex flex-col justify-between gap-2">
|
||||
{user?.platformLightningAddress ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-2 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">Lightning Address:</span> {user.platformLightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.platformLightningAddress.name + "@plebdevs.com")} />
|
||||
</h4>
|
||||
) : user?.lud16 ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-2 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">Lightning Address:</span> {user.lud16} <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lud16)} />
|
||||
</h4>
|
||||
) : (
|
||||
<div className="flex flex-row justify-between bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
@ -211,9 +233,23 @@ const UserProfileCard = ({ user }) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{user?.nip05 ? (
|
||||
{user?.platformNip05 ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">NIP-05:</span> {user.nip05.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.nip05.name + "@plebdevs.com")} />
|
||||
<span className="font-bold">NIP-05:</span>{' '}
|
||||
{user.platformNip05.name}@plebdevs.com{' '}
|
||||
<i
|
||||
className="pi pi-copy cursor-pointer hover:text-gray-400"
|
||||
onClick={() => copyToClipboard(`${user.platformNip05.name}@plebdevs.com`)}
|
||||
/>
|
||||
</h4>
|
||||
) : user?.nip05 ? (
|
||||
<h4 className="bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
<span className="font-bold">NIP-05:</span>{' '}
|
||||
{user.nip05}{' '}
|
||||
<i
|
||||
className="pi pi-copy cursor-pointer hover:text-gray-400"
|
||||
onClick={() => copyToClipboard(user.nip05)}
|
||||
/>
|
||||
</h4>
|
||||
) : (
|
||||
<div className="flex flex-row justify-between bg-gray-900 rounded-lg p-3 max-lap:w-fit min-w-[240px]">
|
||||
|
@ -14,8 +14,8 @@ const LightningAddressForm = ({ visible, onHide }) => {
|
||||
const [existingLightningAddress, setExistingLightningAddress] = useState(null);
|
||||
const [name, setName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [maxSendable, setMaxSendable] = useState(10000000);
|
||||
const [minSendable, setMinSendable] = useState(1);
|
||||
const [maxSendable, setMaxSendable] = useState(10000000000);
|
||||
const [minSendable, setMinSendable] = useState(1000);
|
||||
const [invoiceMacaroon, setInvoiceMacaroon] = useState('');
|
||||
const [lndCert, setLndCert] = useState('');
|
||||
const [lndHost, setLndHost] = useState('');
|
||||
@ -26,18 +26,18 @@ const LightningAddressForm = ({ visible, onHide }) => {
|
||||
const { showToast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (session && session?.user && !session?.user?.lightningAddress) {
|
||||
setName(session.user.name || '');
|
||||
} else if (session && session?.user && session?.user?.lightningAddress) {
|
||||
setExistingLightningAddress(session.user.lightningAddress);
|
||||
setName(session.user.lightningAddress.name || '');
|
||||
setDescription(session.user.lightningAddress.description || '');
|
||||
setMaxSendable(session.user.lightningAddress.maxSendable || 10000000);
|
||||
setMinSendable(session.user.lightningAddress.minSendable || 1);
|
||||
setInvoiceMacaroon(session.user.lightningAddress.invoiceMacaroon || '');
|
||||
setLndCert(session.user.lightningAddress.lndCert || '');
|
||||
setLndHost(session.user.lightningAddress.lndHost || '');
|
||||
setLndPort(session.user.lightningAddress.lndPort || '8080');
|
||||
if (session && session?.user && !session?.user?.platformLightningAddress) {
|
||||
setName(session.user.username || '');
|
||||
} else if (session && session?.user && session?.user?.platformLightningAddress) {
|
||||
setExistingLightningAddress(session.user.platformLightningAddress);
|
||||
setName(session.user.platformLightningAddress.name || '');
|
||||
setDescription(session.user.platformLightningAddress.description || '');
|
||||
setMaxSendable(Number(session.user.platformLightningAddress.maxSendable) || 10000000000);
|
||||
setMinSendable(Number(session.user.platformLightningAddress.minSendable) || 1000);
|
||||
setInvoiceMacaroon(session.user.platformLightningAddress.invoiceMacaroon || '');
|
||||
setLndCert(session.user.platformLightningAddress.lndCert || '');
|
||||
setLndHost(session.user.platformLightningAddress.lndHost || '');
|
||||
setLndPort(session.user.platformLightningAddress.lndPort || '8080');
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
@ -46,10 +46,21 @@ const LightningAddressForm = ({ visible, onHide }) => {
|
||||
try {
|
||||
let response;
|
||||
const lowercaseName = name.toLowerCase();
|
||||
const data = {
|
||||
name: lowercaseName,
|
||||
description,
|
||||
maxSendable: BigInt(maxSendable).toString(),
|
||||
minSendable: BigInt(minSendable).toString(),
|
||||
invoiceMacaroon,
|
||||
lndCert,
|
||||
lndHost,
|
||||
lndPort
|
||||
};
|
||||
|
||||
if (existingLightningAddress) {
|
||||
response = await axios.put(`/api/users/${session.user.id}/lightning-address`, { name: lowercaseName, description, maxSendable, minSendable, invoiceMacaroon, lndCert, lndHost, lndPort });
|
||||
response = await axios.put(`/api/users/${session.user.id}/lightning-address`, data);
|
||||
} else {
|
||||
response = await axios.post(`/api/users/${session.user.id}/lightning-address`, { name: lowercaseName, description, maxSendable, minSendable, invoiceMacaroon, lndCert, lndHost, lndPort });
|
||||
response = await axios.post(`/api/users/${session.user.id}/lightning-address`, data);
|
||||
}
|
||||
if (!existingLightningAddress && response.status === 201) {
|
||||
showToast('success', 'Lightning Address Claimed', 'Your Lightning Address has been claimed');
|
||||
@ -101,10 +112,9 @@ const LightningAddressForm = ({ visible, onHide }) => {
|
||||
<label>Description</label>
|
||||
<InputText placeholder="Description" value={description} onChange={(e) => setDescription(e.target.value)} tooltip='This is your Lightning Address description, it will be displayed as the description LUD16 lnurlp endpoint' />
|
||||
<label>Max Sendable</label>
|
||||
{/* Todo: max is 2,147,483 sats until i imlement bigInt for sat amounts */}
|
||||
<InputNumber placeholder="Max Sendable" value={maxSendable} onChange={(e) => setMaxSendable(e.target.value)} max={2147483647} min={1000} tooltip='This is the maximum amount of sats that can be sent to your Lightning Address (currently denominated in sats NOT msat)' />
|
||||
<InputNumber placeholder="Max Sendable" value={maxSendable} onChange={(e) => setMaxSendable(e.value)} min={1000} tooltip='Maximum amount in millisats (1000 millisats = 1 sat)' />
|
||||
<label>Min Sendable</label>
|
||||
<InputNumber placeholder="Min Sendable" value={minSendable} onChange={(e) => setMinSendable(e.target.value)} min={1} max={2147483647} tooltip='This is the minimum amount of sats that can be sent to your Lightning Address (currently denominated in sats NOT msat)' />
|
||||
<InputNumber placeholder="Min Sendable" value={minSendable} onChange={(e) => setMinSendable(e.value)} min={1} tooltip='Minimum amount in millisats (1000 millisats = 1 sat)' />
|
||||
<label>Invoice Macaroon</label>
|
||||
<InputText placeholder="Invoice Macaroon" value={invoiceMacaroon} onChange={(e) => setInvoiceMacaroon(e.target.value)} tooltip='This is your LND Invoice Macaroon, it is used to create invoices for your Lightning Address but DOES NOT grant access to move funds from your LND node' />
|
||||
<label>LND Cert</label>
|
||||
|
@ -19,13 +19,13 @@ const Nip05Form = ({ visible, onHide }) => {
|
||||
const { showToast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (session && session?.user && !session?.user?.nip05) {
|
||||
if (session && session?.user && !session?.user?.platformNip05) {
|
||||
setPubkey(session.user.pubkey || '');
|
||||
setName(session.user.name || '');
|
||||
} else if (session && session?.user && session?.user?.nip05) {
|
||||
setExistingNip05(session.user.nip05);
|
||||
setPubkey(session.user.nip05.pubkey || '');
|
||||
setName(session.user.nip05.name || '');
|
||||
setName(session.user.username || '');
|
||||
} else if (session && session?.user && session?.user?.platformNip05) {
|
||||
setExistingNip05(session.user.platformNip05);
|
||||
setPubkey(session.user.platformNip05.pubkey || '');
|
||||
setName(session.user.platformNip05.name || '');
|
||||
}
|
||||
}, [session]);
|
||||
|
||||
|
@ -7,7 +7,6 @@ import { useSession } from 'next-auth/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Badge } from 'primereact/badge';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Menu } from "primereact/menu";
|
||||
import { Message } from "primereact/message";
|
||||
@ -104,14 +103,14 @@ const SubscribeModal = ({ user }) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: session?.user?.lightningAddress ? "Update PlebDevs Lightning Address" : "Claim PlebDevs Lightning Address",
|
||||
label: session?.user?.platformLightningAddress ? "Update PlebDevs Lightning Address" : "Claim PlebDevs Lightning Address",
|
||||
icon: "pi pi-bolt",
|
||||
command: () => {
|
||||
setLightningAddressVisible(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: session?.user?.nip05 ? "Update PlebDevs Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05",
|
||||
label: session?.user?.platformNip05?.name ? "Update PlebDevs Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05",
|
||||
icon: "pi pi-at",
|
||||
command: () => {
|
||||
setNip05Visible(true);
|
||||
@ -152,14 +151,14 @@ const SubscribeModal = ({ user }) => {
|
||||
{subscribed && !user?.role?.nwc && (
|
||||
<div className="flex flex-col">
|
||||
<Message className="w-fit" severity="success" text="Subscribed!" />
|
||||
<p className="mt-4">Thank you for your support 🎉</p>
|
||||
<p className="mt-3">Thank you for your support 🎉</p>
|
||||
<p className="text-sm text-gray-400">Pay-as-you-go subscription will renew on {subscribedUntil.toLocaleDateString()}</p>
|
||||
</div>
|
||||
)}
|
||||
{subscribed && user?.role?.nwc && (
|
||||
<div className="flex flex-col">
|
||||
<Message className="w-fit" severity="success" text="Subscribed!" />
|
||||
<p className="mt-4">Thank you for your support 🎉</p>
|
||||
<p className="mt-3">Thank you for your support 🎉</p>
|
||||
<p className="text-sm text-gray-400">Recurring subscription will AUTO renew on {subscribedUntil.toLocaleDateString()}</p>
|
||||
</div>
|
||||
)}
|
||||
@ -168,7 +167,7 @@ const SubscribeModal = ({ user }) => {
|
||||
<Message className="w-fit" severity="info" text="You currently have no active subscription" />
|
||||
<GenericButton
|
||||
label="Subscribe"
|
||||
className="w-auto mt-4 text-[#f8f8ff]"
|
||||
className="w-auto mt-3 text-[#f8f8ff]"
|
||||
onClick={() => setVisible(true)}
|
||||
/>
|
||||
</div>
|
||||
@ -232,7 +231,7 @@ const SubscribeModal = ({ user }) => {
|
||||
visible={calendlyVisible}
|
||||
onHide={() => setCalendlyVisible(false)}
|
||||
userId={session?.user?.id}
|
||||
userName={session?.user?.name || user?.kind0?.username}
|
||||
userName={session?.user?.username || user?.kind0?.username}
|
||||
userEmail={session?.user?.email}
|
||||
/>
|
||||
<CancelSubscription
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import axios from 'axios';
|
||||
import { Card } from 'primereact/card';
|
||||
@ -21,9 +20,7 @@ import RenewSubscription from '@/components/profile/subscription/RenewSubscripti
|
||||
const UserSubscription = () => {
|
||||
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);
|
||||
@ -178,8 +175,8 @@ const UserSubscription = () => {
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col gap-4">
|
||||
<GenericButton severity="info" outlined className="w-fit text-start" label="Schedule 1:1" icon="pi pi-calendar" onClick={() => setCalendlyVisible(true)} />
|
||||
<GenericButton severity="help" outlined className="w-fit text-start" label={user?.nip05 ? "Update Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05"} icon="pi pi-at" onClick={() => setNip05Visible(true)} />
|
||||
<GenericButton severity="warning" outlined className="w-fit text-start" label={user?.lightningAddress ? "Update Lightning Address" : "Claim PlebDevs Lightning Address"} icon={<i style={{ color: "orange" }} className="pi pi-bolt mr-2"></i>} onClick={() => setLightningAddressVisible(true)} />
|
||||
<GenericButton severity="help" outlined className="w-fit text-start" label={user?.platformNip05?.name ? "Update Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05"} icon="pi pi-at" onClick={() => setNip05Visible(true)} />
|
||||
<GenericButton severity="warning" outlined className="w-fit text-start" label={user?.platformLightningAddress ? "Update Lightning Address" : "Claim PlebDevs Lightning Address"} icon={<i style={{ color: "orange" }} className="pi pi-bolt mr-2"></i>} onClick={() => setLightningAddressVisible(true)} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -225,7 +222,7 @@ const UserSubscription = () => {
|
||||
visible={calendlyVisible}
|
||||
onHide={() => setCalendlyVisible(false)}
|
||||
userId={session?.user?.id}
|
||||
userName={session?.user?.name || user?.kind0?.username}
|
||||
userName={session?.user?.username || user?.kind0?.username}
|
||||
userEmail={session?.user?.email}
|
||||
/>
|
||||
<CancelSubscription
|
||||
|
@ -1,24 +1,24 @@
|
||||
import prisma from "@/db/prisma";
|
||||
|
||||
export const getAllLightningAddresses = async () => {
|
||||
return await prisma.lightningAddress.findMany();
|
||||
return await prisma.platformLightningAddress.findMany();
|
||||
};
|
||||
|
||||
export const getLightningAddressByName = async (name) => {
|
||||
return await prisma.lightningAddress.findFirst({
|
||||
return await prisma.platformLightningAddress.findFirst({
|
||||
where: { name },
|
||||
});
|
||||
};
|
||||
|
||||
export const getLightningAddress = async (userId) => {
|
||||
return await prisma.lightningAddress.findUnique({
|
||||
return await prisma.platformLightningAddress.findUnique({
|
||||
where: { userId },
|
||||
});
|
||||
};
|
||||
|
||||
export const createLightningAddress = async (userId, name, description, maxSendable, minSendable, invoiceMacaroon, lndCert, lndHost, lndPort) => {
|
||||
try {
|
||||
return await prisma.lightningAddress.create({
|
||||
return await prisma.platformLightningAddress.create({
|
||||
data: {
|
||||
userId,
|
||||
name,
|
||||
@ -38,14 +38,14 @@ export const createLightningAddress = async (userId, name, description, maxSenda
|
||||
};
|
||||
|
||||
export const updateLightningAddress = async (userId, data) => {
|
||||
return await prisma.lightningAddress.update({
|
||||
return await prisma.platformLightningAddress.update({
|
||||
where: { userId },
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteLightningAddress = async (userId) => {
|
||||
return await prisma.lightningAddress.delete({
|
||||
return await prisma.platformLightningAddress.delete({
|
||||
where: { userId },
|
||||
});
|
||||
};
|
||||
|
@ -1,36 +1,36 @@
|
||||
import prisma from "@/db/prisma";
|
||||
|
||||
export const getAllNip05s = async () => {
|
||||
return await prisma.nip05.findMany();
|
||||
return await prisma.platformNip05.findMany();
|
||||
};
|
||||
|
||||
export const getNip05ByName = async (name) => {
|
||||
return await prisma.nip05.findFirst({
|
||||
return await prisma.platformNip05.findFirst({
|
||||
where: { name },
|
||||
});
|
||||
};
|
||||
|
||||
export const getNip05 = async (userId) => {
|
||||
return await prisma.nip05.findUnique({
|
||||
return await prisma.platformNip05.findUnique({
|
||||
where: { userId },
|
||||
});
|
||||
};
|
||||
|
||||
export const createNip05 = async (userId, pubkey, name) => {
|
||||
return await prisma.nip05.create({
|
||||
return await prisma.platformNip05.create({
|
||||
data: { userId, pubkey, name },
|
||||
});
|
||||
};
|
||||
|
||||
export const updateNip05 = async (userId, data) => {
|
||||
return await prisma.nip05.update({
|
||||
return await prisma.platformNip05.update({
|
||||
where: { userId },
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteNip05 = async (userId) => {
|
||||
return await prisma.nip05.delete({
|
||||
return await prisma.platformNip05.delete({
|
||||
where: { userId },
|
||||
});
|
||||
};
|
||||
|
@ -50,8 +50,8 @@ export const getUserById = async (id) => {
|
||||
lesson: true,
|
||||
},
|
||||
},
|
||||
nip05: true,
|
||||
lightningAddress: true,
|
||||
platformNip05: true,
|
||||
platformLightningAddress: true,
|
||||
userBadges: {
|
||||
include: {
|
||||
badge: true
|
||||
@ -82,8 +82,8 @@ export const getUserByPubkey = async (pubkey) => {
|
||||
lesson: true,
|
||||
},
|
||||
},
|
||||
nip05: true,
|
||||
lightningAddress: true,
|
||||
platformNip05: true,
|
||||
platformLightningAddress: true,
|
||||
userBadges: {
|
||||
include: {
|
||||
badge: true
|
||||
@ -278,8 +278,8 @@ export const getUserByEmail = async (email) => {
|
||||
lesson: true,
|
||||
},
|
||||
},
|
||||
nip05: true,
|
||||
lightningAddress: true,
|
||||
platformNip05: true,
|
||||
platformLightningAddress: true,
|
||||
userBadges: {
|
||||
include: {
|
||||
badge: true
|
||||
|
@ -33,14 +33,19 @@ const syncNostrProfile = async (pubkey) => {
|
||||
let dbUser = await getUserByPubkey(pubkey);
|
||||
|
||||
if (dbUser) {
|
||||
// Update existing user if kind0 fields differ
|
||||
if (fields.avatar !== dbUser.avatar || fields.username !== dbUser.username) {
|
||||
// Update existing user if any of the kind0 fields differ
|
||||
if (fields.avatar !== dbUser.avatar ||
|
||||
fields.username !== dbUser.username ||
|
||||
fields.lud16 !== dbUser.lud16 ||
|
||||
fields.nip05 !== dbUser.nip05) {
|
||||
|
||||
const updates = {
|
||||
...(fields.avatar !== dbUser.avatar && { avatar: fields.avatar }),
|
||||
...(fields.username !== dbUser.username && {
|
||||
username: fields.username,
|
||||
name: fields.username
|
||||
})
|
||||
username: fields.username
|
||||
}),
|
||||
...(fields.lud16 !== dbUser.lud16 && { lud16: fields.lud16 }),
|
||||
...(fields.nip05 !== dbUser.nip05 && { nip05: fields.nip05 })
|
||||
};
|
||||
await updateUser(dbUser.id, updates);
|
||||
dbUser = await getUserByPubkey(pubkey);
|
||||
@ -48,11 +53,14 @@ const syncNostrProfile = async (pubkey) => {
|
||||
} else {
|
||||
// Create new user
|
||||
const username = fields.username || pubkey.slice(0, 8);
|
||||
const lud16 = fields.lud16 || null;
|
||||
const nip05 = fields.nip05 || null;
|
||||
const payload = {
|
||||
pubkey,
|
||||
username,
|
||||
avatar: fields.avatar,
|
||||
name: username
|
||||
lud16,
|
||||
nip05
|
||||
};
|
||||
|
||||
dbUser = await createUser(payload);
|
||||
@ -126,8 +134,7 @@ export const authOptions = {
|
||||
if (!user) {
|
||||
user = await createUser({
|
||||
...keys,
|
||||
username: `anon-${keys.pubkey.slice(0, 8)}`,
|
||||
name: `anon-${keys.pubkey.slice(0, 8)}`
|
||||
username: `anon-${keys.pubkey.slice(0, 8)}`
|
||||
});
|
||||
}
|
||||
return { ...user, privkey: keys.privkey };
|
||||
@ -157,7 +164,7 @@ export const authOptions = {
|
||||
id: profile.id.toString(),
|
||||
pubkey: keys.pubkey,
|
||||
privkey: keys.privkey,
|
||||
name: profile.login,
|
||||
username: profile.login,
|
||||
email: profile.email,
|
||||
avatar: profile.avatar_url
|
||||
};
|
||||
@ -194,8 +201,8 @@ export const authOptions = {
|
||||
purchased: true,
|
||||
userCourses: true,
|
||||
userLessons: true,
|
||||
nip05: true,
|
||||
lightningAddress: true,
|
||||
platformNip05: true,
|
||||
platformLightningAddress: true,
|
||||
userBadges: true
|
||||
}
|
||||
});
|
||||
@ -232,7 +239,6 @@ export const authOptions = {
|
||||
username: user.email.split('@')[0],
|
||||
email: user.email,
|
||||
avatar: user.image,
|
||||
name: user.email.split('@')[0],
|
||||
}
|
||||
|
||||
// Update the user with the new keypair
|
||||
@ -256,7 +262,15 @@ export const authOptions = {
|
||||
|
||||
if (userData) {
|
||||
const fullUser = await getUserById(userData.id);
|
||||
|
||||
// Convert BigInt values to strings if they exist
|
||||
if (fullUser.platformLightningAddress) {
|
||||
fullUser.platformLightningAddress = {
|
||||
...fullUser.platformLightningAddress,
|
||||
maxSendable: fullUser.platformLightningAddress.maxSendable?.toString(),
|
||||
minSendable: fullUser.platformLightningAddress.minSendable?.toString()
|
||||
};
|
||||
}
|
||||
|
||||
// Get the user's GitHub account if it exists
|
||||
const githubAccount = await prisma.account.findFirst({
|
||||
where: {
|
||||
@ -273,13 +287,14 @@ export const authOptions = {
|
||||
role: fullUser.role,
|
||||
username: fullUser.username,
|
||||
avatar: fullUser.avatar,
|
||||
name: fullUser.name,
|
||||
email: fullUser.email,
|
||||
userCourses: fullUser.userCourses,
|
||||
userLessons: fullUser.userLessons,
|
||||
purchased: fullUser.purchased,
|
||||
nip05: fullUser.nip05,
|
||||
lightningAddress: fullUser.lightningAddress,
|
||||
lud16: fullUser.lud16,
|
||||
platformNip05: fullUser.platformNip05,
|
||||
platformLightningAddress: fullUser.platformLightningAddress,
|
||||
githubUsername: token.githubUsername,
|
||||
createdAt: fullUser.createdAt,
|
||||
userBadges: fullUser.userBadges
|
||||
@ -300,15 +315,22 @@ export const authOptions = {
|
||||
return session;
|
||||
},
|
||||
async jwt({ token, user, account, profile, session }) {
|
||||
// Convert BigInt values to strings if they exist
|
||||
if (user?.platformLightningAddress) {
|
||||
user.platformLightningAddress = {
|
||||
...user.platformLightningAddress,
|
||||
maxSendable: user.platformLightningAddress.maxSendable?.toString(),
|
||||
minSendable: user.platformLightningAddress.minSendable?.toString()
|
||||
};
|
||||
}
|
||||
|
||||
// If we are linking a github account to an existing email or anon account (we have privkey)
|
||||
if (account?.provider === "github" && user?.id && user?.pubkey && user?.privkey) {
|
||||
try {
|
||||
// First update the user's profile with GitHub info
|
||||
const updatedUser = await updateUser(user.id, {
|
||||
name: profile?.login || profile?.name,
|
||||
username: profile?.login || profile?.name,
|
||||
avatar: profile?.avatar_url,
|
||||
image: profile?.avatar_url,
|
||||
});
|
||||
|
||||
// Get the updated user
|
||||
@ -341,10 +363,8 @@ export const authOptions = {
|
||||
if (!existingGithubAccount) {
|
||||
// Update user profile with GitHub info
|
||||
const updatedUser = await updateUser(user.id, {
|
||||
name: profile?.login || profile?.name,
|
||||
username: profile?.login || profile?.name,
|
||||
avatar: profile?.avatar_url,
|
||||
image: profile?.avatar_url,
|
||||
email: profile?.email // Add email if user wants it
|
||||
});
|
||||
|
||||
|
@ -60,16 +60,21 @@ export default async function handler(req, res) {
|
||||
descriptionHash = Buffer.from(hash, 'hex').toString('base64');
|
||||
}
|
||||
|
||||
// Convert amount from millisatoshis to satoshis
|
||||
if (amount < (foundAddress.minSendable)) {
|
||||
// Check amount against BigInt min/max values
|
||||
if (amount < foundAddress.minSendable) {
|
||||
res.status(400).json({ error: 'Amount too low' });
|
||||
return;
|
||||
} else if (amount > (foundAddress.maxSendable || Number.MAX_SAFE_INTEGER)) {
|
||||
} else if (amount > foundAddress.maxSendable) {
|
||||
res.status(400).json({ error: 'Amount too high' });
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.post(`${BACKEND_URL}/api/lightning-address/lnd`, { amount: amount, description_hash: descriptionHash, name: slug, zap_request: queryParams?.nostr ? queryParams.nostr : null }, {
|
||||
const response = await axios.post(`${BACKEND_URL}/api/lightning-address/lnd`, {
|
||||
amount: amount,
|
||||
description_hash: descriptionHash,
|
||||
name: slug,
|
||||
zap_request: queryParams?.nostr ? queryParams.nostr : null
|
||||
}, {
|
||||
headers: {
|
||||
'Authorization': PLEBDEVS_API_KEY
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ export default async function handler(req, res) {
|
||||
|
||||
res.status(200).json({
|
||||
callback: `${BACKEND_URL}/api/lightning-address/callback/${foundAddress.name}`,
|
||||
maxSendable: foundAddress.maxSendable || 10000000000,
|
||||
minSendable: foundAddress.minSendable || 1000,
|
||||
maxSendable: parseInt(foundAddress.maxSendable),
|
||||
minSendable: parseInt(foundAddress.minSendable),
|
||||
metadata: JSON.stringify(metadata),
|
||||
tag: 'payRequest',
|
||||
allowsNostr: true,
|
||||
|
@ -30,9 +30,23 @@ export default async function handler(req, res) {
|
||||
case 'POST':
|
||||
try {
|
||||
const { name, description, maxSendable, minSendable, invoiceMacaroon, lndCert, lndHost, lndPort } = req.body;
|
||||
const lightningAddress = await createLightningAddress(userId, name, description, maxSendable, minSendable, invoiceMacaroon, lndCert, lndHost, lndPort);
|
||||
const lightningAddress = await createLightningAddress(
|
||||
userId,
|
||||
name,
|
||||
description,
|
||||
BigInt(maxSendable),
|
||||
BigInt(minSendable),
|
||||
invoiceMacaroon,
|
||||
lndCert,
|
||||
lndHost,
|
||||
lndPort
|
||||
);
|
||||
|
||||
res.status(201).json(lightningAddress);
|
||||
res.status(201).json({
|
||||
...lightningAddress,
|
||||
maxSendable: lightningAddress.maxSendable.toString(),
|
||||
minSendable: lightningAddress.minSendable.toString()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error creating Lightning Address:', error);
|
||||
res.status(500).json({ error: 'Error creating Lightning Address', errorMessage: error.message });
|
||||
|
@ -17,7 +17,7 @@ export default function SignIn() {
|
||||
|
||||
const handleEmailSignIn = async (e) => {
|
||||
e.preventDefault()
|
||||
await signIn("email", { email, callbackUrl: '/' })
|
||||
await signIn("email", { email, callbackUrl: '/profile' })
|
||||
}
|
||||
|
||||
const handleNostrSignIn = async (e) => {
|
||||
@ -28,7 +28,7 @@ export default function SignIn() {
|
||||
try {
|
||||
const user = await ndk.signer.user()
|
||||
const pubkey = user?._pubkey
|
||||
signIn("nostr", { pubkey })
|
||||
signIn("nostr", { pubkey, callbackUrl: '/profile' })
|
||||
} catch (error) {
|
||||
console.error("Error signing Nostr event:", error)
|
||||
}
|
||||
@ -46,7 +46,7 @@ export default function SignIn() {
|
||||
pubkey: storedPubkey,
|
||||
privkey: storedPrivkey,
|
||||
redirect: false,
|
||||
callbackUrl: '/'
|
||||
callbackUrl: '/profile'
|
||||
});
|
||||
|
||||
if (result?.ok) {
|
||||
@ -59,7 +59,7 @@ export default function SignIn() {
|
||||
if (session?.user?.pubkey && session?.user?.privkey) {
|
||||
localStorage.setItem('anonymousPubkey', session.user.pubkey);
|
||||
localStorage.setItem('anonymousPrivkey', session.user.privkey);
|
||||
router.push('/');
|
||||
router.push('/profile');
|
||||
} else {
|
||||
console.error("Session data incomplete:", session);
|
||||
}
|
||||
@ -77,11 +77,11 @@ export default function SignIn() {
|
||||
const result = await signIn("recovery", {
|
||||
nsec,
|
||||
redirect: false,
|
||||
callbackUrl: '/'
|
||||
callbackUrl: '/profile'
|
||||
});
|
||||
|
||||
if (result?.ok) {
|
||||
router.push('/');
|
||||
router.push('/profile');
|
||||
} else {
|
||||
console.error("Recovery login failed:", result?.error);
|
||||
}
|
||||
@ -93,7 +93,7 @@ export default function SignIn() {
|
||||
useEffect(() => {
|
||||
// Redirect if already signed in
|
||||
if (session?.user) {
|
||||
router.push('/');
|
||||
router.push('/profile');
|
||||
}
|
||||
}, [session, router]);
|
||||
|
||||
|
@ -327,7 +327,7 @@ export default function Draft() {
|
||||
<p className='text-lg'>
|
||||
Created by{' '}
|
||||
<a href={`https://nostr.com/${hexToNpub(user?.pubkey)}`} rel='noreferrer noopener' target='_blank' className='text-blue-500 hover:underline'>
|
||||
{user?.username || user?.name || user?.pubkey.slice(0, 10)}{'... '}
|
||||
{user?.username || user?.pubkey.slice(0, 10)}{'... '}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
|
@ -101,12 +101,12 @@ const Subscribe = () => {
|
||||
command: () => setCalendlyVisible(true),
|
||||
},
|
||||
{
|
||||
label: session?.user?.lightningAddress ? "Update PlebDevs Lightning Address" : "Claim PlebDevs Lightning Address",
|
||||
label: session?.user?.platformLightningAddress ? "Update PlebDevs Lightning Address" : "Claim PlebDevs Lightning Address",
|
||||
icon: "pi pi-bolt",
|
||||
command: () => setLightningAddressVisible(true),
|
||||
},
|
||||
{
|
||||
label: session?.user?.nip05 ? "Update PlebDevs Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05",
|
||||
label: session?.user?.platformNip05?.name ? "Update PlebDevs Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05",
|
||||
icon: "pi pi-at",
|
||||
command: () => setNip05Visible(true),
|
||||
},
|
||||
@ -122,23 +122,6 @@ const Subscribe = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const subscriptionCardTitleAndButton = (
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<span className="text-xl text-900 font-bold text-white">Plebdevs Subscription</span>
|
||||
<i
|
||||
className="pi pi-ellipsis-h text-2xlcursor-pointer hover:opacity-75"
|
||||
onClick={(e) => menu.current.toggle(e)}
|
||||
></i>
|
||||
<Menu model={menuItems} popup ref={menu} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const subscriptionCardTitle = (
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<span className="text-xl text-900 font-bold text-white">Plebdevs Subscription</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
{windowWidth < 768 && (
|
||||
@ -242,8 +225,8 @@ const Subscribe = () => {
|
||||
<Card title="Subscription Benefits" className="mb-4">
|
||||
<div className="flex flex-col gap-4">
|
||||
<GenericButton severity="info" outlined className="w-fit text-start" label="Schedule 1:1" icon="pi pi-calendar" onClick={() => setCalendlyVisible(true)} />
|
||||
<GenericButton severity="help" outlined className="w-fit text-start" label={session?.user?.nip05 ? "Update Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05"} icon="pi pi-at" onClick={() => setNip05Visible(true)} />
|
||||
<GenericButton severity="warning" outlined className="w-fit text-start" label={session?.user?.lightningAddress ? "Update Lightning Address" : "Claim PlebDevs Lightning Address"} icon={<i style={{ color: "orange" }} className="pi pi-bolt mr-2"></i>} onClick={() => setLightningAddressVisible(true)} />
|
||||
<GenericButton severity="help" outlined className="w-fit text-start" label={session?.user?.platformNip05?.name ? "Update Nostr NIP-05" : "Claim PlebDevs Nostr NIP-05"} icon="pi pi-at" onClick={() => setNip05Visible(true)} />
|
||||
<GenericButton severity="warning" outlined className="w-fit text-start" label={session?.user?.platformLightningAddress ? "Update Lightning Address" : "Claim PlebDevs Lightning Address"} icon={<i style={{ color: "orange" }} className="pi pi-bolt mr-2"></i>} onClick={() => setLightningAddressVisible(true)} />
|
||||
</div>
|
||||
</Card>
|
||||
<Card title="Manage Subscription" className="mb-4">
|
||||
@ -296,7 +279,7 @@ const Subscribe = () => {
|
||||
visible={calendlyVisible}
|
||||
onHide={() => setCalendlyVisible(false)}
|
||||
userId={session?.user?.id}
|
||||
userName={session?.user?.name || user?.kind0?.username}
|
||||
userName={session?.user?.username || user?.kind0?.username}
|
||||
userEmail={session?.user?.email}
|
||||
/>
|
||||
<CancelSubscription
|
||||
|
@ -40,6 +40,12 @@ export const findKind0Fields = async (kind0) => {
|
||||
fields.lud16 = lud16;
|
||||
}
|
||||
|
||||
const nip05 = findTruthyPropertyValue(kind0, ['nip05']);
|
||||
|
||||
if (nip05) {
|
||||
fields.nip05 = nip05;
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user