import "./send-zap.css" import { Fragment, type ReactNode, useEffect, useState } from "react" import { LNURL } from "@snort/shared" import { EventPublisher, type NostrEvent, PrivateKeySigner, type TaggedNostrEvent } from "@snort/system" import { FormattedMessage, FormattedNumber } from "react-intl" import { formatSats } from "../number" import { Icon } from "./icon" import QrCode from "./qr-code" import { useLogin } from "@/hooks/login" import Copy from "./copy" import { defaultRelays } from "@/const" import { useRates } from "@/hooks/rates" import { DefaultButton, PrimaryButton } from "./buttons" import Modal from "./modal" import Pill from "./pill" import { useUserProfile } from "@snort/system-react" import { getHost } from "@/utils" import { getName } from "./profile" import { useWallet } from "@/hooks/wallet" 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 onTargetReady?: () => void button?: ReactNode } export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish, onTargetReady }: SendZapsProps) { const satsAmounts = [ 21, 69, 121, 420, 1_000, 2_100, 4_200, 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 [customAmount, setCustomAmount] = useState(false) const [amount, setAmount] = useState(satsAmounts[0]) const [comment, setComment] = useState("") const [invoice, setInvoice] = useState("") const login = useLogin() const wallet = useWallet() const rate = useRates("BTCUSD") 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) } const usdRate = rate.time ? rate.ask : 26_000 useEffect(() => { if (!svc) { if (typeof lnurl === "string") { loadService(lnurl) .then(() => { onTargetReady?.() }) .catch(console.warn) } else { setSvc(lnurl) onTargetReady?.() } } }, [lnurl]) async function send() { if (!svc) return let pub = login?.publisher() let isAnon = false if (!pub) { const signer = PrivateKeySigner.random() pub = new EventPublisher(signer, signer.getPubKey()) 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 (wallet) { try { await wallet.payInvoice(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 && ( <>   , }} /> )}
{!customAmount && (isFiat ? usdAmounts : satsAmounts).map(a => ( setAmount(a)}> {isFiat ? `$${a.toLocaleString()}` : formatSats(a)} ))} setCustomAmount(s => !s)} selected={customAmount}>
{customAmount && setAmount(e.target.valueAsNumber)} />}
{svc && (svc.maxCommentLength > 0 || svc.canZap) && (