"use client"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useFormState } from "react-dom"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { createDonationAction, type DonationState, } from "../app/(marketing)/donate/actions"; import { formatUsdAmount } from "../lib/formatting"; import { useXmrUsdRate } from "../lib/use-xmr-usd-rate"; const donateParam = "donate"; const initialState: DonationState = { error: null, invoiceId: null, }; export default function DonateModal() { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const donationsEnabled = process.env.NEXT_PUBLIC_DONATIONS_ENABLED === "true"; const isOpen = searchParams.get(donateParam) === "1"; const [state, formAction] = useFormState(createDonationAction, initialState); const formRef = useRef(null); const [amountInput, setAmountInput] = useState(""); const { rate: usdRate } = useXmrUsdRate(); const openModal = useCallback(() => { const params = new URLSearchParams(searchParams.toString()); params.set(donateParam, "1"); const query = params.toString(); router.push(query ? `${pathname}?${query}` : pathname); }, [pathname, router, searchParams]); const closeModal = useCallback(() => { const params = new URLSearchParams(searchParams.toString()); params.delete(donateParam); const query = params.toString(); router.replace(query ? `${pathname}?${query}` : pathname); }, [pathname, router, searchParams]); useEffect(() => { if (!state.invoiceId) { return; } closeModal(); router.push(`/donate/${state.invoiceId}`); }, [closeModal, router, state.invoiceId]); useEffect(() => { if (!isOpen) { return; } formRef.current?.reset(); setAmountInput(""); const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { closeModal(); } }; const previousOverflow = document.body.style.overflow; document.body.style.overflow = "hidden"; window.addEventListener("keydown", handleKeyDown); return () => { document.body.style.overflow = previousOverflow; window.removeEventListener("keydown", handleKeyDown); }; }, [closeModal, isOpen]); const amountValue = useMemo(() => { const parsed = Number.parseFloat(amountInput); return Number.isFinite(parsed) ? parsed : null; }, [amountInput]); const usdEstimate = useMemo(() => { if (!usdRate || amountValue === null || amountValue <= 0) { return null; } return formatUsdAmount(usdRate * amountValue); }, [usdRate, amountValue]); if (!donationsEnabled) { return null; } return (
{isOpen ? (
event.stopPropagation()} >

Support xmrcheckout.com

Choose an amount and we will open an invoice page with payment details and status.

setAmountInput(event.target.value)} />
{usdEstimate ? ( <>

Approx. USD reference: ~{usdEstimate}

About this estimate

Reference only, uses CoinGecko spot rate. Not a quote or guarantee.

) : null}
{state.error ? (

{state.error}

) : null}

Payments go directly to the founder wallet. This checkout never moves funds.

) : null}
); }