import "./send-zap.css"; import * as Dialog from "@radix-ui/react-dialog"; import { useEffect, useState, type ReactNode } from "react"; import { LNURL } from "@snort/shared"; import { NostrEvent, EventPublisher } from "@snort/system"; import { secp256k1 } from "@noble/curves/secp256k1"; import { bytesToHex } from "@noble/curves/abstract/utils"; import { formatSats } from "../number"; import { Icon } from "./icon"; import AsyncButton from "./async-button"; import QrCode from "./qr-code"; import { useLogin } from "hooks/login"; import Copy from "./copy"; import { defaultRelays } from "const"; import { FormattedMessage } from "react-intl"; export interface LNURLLike { get name(): string; get maxCommentLength(): number; get canZap(): boolean; getInvoice(amountInSats: number, comment?: string, zap?: NostrEvent): Promise<{ pr?: string }>; } export interface SendZapsProps { lnurl: string | LNURLLike; pubkey?: string; aTag?: string; eTag?: string; targetName?: string; onFinish: () => void; button?: ReactNode; } export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: SendZapsProps) { const UsdRate = 28_000; const satsAmounts = [ 21, 69, 121, 221, 420, 1_000, 2_100, 5_000, 6_666, 10_000, 21_000, 42_000, 69_000, 100_000, 210_000, 500_000, 1_000_000, ]; const usdAmounts = [0.05, 0.5, 2, 5, 10, 50, 100, 200]; const [isFiat, setIsFiat] = useState(false); const [svc, setSvc] = useState(); const [amount, setAmount] = useState(satsAmounts[0]); const [comment, setComment] = useState(""); const [invoice, setInvoice] = useState(""); const login = useLogin(); const relays = Object.keys(defaultRelays); const name = targetName ?? svc?.name; async function loadService(lnurl: string) { const s = new LNURL(lnurl); await s.load(); setSvc(s); } useEffect(() => { if (!svc) { if (typeof lnurl === "string") { loadService(lnurl).catch(console.warn); } else { setSvc(lnurl); } } }, [lnurl]); async function send() { if (!svc) return; let pub = login?.publisher(); let isAnon = false; if (!pub) { pub = EventPublisher.privateKey(bytesToHex(secp256k1.utils.randomPrivateKey())); isAnon = true; } const amountInSats = isFiat ? Math.floor((amount / UsdRate) * 1e8) : amount; let zap: NostrEvent | undefined; if (pubkey) { zap = await pub.zap(amountInSats * 1000, pubkey, relays, undefined, comment, eb => { if (aTag) { eb.tag(["a", aTag]); } if (eTag) { eb.tag(["e", eTag]); } if (isAnon) { eb.tag(["anon", ""]); } return eb; }); } const invoice = await svc.getInvoice(amountInSats, comment, zap); if (!invoice.pr) return; if (window.webln) { await window.webln.enable(); try { await window.webln.sendPayment(invoice.pr); onFinish(); } catch (error) { setInvoice(invoice.pr); } } else { setInvoice(invoice.pr); } } function input() { if (invoice) return; return ( <>
{ setIsFiat(false); setAmount(satsAmounts[0]); }}> SATS { setIsFiat(true); setAmount(usdAmounts[0]); }}> USD
{(isFiat ? usdAmounts : satsAmounts).map(a => ( setAmount(a)}> {isFiat ? `$${a.toLocaleString()}` : formatSats(a)} ))}
{svc && (svc.maxCommentLength > 0 || svc.canZap) && (