Skip to content

Commit

Permalink
Added modal messaging to pay button
Browse files Browse the repository at this point in the history
  • Loading branch information
Replit user committed Apr 17, 2024
1 parent 7f1e94e commit 1fa662f
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 63 deletions.
15 changes: 12 additions & 3 deletions components/invoice-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import {
ClipboardIcon,
EnvelopeIcon,
} from "@heroicons/react/24/outline";
import { CashuMint, CashuWallet, getEncodedToken, Proof } from "@cashu/cashu-ts";
import {
CashuMint,
CashuWallet,
getEncodedToken,
Proof,
} from "@cashu/cashu-ts";
import {
getLocalStorageData,
isUserLoggedIn,
Expand Down Expand Up @@ -231,7 +236,9 @@ export default function InvoiceCard({
}
const mintKeySetResponse = await mint.getKeySets();
const mintKeySetIds = mintKeySetResponse?.keysets;
const filteredProofs = tokens.filter((p: Proof) => mintKeySetIds?.includes(p.id));
const filteredProofs = tokens.filter(
(p: Proof) => mintKeySetIds?.includes(p.id),
);
const tokenToSend = await wallet.send(price, filteredProofs);
const encodedSendToken = getEncodedToken({
token: [
Expand All @@ -245,7 +252,9 @@ export default function InvoiceCard({
// captureInvoicePaidmetric(metricsInvoiceId, productData.id);
// another metric to capture native Cashu payments is needed
const changeProofs = tokenToSend?.returnChange;
const remainingProofs = tokens.filter((p: Proof) => !mintKeySetIds?.includes(p.id));
const remainingProofs = tokens.filter(
(p: Proof) => !mintKeySetIds?.includes(p.id),
);
let proofArray;
if (changeProofs.length >= 1 && changeProofs) {
proofArray = [...remainingProofs, ...changeProofs];
Expand Down
5 changes: 4 additions & 1 deletion components/utility-components/redeem-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ export default function RedeemButton({ token }: { token: string }) {
const changeProofs = response?.change;
const changeAmount =
Array.isArray(changeProofs) && changeProofs.length > 0
? changeProofs.reduce((acc, current: Proof) => acc + current.amount, 0)
? changeProofs.reduce(
(acc, current: Proof) => acc + current.amount,
0,
)
: 0;
if (changeAmount >= 1 && changeProofs) {
setRedemptionChangeAmount(changeAmount);
Expand Down
121 changes: 94 additions & 27 deletions components/wallet/pay-button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useEffect, useContext, useMemo } from "react";
import { useForm, Controller } from "react-hook-form";
import Link from "next/link";
import { BoltIcon } from "@heroicons/react/24/outline";
import { BoltIcon, CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
import {
Button,
Textarea,
Expand All @@ -15,6 +15,7 @@ import { getLocalStorageData } from "../utility/nostr-helper-functions";
import { SHOPSTRBUTTONCLASSNAMES } from "../utility/STATIC-VARIABLES";
import { LightningAddress } from "@getalby/lightning-tools";
import { CashuMint, CashuWallet, Proof } from "@cashu/cashu-ts";
// import { Invoice } from "@getalby/lightning-tools";
import { formatWithCommas } from "../utility-components/display-monetary-info";

const PayButton = () => {
Expand All @@ -25,6 +26,7 @@ const PayButton = () => {
const [wallet, setWallet] = useState<CashuWallet>();
const [proofs, setProofs] = useState([]);

// const [totalAmount, setTotalAmount] = useState(0);
const [feeAmount, setFeeAmount] = useState("");

const { mints, tokens, history } = getLocalStorageData();
Expand Down Expand Up @@ -53,6 +55,23 @@ const PayButton = () => {
await handlePay(invoiceString);
};

const calculateFee = async (invoice) => {
if (invoice && /^lnbc/.test(invoice)) {
const fee = await wallet?.getFee(invoice);
if (fee) {
setFeeAmount(formatWithCommas(fee, "sats"));
// const invoiceValue = new Invoice({ invoice });
// const { satoshi } = invoiceValue;
// const total = satoshi + fee;
// setTotalAmount(total);
} else {
setFeeAmount("");
}
} else {
setFeeAmount("");
}
};

const handlePay = async (invoiceString: string) => {
setIsPaid(false);
setPaymentFailed(false);
Expand Down Expand Up @@ -98,7 +117,6 @@ const PayButton = () => {
setIsPaid(true);
} catch (error) {
console.log(error);
setIsPaid(true);
setPaymentFailed(true);
}
};
Expand Down Expand Up @@ -140,11 +158,7 @@ const PayButton = () => {
control={payControl}
rules={{
required: "A Lightning invoice is required.",
validate: async (value) => {
const fee = await wallet?.getFee(value);
if (fee && fee >= 1) {
setFeeAmount(formatWithCommas(fee, "sats"));
}
validate: (value) => {
return (
/^lnbc/.test(value) ||
"The lightning invoice must start with 'lnbc'."
Expand All @@ -170,34 +184,87 @@ const PayButton = () => {
labelPlacement="inside"
isInvalid={isErrored}
errorMessage={errorMessage}
// controller props
onChange={onChange} // send value to hook form
onChange={async (e) => {
const newValue = e.target.value;
onChange(newValue);
await calculateFee(newValue);
}}
onBlur={onBlur} // notify when input is touched/blur
value={value}
/>
{feeAmount && (
<div className="mt-2 text-right text-light-text dark:text-dark-text">
{feeAmount && feeAmount >= 1 && (
<div className="mt-2 text-left text-light-text dark:text-dark-text">
Estimated Fee: {feeAmount}
</div>
)}
{isPaid && (
{/* {totalAmount && totalAmount >= 1 && (
<div className="mt-2 text-right text-light-text dark:text-dark-text">
Total Amount: {totalAmount} sats
</div>
)} */}
{paymentFailed ? (
<>
{paymentFailed ? (
<div className="mt-2 items-center justify-center">
Invoice payment failed! No routes could be found,
or you don&apos;t have enough funds. Please try
again with a new invoice, or change your mint in
settings.
</div>
) : (
<>
<div className="mt-2 items-center justify-center">
Invoice paid successfully!
</div>
</>
)}
<Modal
backdrop="blur"
isOpen={paymentFailed}
onClose={() => setPaymentFailed(false)}
// className="bg-light-fg dark:bg-dark-fg text-black dark:text-white"
classNames={{
body: "py-6 ",
backdrop: "bg-[#292f46]/50 backdrop-opacity-60",
header: "border-b-[1px] border-[#292f46]",
footer: "border-t-[1px] border-[#292f46]",
closeButton: "hover:bg-black/5 active:bg-white/10",
}}
isDismissable={true}
scrollBehavior={"normal"}
placement={"center"}
size="2xl"
>
<ModalContent>
<ModalBody className="flex flex-col overflow-hidden text-light-text dark:text-dark-text">
<div className="flex items-center justify-center">
<XCircleIcon className="h-6 w-6 text-red-500" />
<div className="ml-2">Invoice payment failed! No routes could be found,
or you don&apos;t have enough funds. Please try
again with a new invoice, or change your mint in
settings.</div>
</div>
</ModalBody>
</ModalContent>
</Modal>
</>
)}
) : null}
{isPaid ? (
<>
<Modal
backdrop="blur"
isOpen={isPaid}
onClose={() => setIsPaid(false)}
// className="bg-light-fg dark:bg-dark-fg text-black dark:text-white"
classNames={{
body: "py-6 ",
backdrop: "bg-[#292f46]/50 backdrop-opacity-60",
header: "border-b-[1px] border-[#292f46]",
footer: "border-t-[1px] border-[#292f46]",
closeButton: "hover:bg-black/5 active:bg-white/10",
}}
isDismissable={true}
scrollBehavior={"normal"}
placement={"center"}
size="2xl"
>
<ModalContent>
<ModalBody className="flex flex-col overflow-hidden text-light-text dark:text-dark-text">
<div className="flex items-center justify-center">
<CheckCircleIcon className="h-6 w-6 text-green-500" />
<div className="ml-2">Token successfully claimed!</div>
</div>
</ModalBody>
</ModalContent>
</Modal>
</>
) : null}
</>
);
}}
Expand Down
7 changes: 6 additions & 1 deletion components/wallet/receive-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import {
} from "@nextui-org/react";
import { SHOPSTRBUTTONCLASSNAMES } from "../utility/STATIC-VARIABLES";
import { getLocalStorageData } from "../utility/nostr-helper-functions";
import { CashuMint, CashuWallet, getDecodedToken, Proof } from "@cashu/cashu-ts";
import {
CashuMint,
CashuWallet,
getDecodedToken,
Proof,
} from "@cashu/cashu-ts";

const ReceiveButton = () => {
const [showReceiveModal, setShowReceiveModal] = useState(false);
Expand Down
16 changes: 12 additions & 4 deletions components/wallet/send-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ClipboardIcon,
CheckIcon,
CheckCircleIcon,
XCircleIcon,
} from "@heroicons/react/24/outline";
import {
Card,
Expand All @@ -23,7 +22,12 @@ import {
} from "@nextui-org/react";
import { SHOPSTRBUTTONCLASSNAMES } from "../utility/STATIC-VARIABLES";
import { getLocalStorageData } from "../utility/nostr-helper-functions";
import { CashuMint, CashuWallet, getEncodedToken, Proof } from "@cashu/cashu-ts";
import {
CashuMint,
CashuWallet,
getEncodedToken,
Proof,
} from "@cashu/cashu-ts";

const SendButton = () => {
const [showSendModal, setShowSendModal] = useState(false);
Expand Down Expand Up @@ -61,7 +65,9 @@ const SendButton = () => {
const wallet = new CashuWallet(mint);
const mintKeySetResponse = await mint.getKeySets();
const mintKeySetIds = mintKeySetResponse?.keysets;
const filteredProofs = tokens.filter((p: Proof) => mintKeySetIds?.includes(p.id));
const filteredProofs = tokens.filter(
(p: Proof) => mintKeySetIds?.includes(p.id),
);
const tokenToSend = await wallet.send(numSats, filteredProofs);
const encodedSendToken = getEncodedToken({
token: [
Expand All @@ -73,7 +79,9 @@ const SendButton = () => {
});
setNewToken(encodedSendToken);
const changeProofs = tokenToSend?.returnChange;
const remainingProofs = tokens.filter((p: Proof) => !mintKeySetIds?.includes(p.id));
const remainingProofs = tokens.filter(
(p: Proof) => !mintKeySetIds?.includes(p.id),
);
let proofArray;
if (changeProofs.length >= 1 && changeProofs) {
proofArray = [...remainingProofs, ...changeProofs];
Expand Down
19 changes: 15 additions & 4 deletions components/wallet/transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,21 @@ const Transactions = () => {
const [history, setHistory] = useState([]);

useEffect(() => {
const localData = getLocalStorageData();
if (localData && localData.history) {
setHistory(localData.history);
}
// Function to fetch and update transactions
const fetchAndUpdateTransactions = () => {
const localData = getLocalStorageData();
if (localData && localData.history) {
setHistory(localData.history);
}
};
// Initial fetch
fetchAndUpdateTransactions();
// Set up polling with setInterval
const interval = setInterval(() => {
fetchAndUpdateTransactions();
}, 5000); // Polling every 5000 milliseconds (5 seconds)
// Clean up on component unmount
return () => clearInterval(interval);
}, []);

const formatDate = (timestamp: number) => {
Expand Down
61 changes: 38 additions & 23 deletions pages/wallet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,44 @@ const Wallet = () => {
const [walletBalance, setWalletBalance] = useState(0);
const [mint, setMint] = useState("");

const { mints, tokens } = getLocalStorageData();

useEffect(() => {
let tokensTotal =
tokens && tokens.length >= 1
? tokens.reduce((acc, token: Proof) => acc + token.amount, 0)
: 0;
setTotalBalance(tokensTotal);
}, [tokens]);

useEffect(() => {
const getWalleteBalance = async () => {
const currentMint = new CashuMint(mints[0]);
setMint(mints[0]);
const mintKeySetResponse = await currentMint.getKeySets();
const mintKeySetIds = mintKeySetResponse?.keysets;
const filteredProofs = tokens.filter((p: Proof) => mintKeySetIds?.includes(p.id));
let walletTotal =
filteredProofs && filteredProofs.length >= 1 ? filteredProofs.reduce((acc, p: Proof) => acc + p.amount, 0) : 0
setWalletBalance(walletTotal);
}
getWalleteBalance();
}, [mints, tokens]);
// Function to fetch and update balances
const fetchAndUpdateBalances = async () => {
const localData = getLocalStorageData();
if (localData && localData.tokens) {
let tokensTotal =
localData.tokens && localData.tokens.length >= 1
? localData.tokens.reduce(
(acc, token: Proof) => acc + token.amount,
0,
)
: 0;
setTotalBalance(tokensTotal);
}
if (localData && localData.mints && localData.tokens) {
const currentMint = new CashuMint(localData.mints[0]);
setMint(localData.mints[0]);
const mintKeySetResponse = await currentMint.getKeySets();
const mintKeySetIds = mintKeySetResponse?.keysets;
const filteredProofs = localData.tokens.filter(
(p: Proof) => mintKeySetIds?.includes(p.id),
);
let walletTotal =
filteredProofs && filteredProofs.length >= 1
? filteredProofs.reduce((acc, p: Proof) => acc + p.amount, 0)
: 0;
setWalletBalance(walletTotal);
}
};
// Initial fetch
fetchAndUpdateBalances();
// Set up polling with setInterval
const interval = setInterval(() => {
fetchAndUpdateBalances();
}, 5000); // Polling every 5000 milliseconds (5 seconds)
// Clean up on component unmount
return () => clearInterval(interval);
}, []);

return (
<div className="flex max-h-screen flex-col bg-light-bg pb-20 pt-6 dark:bg-dark-bg sm:ml-[120px] md:ml-[250px]">
Expand All @@ -46,7 +61,7 @@ const Wallet = () => {
</p>
</center>
<center>
<p className="mb-2 break-words text-center text-sm text-gray-500 italic">
<p className="mb-2 break-words text-center text-sm italic text-gray-500">
{mint}: {totalBalance} sats
</p>
</center>
Expand Down

0 comments on commit 1fa662f

Please sign in to comment.