2023-01-02 13:40:42 +00:00
|
|
|
import "./Invoice.css";
|
2023-01-09 22:26:41 +00:00
|
|
|
import { useState } from "react";
|
2023-02-08 21:10:26 +00:00
|
|
|
import { useIntl, FormattedMessage } from "react-intl";
|
2023-02-07 19:47:57 +00:00
|
|
|
// @ts-expect-error No types available
|
2023-01-02 13:40:42 +00:00
|
|
|
import { decode as invoiceDecode } from "light-bolt11-decoder";
|
|
|
|
import { useMemo } from "react";
|
2023-02-07 13:32:32 +00:00
|
|
|
import SendSats from "Element/SendSats";
|
2023-02-01 22:42:46 +00:00
|
|
|
import ZapCircle from "Icons/ZapCircle";
|
2023-01-26 15:57:36 +00:00
|
|
|
import useWebln from "Hooks/useWebln";
|
2023-01-02 13:40:42 +00:00
|
|
|
|
2023-02-08 21:10:26 +00:00
|
|
|
import messages from "./messages";
|
|
|
|
|
2023-01-16 17:48:25 +00:00
|
|
|
export interface InvoiceProps {
|
2023-02-07 20:04:50 +00:00
|
|
|
invoice: string;
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
2023-02-07 19:47:57 +00:00
|
|
|
|
|
|
|
interface Section {
|
|
|
|
name: string;
|
|
|
|
}
|
|
|
|
|
2023-01-16 17:48:25 +00:00
|
|
|
export default function Invoice(props: InvoiceProps) {
|
2023-02-07 20:04:50 +00:00
|
|
|
const invoice = props.invoice;
|
|
|
|
const webln = useWebln();
|
|
|
|
const [showInvoice, setShowInvoice] = useState(false);
|
2023-02-08 21:10:26 +00:00
|
|
|
const { formatMessage } = useIntl();
|
2023-01-02 13:40:42 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
const info = useMemo(() => {
|
|
|
|
try {
|
2023-02-07 19:47:57 +00:00
|
|
|
const parsed = invoiceDecode(invoice);
|
2023-01-09 22:26:41 +00:00
|
|
|
|
2023-02-07 19:47:57 +00:00
|
|
|
const amount = parseInt(
|
|
|
|
parsed.sections.find((a: Section) => a.name === "amount")?.value
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
2023-02-07 19:47:57 +00:00
|
|
|
const timestamp = parseInt(
|
|
|
|
parsed.sections.find((a: Section) => a.name === "timestamp")?.value
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
2023-02-07 19:47:57 +00:00
|
|
|
const expire = parseInt(
|
|
|
|
parsed.sections.find((a: Section) => a.name === "expiry")?.value
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
2023-02-07 19:47:57 +00:00
|
|
|
const description = parsed.sections.find(
|
|
|
|
(a: Section) => a.name === "description"
|
2023-02-07 20:04:50 +00:00
|
|
|
)?.value;
|
2023-02-07 19:47:57 +00:00
|
|
|
const ret = {
|
2023-02-07 20:04:50 +00:00
|
|
|
amount: !isNaN(amount) ? amount / 1000 : 0,
|
|
|
|
expire: !isNaN(timestamp) && !isNaN(expire) ? timestamp + expire : null,
|
|
|
|
description,
|
|
|
|
expired: false,
|
|
|
|
};
|
|
|
|
if (ret.expire) {
|
|
|
|
ret.expired = ret.expire < new Date().getTime() / 1000;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
}, [invoice]);
|
2023-01-02 13:40:42 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
const [isPaid, setIsPaid] = useState(false);
|
|
|
|
const isExpired = info?.expired;
|
|
|
|
const amount = info?.amount ?? 0;
|
|
|
|
const description = info?.description;
|
2023-02-01 22:42:46 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
function header() {
|
|
|
|
return (
|
|
|
|
<>
|
2023-02-08 21:10:26 +00:00
|
|
|
<h4>
|
|
|
|
<FormattedMessage {...messages.Invoice} />
|
|
|
|
</h4>
|
2023-02-07 20:04:50 +00:00
|
|
|
<ZapCircle className="zap-circle" />
|
|
|
|
<SendSats
|
2023-02-08 21:10:26 +00:00
|
|
|
title={formatMessage(messages.PayInvoice)}
|
2023-02-07 20:04:50 +00:00
|
|
|
invoice={invoice}
|
|
|
|
show={showInvoice}
|
|
|
|
onClose={() => setShowInvoice(false)}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
2023-01-06 19:57:26 +00:00
|
|
|
|
2023-02-07 19:47:57 +00:00
|
|
|
async function payInvoice(e: React.MouseEvent<HTMLButtonElement>) {
|
2023-02-07 20:04:50 +00:00
|
|
|
e.stopPropagation();
|
|
|
|
if (webln?.enabled) {
|
|
|
|
try {
|
|
|
|
await webln.sendPayment(invoice);
|
|
|
|
setIsPaid(true);
|
|
|
|
} catch (error) {
|
2023-01-26 15:57:36 +00:00
|
|
|
setShowInvoice(true);
|
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
} else {
|
|
|
|
setShowInvoice(true);
|
2023-01-26 15:57:36 +00:00
|
|
|
}
|
2023-02-07 20:04:50 +00:00
|
|
|
}
|
2023-01-26 15:57:36 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div
|
|
|
|
className={`note-invoice flex ${isExpired ? "expired" : ""} ${
|
|
|
|
isPaid ? "paid" : ""
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<div className="invoice-header">{header()}</div>
|
2023-01-06 19:57:26 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
<p className="invoice-amount">
|
|
|
|
{amount > 0 && (
|
|
|
|
<>
|
|
|
|
{amount.toLocaleString()}{" "}
|
|
|
|
<span className="sats">sat{amount === 1 ? "" : "s"}</span>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</p>
|
2023-01-12 06:07:48 +00:00
|
|
|
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className="invoice-body">
|
|
|
|
{description && <p>{description}</p>}
|
|
|
|
{isPaid ? (
|
2023-02-08 21:10:26 +00:00
|
|
|
<div className="paid">
|
|
|
|
<FormattedMessage {...messages.Paid} />
|
|
|
|
</div>
|
2023-02-07 20:04:50 +00:00
|
|
|
) : (
|
|
|
|
<button disabled={isExpired} type="button" onClick={payInvoice}>
|
2023-02-08 21:10:26 +00:00
|
|
|
{isExpired ? (
|
|
|
|
<FormattedMessage {...messages.Expired} />
|
|
|
|
) : (
|
|
|
|
<FormattedMessage {...messages.Pay} />
|
|
|
|
)}
|
2023-02-07 20:04:50 +00:00
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
);
|
2023-02-01 22:42:46 +00:00
|
|
|
}
|