added confirm modal and fixed some subscribing bugs
This commit is contained in:
parent
675e0c4507
commit
7a478f9efe
@ -4,8 +4,7 @@ import Image from "next/image";
|
|||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import useProfile from "@/lib/hooks/useProfile";
|
import useProfile from "@/lib/hooks/useProfile";
|
||||||
import { nip19 } from "nostr-tools";
|
import { HiOutlineLightningBolt } from "react-icons/hi";
|
||||||
import useEvents from "@/lib/hooks/useEvents";
|
|
||||||
import Spinner from "@/components/spinner";
|
import Spinner from "@/components/spinner";
|
||||||
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
||||||
import ProfileInfo from "./ProfileInfo";
|
import ProfileInfo from "./ProfileInfo";
|
||||||
@ -19,19 +18,22 @@ import {
|
|||||||
} from "@/lib/actions/zap";
|
} from "@/lib/actions/zap";
|
||||||
import { useModal } from "@/app/_providers/modal/provider";
|
import { useModal } from "@/app/_providers/modal/provider";
|
||||||
import { type NDKEvent } from "@nostr-dev-kit/ndk";
|
import { type NDKEvent } from "@nostr-dev-kit/ndk";
|
||||||
|
import { btcToSats, formatNumber } from "@/lib/utils";
|
||||||
|
import { formatDate } from "@/lib/utils/dates";
|
||||||
const EditListModal = dynamic(() => import("@/components/Modals/EditList"), {
|
const EditListModal = dynamic(() => import("@/components/Modals/EditList"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
const CreateEventModal = dynamic(() => import("@/components/Modals/NewEvent"), {
|
const CreateEventModal = dynamic(() => import("@/components/Modals/NewEvent"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
const ConfirmModal = dynamic(() => import("@/components/Modals/Confirm"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
export default function Header({ event }: { event: NDKEvent }) {
|
export default function Header({ event }: { event: NDKEvent }) {
|
||||||
const { currentUser } = useCurrentUser();
|
const { currentUser } = useCurrentUser();
|
||||||
const modal = useModal();
|
const modal = useModal();
|
||||||
const { ndk } = useNDK();
|
const { ndk } = useNDK();
|
||||||
const [sendingZap, setSendingZap] = useState(false);
|
|
||||||
const [checkingPayment, setCheckingPayment] = useState(false);
|
const [checkingPayment, setCheckingPayment] = useState(false);
|
||||||
const [hasValidPayment, setHasValidPayment] = useState(false);
|
const [hasValidPayment, setHasValidPayment] = useState(false);
|
||||||
const [syncingUsers, setSyncingUsers] = useState(false);
|
const [syncingUsers, setSyncingUsers] = useState(false);
|
||||||
@ -39,7 +41,6 @@ export default function Header({ event }: { event: NDKEvent }) {
|
|||||||
const { profile } = useProfile(pubkey);
|
const { profile } = useProfile(pubkey);
|
||||||
|
|
||||||
const noteIds = getTagsValues("e", event.tags).filter(Boolean);
|
const noteIds = getTagsValues("e", event.tags).filter(Boolean);
|
||||||
console.log("notes", event.tags);
|
|
||||||
const title =
|
const title =
|
||||||
getTagValues("title", event.tags) ??
|
getTagValues("title", event.tags) ??
|
||||||
getTagValues("name", event.tags) ??
|
getTagValues("name", event.tags) ??
|
||||||
@ -53,7 +54,7 @@ export default function Header({ event }: { event: NDKEvent }) {
|
|||||||
const description = getTagValues("description", event.tags);
|
const description = getTagValues("description", event.tags);
|
||||||
const rawEvent = event.rawEvent();
|
const rawEvent = event.rawEvent();
|
||||||
const subscriptionsEnabled = !!getTagValues("subscriptions", rawEvent.tags);
|
const subscriptionsEnabled = !!getTagValues("subscriptions", rawEvent.tags);
|
||||||
const priceInBTC = getTagValues("price", rawEvent.tags);
|
const priceInBTC = parseFloat(getTagValues("price", rawEvent.tags) ?? "0");
|
||||||
const isMember =
|
const isMember =
|
||||||
currentUser &&
|
currentUser &&
|
||||||
getTagsValues("p", rawEvent.tags).includes(currentUser.pubkey);
|
getTagsValues("p", rawEvent.tags).includes(currentUser.pubkey);
|
||||||
@ -99,6 +100,21 @@ export default function Header({ event }: { event: NDKEvent }) {
|
|||||||
setSyncingUsers(false);
|
setSyncingUsers(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function handleSendZap() {
|
||||||
|
try {
|
||||||
|
const result = await sendZap(
|
||||||
|
ndk!,
|
||||||
|
btcToSats(priceInBTC),
|
||||||
|
rawEvent,
|
||||||
|
`Access payment: ${title}`,
|
||||||
|
);
|
||||||
|
toast.success("Payment Sent!");
|
||||||
|
void handleCheckPayment();
|
||||||
|
} catch (err) {
|
||||||
|
console.log("error sending zap", err);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return (
|
return (
|
||||||
<div className="center pt-20 text-primary">
|
<div className="center pt-20 text-primary">
|
||||||
@ -155,7 +171,39 @@ export default function Header({ event }: { event: NDKEvent }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{subscriptionsEnabled && !isMember && <Button>Subscribe</Button>}
|
{subscriptionsEnabled && !isMember && (
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
modal?.show(
|
||||||
|
<ConfirmModal
|
||||||
|
title={`Subscribe to ${title}`}
|
||||||
|
onConfirm={handleSendZap}
|
||||||
|
ctaBody={
|
||||||
|
<>
|
||||||
|
<span>Zap to Subscribe</span>
|
||||||
|
<HiOutlineLightningBolt className="h-4 w-4" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p className="text-muted-forground">
|
||||||
|
{`Pay ${priceInBTC} BTC (${formatNumber(
|
||||||
|
btcToSats(priceInBTC),
|
||||||
|
)} sats) for year long access until ${formatDate(
|
||||||
|
new Date(
|
||||||
|
new Date().setFullYear(
|
||||||
|
new Date().getFullYear() + 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"MMM Do, YYYY",
|
||||||
|
)}`}
|
||||||
|
</p>
|
||||||
|
</ConfirmModal>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Subscribe
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
56
components/Modals/Confirm.tsx
Normal file
56
components/Modals/Confirm.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"use client";
|
||||||
|
import { useState, type ReactNode } from "react";
|
||||||
|
import Template from "./Template";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useModal } from "@/app/_providers/modal/provider";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
// import { useKeys } from "@/app/_providers/keysProvider";
|
||||||
|
import { useNDK } from "@/app/_providers/ndk";
|
||||||
|
import useCurrentUser from "@/lib/hooks/useCurrentUser";
|
||||||
|
import { HiOutlineLightningBolt } from "react-icons/hi";
|
||||||
|
import { RiSubtractFill, RiAddFill } from "react-icons/ri";
|
||||||
|
import { formatCount } from "@/lib/utils";
|
||||||
|
|
||||||
|
type ConfirmModalProps = {
|
||||||
|
title?: string;
|
||||||
|
children: ReactNode;
|
||||||
|
ctaBody?: ReactNode;
|
||||||
|
onConfirm: () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ConfirmModal({
|
||||||
|
title = "Confirm",
|
||||||
|
children,
|
||||||
|
ctaBody = "Confirm",
|
||||||
|
onConfirm,
|
||||||
|
}: ConfirmModalProps) {
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const modal = useModal();
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
await onConfirm();
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Error", err);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
modal?.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template title={title} className="md:max-w-[400px]">
|
||||||
|
<div className="flex flex-col gap-y-5">
|
||||||
|
<div className="pb-2">{children}</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => void handleSubmit()}
|
||||||
|
loading={isLoading}
|
||||||
|
className="w-full gap-x-1"
|
||||||
|
>
|
||||||
|
{ctaBody}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Template>
|
||||||
|
);
|
||||||
|
}
|
@ -114,6 +114,7 @@ export default function FormModal<TSchema extends FieldValues>({
|
|||||||
if (!condition) {
|
if (!condition) {
|
||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
|
key={slug}
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={slug as Path<TSchema>}
|
name={slug as Path<TSchema>}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
@ -187,6 +188,7 @@ export default function FormModal<TSchema extends FieldValues>({
|
|||||||
if (!state) return;
|
if (!state) return;
|
||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
|
key={slug}
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={slug as Path<TSchema>}
|
name={slug as Path<TSchema>}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
@ -103,10 +103,11 @@ export async function updateListUsersFromZaps(
|
|||||||
event: NostrEvent,
|
event: NostrEvent,
|
||||||
) {
|
) {
|
||||||
const SECONDS_IN_MONTH = 2_628_000;
|
const SECONDS_IN_MONTH = 2_628_000;
|
||||||
|
const SECONDS_IN_YEAR = SECONDS_IN_MONTH * 365;
|
||||||
const paymentEvents = await ndk.fetchEvents({
|
const paymentEvents = await ndk.fetchEvents({
|
||||||
kinds: [9735],
|
kinds: [9735],
|
||||||
["#a"]: [tagId],
|
["#a"]: [tagId],
|
||||||
since: unixTimeNowInSeconds() - SECONDS_IN_MONTH,
|
since: unixTimeNowInSeconds() - SECONDS_IN_YEAR,
|
||||||
});
|
});
|
||||||
const paymentInvoices = Array.from(paymentEvents).map((paymentEvent) =>
|
const paymentInvoices = Array.from(paymentEvents).map((paymentEvent) =>
|
||||||
zapInvoiceFromEvent(paymentEvent),
|
zapInvoiceFromEvent(paymentEvent),
|
||||||
@ -137,7 +138,7 @@ export async function updateListUsersFromZaps(
|
|||||||
paymentInvoice.zappee,
|
paymentInvoice.zappee,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
(unixTimeNowInSeconds() + SECONDS_IN_MONTH).toString(),
|
(unixTimeNowInSeconds() + SECONDS_IN_YEAR).toString(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,14 +153,14 @@ export async function updateListUsersFromZaps(
|
|||||||
event.pubkey,
|
event.pubkey,
|
||||||
"",
|
"",
|
||||||
"self",
|
"self",
|
||||||
(unixTimeNowInSeconds() + SECONDS_IN_MONTH).toString(),
|
(unixTimeNowInSeconds() + SECONDS_IN_YEAR).toString(),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
validUsers.push([
|
validUsers.push([
|
||||||
event.pubkey,
|
event.pubkey,
|
||||||
"",
|
"",
|
||||||
"self",
|
"self",
|
||||||
(unixTimeNowInSeconds() + SECONDS_IN_MONTH).toString(),
|
(unixTimeNowInSeconds() + SECONDS_IN_YEAR).toString(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
console.log("Valid users", validUsers);
|
console.log("Valid users", validUsers);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user