From 5859f8157ab4ab5a6f9cdedb1dd94c71f0af44ef Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 6 Dec 2024 18:12:58 +0700 Subject: [PATCH] feat: show failed and pending transactions --- hooks/useTransactions.ts | 22 +++++++-- pages/Transaction.tsx | 88 ++++++++++++++++++++++++----------- pages/Transactions.tsx | 55 ++++++++++++++++------ pages/send/ConfirmPayment.tsx | 32 ++++++++++++- 4 files changed, 149 insertions(+), 48 deletions(-) diff --git a/hooks/useTransactions.ts b/hooks/useTransactions.ts index 993d17e..2fa3705 100644 --- a/hooks/useTransactions.ts +++ b/hooks/useTransactions.ts @@ -1,3 +1,7 @@ +import { + Nip47ListTransactionsRequest, + Nip47Transaction, +} from "@getalby/sdk/dist/NWCClient"; import { useAppStore } from "lib/state/appStore"; import useSWR from "swr"; import { TRANSACTIONS_PAGE_SIZE } from "~/lib/constants"; @@ -15,11 +19,19 @@ const fetcher = async (...args: FetchArgs) => { const page = +(transactionsUrl.searchParams.get("page") as string); try { - const transactions = await nwcClient.listTransactions({ - limit: TRANSACTIONS_PAGE_SIZE, - offset: (page - 1) * TRANSACTIONS_PAGE_SIZE, - }); - return transactions; + const transactions = await nwcClient.listTransactions( + { + limit: TRANSACTIONS_PAGE_SIZE, + offset: (page - 1) * TRANSACTIONS_PAGE_SIZE, + unpaid_outgoing: true, + } as Nip47ListTransactionsRequest /* TODO: remove cast once unpaid_outgoing or similar is part of spec/js-sdk */, + ); + return transactions as { + // TODO: undo when JS SDK includes state property + transactions: (Nip47Transaction & { + state: "settled" | "pending" | "failed"; + })[]; + }; } catch (error) { errorToast(error); throw error; diff --git a/pages/Transaction.tsx b/pages/Transaction.tsx index 845f130..ec9ddb0 100644 --- a/pages/Transaction.tsx +++ b/pages/Transaction.tsx @@ -7,7 +7,7 @@ import { useLocalSearchParams } from "expo-router"; import React from "react"; import { ScrollView, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; -import { MoveDownLeft, MoveUpRight } from "~/components/Icons"; +import { MoveDownLeft, MoveUpRight, X } from "~/components/Icons"; import Screen from "~/components/Screen"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; @@ -38,7 +38,10 @@ export function Transaction() { const { transactionJSON } = useLocalSearchParams() as unknown as { transactionJSON: string; }; - const transaction: Nip47Transaction = JSON.parse(transactionJSON); + // TODO: undo when JS SDK includes state property + const transaction: Nip47Transaction & { + state: "settled" | "pending" | "failed"; + } = JSON.parse(transactionJSON); const getFiatAmount = useGetFiatAmount(); const boostagram = React.useMemo(() => { @@ -64,18 +67,43 @@ export function Transaction() { - {transaction.type === "incoming" && ( - + {transaction.state !== "failed" && ( + <> + {transaction.type === "incoming" && ( + + )} + {transaction.type === "outgoing" && ( + + )} + )} - {transaction.type === "outgoing" && ( - + {transaction.state === "failed" && ( + )} - - {transaction.type === "incoming" ? "Received" : "Sent"} + + {transaction.type === "incoming" + ? "Received" + : transaction.state === "failed" + ? "Failed" + : transaction.state === "pending" + ? "Sending" + : "Sent"} @@ -104,7 +132,9 @@ export function Transaction() { } + {transaction.state === "settled" && + transaction.type === "outgoing" && ( + + )} - - + {transaction.state === "settled" && ( + + )} diff --git a/pages/Transactions.tsx b/pages/Transactions.tsx index ee16cb9..af13f41 100644 --- a/pages/Transactions.tsx +++ b/pages/Transactions.tsx @@ -26,7 +26,11 @@ export function Transactions() { const [loadingNextPage, setLoadingNextPage] = React.useState(false); const [transactionsLoaded, setTransactionsLoaded] = React.useState(false); const [allTransactions, setAllTransactions] = React.useState< - Nip47Transaction[] + //Nip47Transaction[] + // TODO: undo when JS SDK includes state property + (Nip47Transaction & { + state: "settled" | "pending" | "failed"; + })[] >([]); const [refreshingTransactions, setRefreshingTransactions] = React.useState(false); @@ -116,26 +120,47 @@ export function Transactions() { }) } > - + - {transaction.type === "incoming" && ( - + {transaction.state !== "failed" && ( + <> + {transaction.type === "incoming" && ( + + )} + {transaction.type === "outgoing" && ( + + )} + )} - {transaction.type === "outgoing" && ( - + {transaction.state === "failed" && ( + )} - - {transaction.description - ? transaction.description - : transaction.type === "incoming" + + + {transaction.type === "incoming" ? "Received" - : "Sent"} - - - {dayjs.unix(transaction.settled_at).fromNow()} - + : transaction.state === "failed" + ? "Failed" + : transaction.state === "pending" + ? "Sending" + : "Sent"} + + + {dayjs + .unix(transaction.settled_at || transaction.created_at) + .fromNow()} + + + {transaction.description && ( + {transaction.description} + )} + {!transactions?.transactions.some( + (transaction) => transaction.state === "pending", + ) && ( + + + + + + + One or more pending payments + + + + Please check your transaction list before paying to ensure you + do not make a payment twice. + + + + + )}