Skip to content

Commit

Permalink
feat: show failed and pending transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Dec 6, 2024
1 parent 7505837 commit 5859f81
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 48 deletions.
22 changes: 17 additions & 5 deletions hooks/useTransactions.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand Down
88 changes: 62 additions & 26 deletions pages/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(() => {
Expand All @@ -64,18 +67,43 @@ export function Transaction() {
<ScrollView className="p-6">
<View className="flex flex-col gap-5 justify-center items-center mb-12">
<View
className="my-8 bg-muted rounded-full p-8"
className={cn(
"my-8 bg-muted rounded-full p-8",
transaction.state === "pending" && "animate-pulse",
)}
style={{ elevation: 2 }}
>
{transaction.type === "incoming" && (
<MoveDownLeft className="text-receive" width={100} height={100} />
{transaction.state !== "failed" && (
<>
{transaction.type === "incoming" && (
<MoveDownLeft
className="text-receive"
width={100}
height={100}
/>
)}
{transaction.type === "outgoing" && (
<MoveUpRight className="text-send" width={100} height={100} />
)}
</>
)}
{transaction.type === "outgoing" && (
<MoveUpRight className="text-send" width={100} height={100} />
{transaction.state === "failed" && (
<X className="text-destructive" width={100} height={100} />
)}
</View>
<Text className="text-3xl font-bold2 text-foreground">
{transaction.type === "incoming" ? "Received" : "Sent"}
<Text
className={cn(
"text-3xl font-bold2 text-foreground",
transaction.state === "pending" && "animate-pulse",
)}
>
{transaction.type === "incoming"
? "Received"
: transaction.state === "failed"
? "Failed"
: transaction.state === "pending"
? "Sending"
: "Sent"}
</Text>
<View className="flex flex-col items-center justify-center gap-2">
<View className="flex flex-row items-end mt-5">
Expand Down Expand Up @@ -104,7 +132,9 @@ export function Transaction() {
<View className="flex flex-col gap-2 w-full mt-8">
<TransactionDetailRow
title="Date & Time"
content={dayjs.unix(transaction.settled_at).fromNow()}
content={dayjs
.unix(transaction.settled_at || transaction.created_at)
.fromNow()}
/>
<TransactionDetailRow
title="Description"
Expand All @@ -113,27 +143,33 @@ export function Transaction() {

{boostagram && <PodcastingInfo boost={boostagram} />}

{transaction.state === "settled" &&
transaction.type === "outgoing" && (
<TransactionDetailRow
title="Fee"
content={
Math.floor(transaction.fees_paid / 1000).toString() +
" sats (" +
(
(transaction.fees_paid / transaction.amount) *
100
).toFixed(2) +
"%)"
}
/>
)}
<TransactionDetailRow
title="Payment Hash"
content={transaction.payment_hash}
copy
/>
<TransactionDetailRow
title="Preimage"
content={transaction.preimage}
copy
/>
<TransactionDetailRow
title="Fee"
content={
Math.floor(transaction.fees_paid / 1000).toString() +
" sats (" +
((transaction.fees_paid / transaction.amount) * 100).toFixed(
2,
) +
"%)"
}
/>
{transaction.state === "settled" && (
<TransactionDetailRow
title="Preimage"
content={transaction.preimage}
copy
/>
)}
</View>
</View>
</ScrollView>
Expand Down
55 changes: 40 additions & 15 deletions pages/Transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -116,26 +120,47 @@ export function Transactions() {
})
}
>
<View className="flex flex-row items-center gap-3 px-4 py-3">
<View
className={cn(
"flex flex-row items-center gap-3 px-4 py-3",
transaction.state === "pending" && "animate-pulse",
)}
>
<View className="w-10 h-10 bg-muted rounded-full flex flex-col items-center justify-center">
{transaction.type === "incoming" && (
<MoveDownLeft className="text-receive" size={20} />
{transaction.state !== "failed" && (
<>
{transaction.type === "incoming" && (
<MoveDownLeft className="text-receive" size={20} />
)}
{transaction.type === "outgoing" && (
<MoveUpRight className="text-send" size={20} />
)}
</>
)}
{transaction.type === "outgoing" && (
<MoveUpRight className="text-send" size={20} />
{transaction.state === "failed" && (
<X className="text-destructive" size={20} />
)}
</View>
<View className="flex flex-col flex-1">
<Text numberOfLines={1} className="font-medium2">
{transaction.description
? transaction.description
: transaction.type === "incoming"
<View className="flex flex-row flex-1 items-center gap-2">
<Text numberOfLines={1} className="font-medium2">
{transaction.type === "incoming"
? "Received"
: "Sent"}
</Text>
<Text className="text-muted-foreground text-sm">
{dayjs.unix(transaction.settled_at).fromNow()}
</Text>
: transaction.state === "failed"
? "Failed"
: transaction.state === "pending"
? "Sending"
: "Sent"}
</Text>
<Text className="text-muted-foreground text-sm">
{dayjs
.unix(transaction.settled_at || transaction.created_at)
.fromNow()}
</Text>
</View>
{transaction.description && (
<Text numberOfLines={1}>{transaction.description}</Text>
)}
</View>
<View>
<Text
Expand Down
32 changes: 30 additions & 2 deletions pages/send/ConfirmPayment.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { Invoice } from "@getalby/lightning-tools";

import { router, useLocalSearchParams } from "expo-router";
import { Link, router, useLocalSearchParams } from "expo-router";
import React from "react";
import { View } from "react-native";
import { ZapIcon } from "~/components/Icons";
import { TriangleAlert, ZapIcon } from "~/components/Icons";
import Loading from "~/components/Loading";
import { Receiver } from "~/components/Receiver";
import Screen from "~/components/Screen";
import { Button } from "~/components/ui/button";
import {
Card,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Text } from "~/components/ui/text";
import { useGetFiatAmount } from "~/hooks/useGetFiatAmount";
import { useTransactions } from "~/hooks/useTransactions";
import { ALBY_LIGHTNING_ADDRESS } from "~/lib/constants";
import { errorToast } from "~/lib/errorToast";
import { useAppStore } from "~/lib/state/appStore";

export function ConfirmPayment() {
const { data: transactions } = useTransactions();
const { invoice, originalText, comment, successAction } =
useLocalSearchParams() as {
invoice: string;
Expand Down Expand Up @@ -108,6 +116,26 @@ export function ConfirmPayment() {
<Receiver originalText={originalText} invoice={invoice} />
</View>
<View className="p-6">
{!transactions?.transactions.some(
(transaction) => transaction.state === "pending",
) && (
<Link href="/transactions" className="mb-6">
<Card>
<CardHeader>
<CardTitle>
<View className="flex flex-row gap-3 items-center animate-pulse">
<TriangleAlert size={16} className="text-foreground" />
<Text>One or more pending payments</Text>
</View>
</CardTitle>
<CardDescription>
Please check your transaction list before paying to ensure you
do not make a payment twice.
</CardDescription>
</CardHeader>
</Card>
</Link>
)}
<Button
size="lg"
onPress={pay}
Expand Down

0 comments on commit 5859f81

Please sign in to comment.