mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-09 06:57:07 +02:00
feat: add invoice description fallback for wallet transactions
Add fallback to lightning invoice description when transaction description is not available. Improves transaction list readability by showing invoice descriptions instead of generic "Payment" labels. - Add getInvoiceDescription helper with applesauce caching pattern - Update TransactionLabel to check invoice description - Update detail dialog to show invoice description as fallback - Maintains zap detection logic and UI Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,7 +52,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import ConnectWalletDialog from "./ConnectWalletDialog";
|
||||
import { RelayLink } from "@/components/nostr/RelayLink";
|
||||
import { parseZapRequest } from "@/lib/wallet-utils";
|
||||
import { parseZapRequest, getInvoiceDescription } from "@/lib/wallet-utils";
|
||||
import { Zap } from "lucide-react";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { KindRenderer } from "./nostr/kinds";
|
||||
@@ -312,14 +312,14 @@ function ZapTransactionDetail({ transaction }: { transaction: Transaction }) {
|
||||
function TransactionLabel({ transaction }: { transaction: Transaction }) {
|
||||
const zapInfo = parseZapRequest(transaction);
|
||||
|
||||
// Not a zap - use original description or default label
|
||||
// Not a zap - use original description, invoice description, or default label
|
||||
if (!zapInfo) {
|
||||
return (
|
||||
<span className="text-sm truncate">
|
||||
{transaction.description ||
|
||||
(transaction.type === "incoming" ? "Received" : "Payment")}
|
||||
</span>
|
||||
);
|
||||
const description =
|
||||
transaction.description ||
|
||||
getInvoiceDescription(transaction) ||
|
||||
(transaction.type === "incoming" ? "Received" : "Payment");
|
||||
|
||||
return <span className="text-sm truncate">{description}</span>;
|
||||
}
|
||||
|
||||
// It's a zap! Show username + message on one line
|
||||
@@ -1260,17 +1260,24 @@ export default function WalletViewer() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
{selectedTransaction.description &&
|
||||
!parseZapRequest(selectedTransaction) && (
|
||||
<div>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
Description
|
||||
</Label>
|
||||
<p className="text-sm">
|
||||
{selectedTransaction.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{(() => {
|
||||
const description =
|
||||
selectedTransaction.description ||
|
||||
getInvoiceDescription(selectedTransaction);
|
||||
const isZap = parseZapRequest(selectedTransaction);
|
||||
|
||||
return (
|
||||
description &&
|
||||
!isZap && (
|
||||
<div>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
Description
|
||||
</Label>
|
||||
<p className="text-sm">{description}</p>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
})()}
|
||||
|
||||
<div>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
|
||||
@@ -119,3 +119,37 @@ export function parseZapRequest(transaction: {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// Symbol for caching invoice description on transaction objects
|
||||
const InvoiceDescriptionSymbol = Symbol("invoiceDescription");
|
||||
|
||||
/**
|
||||
* Extract the description from a BOLT11 invoice
|
||||
* Results are cached on the transaction object using applesauce pattern
|
||||
*
|
||||
* @param transaction - The transaction object with invoice field
|
||||
* @returns The invoice description string, or undefined if not available
|
||||
*/
|
||||
export function getInvoiceDescription(transaction: {
|
||||
invoice?: string;
|
||||
}): string | undefined {
|
||||
// Use applesauce caching pattern - cache result on transaction object
|
||||
return getOrComputeCachedValue(transaction, InvoiceDescriptionSymbol, () => {
|
||||
if (!transaction.invoice) return undefined;
|
||||
|
||||
try {
|
||||
const decoded = decodeBolt11(transaction.invoice);
|
||||
const descSection = decoded.sections.find(
|
||||
(s) => s.name === "description",
|
||||
);
|
||||
|
||||
if (descSection && "value" in descSection) {
|
||||
return String(descSection.value);
|
||||
}
|
||||
} catch {
|
||||
// Invoice decoding failed, ignore
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user