Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add extra metadata for payments (POC) #247

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion pages/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ export function Transaction() {
return parsedBoostagram;
}, [transaction.metadata]);

// TODO: extract typings
// TODO: review "description" field
const metadata = transaction.metadata as
| {
payer_data?: { name?: string };
recipient_data?: { identifier?: string; description?: string };
comment?: string;
}
| undefined;

return (
<View className="flex-1 flex flex-col gap-3">
<Screen title="Transaction" />
Expand Down Expand Up @@ -130,6 +140,18 @@ export function Transaction() {
)}
</View>
<View className="flex flex-col gap-4 w-full mt-10">
{metadata?.recipient_data?.identifier && (
<TransactionDetailRow
title="To"
content={metadata.recipient_data.identifier}
/>
)}
{metadata?.payer_data?.name && (
<TransactionDetailRow
title="From"
content={metadata.payer_data.name}
/>
)}
<TransactionDetailRow
title="Date & Time"
content={dayjs
Expand All @@ -138,8 +160,19 @@ export function Transaction() {
/>
<TransactionDetailRow
title="Description"
content={transaction.description || "-"}
content={
// TODO: should this description be merged on Alby Hub side?
transaction.description ||
metadata?.recipient_data?.description ||
"-"
}
/>
{metadata?.comment && (
<TransactionDetailRow
title="Comment"
content={metadata.comment}
/>
)}

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

Expand Down Expand Up @@ -170,6 +203,13 @@ export function Transaction() {
copy
/>
)}
{transaction.metadata && (
<TransactionDetailRow
title="Metadata"
content={JSON.stringify(transaction.metadata, null, 2)}
copy
/>
)}
</View>
</View>
</ScrollView>
Expand Down
166 changes: 95 additions & 71 deletions pages/Transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,83 +110,107 @@ export function Transactions() {
setPage(page + 1);
}
}}
renderItem={({ item: transaction }) => (
<Pressable
key={transaction.payment_hash}
onPress={() =>
router.navigate({
pathname: "/transaction",
params: { transactionJSON: JSON.stringify(transaction) },
})
}
>
<View
className={cn(
"flex flex-row items-center gap-3 px-6 py-2 my-2",
transaction.state === "pending" && "animate-pulse",
)}
renderItem={({ item: transaction }) => {
// TODO: extract typings
// TODO: review "description" field
const metadata = transaction.metadata as
| {
recipient_data?: {
identifier?: string;
description?: string;
};
}
| undefined;

return (
<Pressable
key={transaction.payment_hash}
onPress={() =>
router.navigate({
pathname: "/transaction",
params: { transactionJSON: JSON.stringify(transaction) },
})
}
>
<View className="w-10 h-10 bg-muted rounded-full flex flex-col items-center justify-center">
{!(
transaction.state === "failed" ||
transaction.state === "pending"
) && (
<>
{transaction.type === "incoming" && (
<ReceivedTransactionIcon />
)}
{transaction.type === "outgoing" && (
<SentTransactionIcon />
)}
</>
)}
{transaction.state === "pending" && (
<PendingTransactionIcon />
<View
className={cn(
"flex flex-row items-center gap-3 px-6 py-2 my-2",
transaction.state === "pending" && "animate-pulse",
)}
{transaction.state === "failed" && <FailedTransactionIcon />}
</View>
<View className="flex flex-col flex-1">
<View className="flex flex-row flex-1 items-center gap-2">
<Text numberOfLines={1} className="font-medium2 text-lg">
{transaction.type === "incoming"
? "Received"
: transaction.state === "failed"
? "Failed"
: transaction.state === "pending"
? "Sending"
: "Sent"}
>
<View className="w-10 h-10 bg-muted rounded-full flex flex-col items-center justify-center">
{!(
transaction.state === "failed" ||
transaction.state === "pending"
) && (
<>
{transaction.type === "incoming" && (
<ReceivedTransactionIcon />
)}
{transaction.type === "outgoing" && (
<SentTransactionIcon />
)}
</>
)}
{transaction.state === "pending" && (
<PendingTransactionIcon />
)}
{transaction.state === "failed" && (
<FailedTransactionIcon />
)}
</View>
<View className="flex flex-col flex-1">
<View className="flex flex-row flex-1 items-center gap-2">
<Text numberOfLines={1} className="font-medium2 text-lg">
{transaction.type === "incoming"
? "Received"
: 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 ||
metadata?.recipient_data?.description) && (
<Text numberOfLines={1}>
{transaction.description ||
metadata?.recipient_data?.description}
</Text>
)}
</View>
<View>
<Text
className={cn(
"text-right font-medium2 text-lg",
transaction.type === "incoming"
? "text-receive"
: "text-foreground",
)}
>
{transaction.type === "incoming" ? "+" : "-"}{" "}
{Math.floor(transaction.amount / 1000)}
<Text className="text-muted-foreground text-lg">
{" "}
sats
</Text>
</Text>
<Text className="text-muted-foreground text-sm">
{dayjs
.unix(transaction.settled_at || transaction.created_at)
.fromNow()}
<Text className="text-right text-sm text-muted-foreground font-medium2">
{getFiatAmount &&
getFiatAmount(Math.floor(transaction.amount / 1000))}
</Text>
</View>
{transaction.description && (
<Text numberOfLines={1}>{transaction.description}</Text>
)}
</View>
<View>
<Text
className={cn(
"text-right font-medium2 text-lg",
transaction.type === "incoming"
? "text-receive"
: "text-foreground",
)}
>
{transaction.type === "incoming" ? "+" : "-"}{" "}
{Math.floor(transaction.amount / 1000)}
<Text className="text-muted-foreground text-lg"> sats</Text>
</Text>
<Text className="text-right text-sm text-muted-foreground font-medium2">
{getFiatAmount &&
getFiatAmount(Math.floor(transaction.amount / 1000))}
</Text>
</View>
</View>
</Pressable>
)}
</Pressable>
);
}}
/>
) : !transactionsLoaded ? (
<ScrollView className="mt-3">
Expand Down
32 changes: 24 additions & 8 deletions pages/send/ConfirmPayment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,23 @@ import { useAppStore } from "~/lib/state/appStore";

export function ConfirmPayment() {
const { data: transactions } = useTransactions();
const { invoice, originalText, comment, successAction, amount } =
useLocalSearchParams() as {
invoice: string;
originalText: string;
comment: string;
successAction: string;
amount?: string;
};
const {
invoice,
originalText,
comment,
successAction,
amount,
recipientDescription,
recipientIdentifier,
} = useLocalSearchParams() as {
invoice: string;
originalText: string;
comment: string;
successAction: string;
amount?: string;
recipientDescription?: string;
recipientIdentifier?: string;
};
const getFiatAmount = useGetFiatAmount();
const [isLoading, setLoading] = React.useState(false);
const wallets = useAppStore((store) => store.wallets);
Expand All @@ -45,6 +54,13 @@ export function ConfirmPayment() {
const response = await nwcClient.payInvoice({
invoice,
amount: amount ? amountToPaySats * 1000 : undefined,
metadata: {
recipient_data: {
identifier: recipientIdentifier,
description: recipientDescription,
},
comment,
},
});

console.info("payInvoice Response", response);
Expand Down
20 changes: 20 additions & 0 deletions pages/send/LNURLPay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ export function LNURLPay() {
if (comment) {
callback.searchParams.append("comment", comment);
}
let recipientDescription = "";
try {
recipientDescription =
(
JSON.parse(decodeURIComponent(lnurlDetails.metadata)) as string[][]
).find((t) => t[0] === "text/plain")?.[1] || "";
} catch (error) {
console.error("failed to parse recipient description", error);
}
let recipientIdentifier = "";
try {
recipientIdentifier =
(
JSON.parse(decodeURIComponent(lnurlDetails.metadata)) as string[][]
).find((t) => t[0] === "text/identifier")?.[1] || "";
} catch (error) {
console.error("failed to parse recipient identifier", error);
}
//callback.searchParams.append("payerdata", JSON.stringify({ test: 1 }));
const lnurlPayInfo = await lnurl.getPayRequest(callback.toString());
//console.log("Got pay request", lnurlPayInfo.pr);
Expand All @@ -60,6 +78,8 @@ export function LNURLPay() {
params: {
invoice: lnurlPayInfo.pr,
originalText,
recipientDescription,
recipientIdentifier,
comment,
successAction: lnurlPayInfo.successAction
? JSON.stringify(lnurlPayInfo.successAction)
Expand Down
Loading