mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-06 18:51:21 +02:00
fix: improve Cashu wallet transaction display and relays
- Subscribe to WalletHistory.meta$ observable for actual amounts and direction - Create HistoryEntryRow component that properly shows +/- amounts - Show effective relays (wallet + user outbox) instead of just wallet relays - Remove settings dropdown (feature deferred) - Add colored amount display (+green for in, -red for out) - Show mint in transaction detail dialog when available
This commit is contained in:
@@ -13,7 +13,6 @@ import {
|
|||||||
Send,
|
Send,
|
||||||
Download,
|
Download,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Settings,
|
|
||||||
Coins,
|
Coins,
|
||||||
Loader2,
|
Loader2,
|
||||||
Wallet,
|
Wallet,
|
||||||
@@ -21,11 +20,13 @@ import {
|
|||||||
ChevronRight,
|
ChevronRight,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ArrowDownLeft,
|
ArrowDownLeft,
|
||||||
|
ArrowUpRight,
|
||||||
Search,
|
Search,
|
||||||
Landmark,
|
Landmark,
|
||||||
Radio,
|
Radio,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { use$ } from "applesauce-react/hooks";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -46,12 +47,10 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import {
|
import {
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
WalletHeader,
|
WalletHeader,
|
||||||
WalletHistoryList,
|
WalletHistoryList,
|
||||||
TransactionRow,
|
|
||||||
type HistoryItem,
|
type HistoryItem,
|
||||||
} from "@/components/wallet";
|
} from "@/components/wallet";
|
||||||
import { CodeCopyButton } from "@/components/CodeCopyButton";
|
import { CodeCopyButton } from "@/components/CodeCopyButton";
|
||||||
@@ -206,6 +205,57 @@ function historyToItem(entry: WalletHistory): HistoryItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* History entry row that subscribes to meta$ for amounts
|
||||||
|
*/
|
||||||
|
function HistoryEntryRow({
|
||||||
|
entry,
|
||||||
|
blurred,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
entry: WalletHistory;
|
||||||
|
blurred: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}) {
|
||||||
|
// Subscribe to meta$ observable to get direction and amount
|
||||||
|
const meta = use$(() => entry.meta$, [entry]);
|
||||||
|
|
||||||
|
const direction = meta?.direction || "in";
|
||||||
|
const amount = meta?.amount || 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-between border-b border-border px-4 py-2.5 hover:bg-muted/50 transition-colors flex-shrink-0 cursor-pointer"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3 min-w-0 flex-1">
|
||||||
|
{direction === "in" ? (
|
||||||
|
<ArrowDownLeft className="size-4 text-green-500 flex-shrink-0" />
|
||||||
|
) : (
|
||||||
|
<ArrowUpRight className="size-4 text-red-500 flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<span className="text-sm">
|
||||||
|
{formatTimestamp(entry.event.created_at, "datetime")}
|
||||||
|
</span>
|
||||||
|
{!entry.unlocked && (
|
||||||
|
<span className="text-xs text-muted-foreground ml-2">(locked)</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-shrink-0 ml-4">
|
||||||
|
<p
|
||||||
|
className={`text-sm font-semibold font-mono ${direction === "in" ? "text-green-500" : "text-red-500"}`}
|
||||||
|
>
|
||||||
|
{blurred
|
||||||
|
? "✦✦✦✦"
|
||||||
|
: `${direction === "in" ? "+" : "-"}${amount.toLocaleString()}`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Nip61WalletViewer() {
|
export default function Nip61WalletViewer() {
|
||||||
const { state, toggleWalletBalancesBlur } = useGrimoire();
|
const { state, toggleWalletBalancesBlur } = useGrimoire();
|
||||||
const { isLoggedIn } = useAccount();
|
const { isLoggedIn } = useAccount();
|
||||||
@@ -221,8 +271,6 @@ export default function Nip61WalletViewer() {
|
|||||||
unlock,
|
unlock,
|
||||||
unlocking,
|
unlocking,
|
||||||
error,
|
error,
|
||||||
syncEnabled,
|
|
||||||
toggleSyncEnabled,
|
|
||||||
} = useNip61Wallet();
|
} = useNip61Wallet();
|
||||||
|
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
@@ -231,7 +279,6 @@ export default function Nip61WalletViewer() {
|
|||||||
const [detailDialogOpen, setDetailDialogOpen] = useState(false);
|
const [detailDialogOpen, setDetailDialogOpen] = useState(false);
|
||||||
const [showRawTransaction, setShowRawTransaction] = useState(false);
|
const [showRawTransaction, setShowRawTransaction] = useState(false);
|
||||||
const [copiedRawTx, setCopiedRawTx] = useState(false);
|
const [copiedRawTx, setCopiedRawTx] = useState(false);
|
||||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
||||||
|
|
||||||
const blurred = state.walletBalancesBlurred ?? false;
|
const blurred = state.walletBalancesBlurred ?? false;
|
||||||
|
|
||||||
@@ -265,34 +312,11 @@ export default function Nip61WalletViewer() {
|
|||||||
const renderHistoryEntry = useCallback(
|
const renderHistoryEntry = useCallback(
|
||||||
(item: HistoryItem) => {
|
(item: HistoryItem) => {
|
||||||
const entry = item.data as WalletHistory;
|
const entry = item.data as WalletHistory;
|
||||||
|
|
||||||
// If not unlocked, show placeholder
|
|
||||||
if (!entry.unlocked) {
|
|
||||||
return (
|
|
||||||
<TransactionRow
|
|
||||||
key={entry.id}
|
|
||||||
direction="in"
|
|
||||||
amount={0}
|
|
||||||
blurred={true}
|
|
||||||
label={
|
|
||||||
<span className="text-sm text-muted-foreground">Locked</span>
|
|
||||||
}
|
|
||||||
onClick={() => handleTransactionClick(entry)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TransactionRow
|
<HistoryEntryRow
|
||||||
key={entry.id}
|
key={entry.id}
|
||||||
direction="in" // TODO: Determine from meta$
|
entry={entry}
|
||||||
amount={0} // TODO: Get from meta$
|
|
||||||
blurred={blurred}
|
blurred={blurred}
|
||||||
label={
|
|
||||||
<span className="text-sm">
|
|
||||||
{formatTimestamp(entry.event.created_at, "datetime")}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
onClick={() => handleTransactionClick(entry)}
|
onClick={() => handleTransactionClick(entry)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -424,19 +448,6 @@ export default function Nip61WalletViewer() {
|
|||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={() => setSettingsOpen(true)}
|
|
||||||
className="flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors"
|
|
||||||
aria-label="Settings"
|
|
||||||
>
|
|
||||||
<Settings className="size-3" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Settings</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -498,7 +509,8 @@ export default function Nip61WalletViewer() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Transaction Detail Dialog */}
|
{/* Transaction Detail Dialog */}
|
||||||
<Dialog
|
<TransactionDetailDialog
|
||||||
|
transaction={selectedTransaction}
|
||||||
open={detailDialogOpen}
|
open={detailDialogOpen}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
setDetailDialogOpen(open);
|
setDetailDialogOpen(open);
|
||||||
@@ -507,159 +519,161 @@ export default function Nip61WalletViewer() {
|
|||||||
setCopiedRawTx(false);
|
setCopiedRawTx(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
showRaw={showRawTransaction}
|
||||||
<DialogContent className="max-w-md max-h-[70vh] flex flex-col">
|
onToggleRaw={() => setShowRawTransaction(!showRawTransaction)}
|
||||||
<DialogHeader>
|
copiedRaw={copiedRawTx}
|
||||||
<DialogTitle>Transaction Details</DialogTitle>
|
onCopyRaw={() => {
|
||||||
</DialogHeader>
|
if (selectedTransaction) {
|
||||||
|
navigator.clipboard.writeText(
|
||||||
<div className="overflow-y-auto max-h-[calc(70vh-8rem)] pr-2">
|
JSON.stringify(selectedTransaction.event, null, 2),
|
||||||
{selectedTransaction && (
|
);
|
||||||
<div className="space-y-4">
|
setCopiedRawTx(true);
|
||||||
<div className="flex items-center gap-3">
|
setTimeout(() => setCopiedRawTx(false), 2000);
|
||||||
<ArrowDownLeft className="size-6 text-green-500" />
|
}
|
||||||
<div>
|
}}
|
||||||
<p className="text-lg font-semibold">Transaction</p>
|
blurred={blurred}
|
||||||
<p className="text-sm text-muted-foreground">
|
/>
|
||||||
{formatTimestamp(
|
|
||||||
selectedTransaction.event.created_at,
|
|
||||||
"datetime",
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div>
|
|
||||||
<Label className="text-xs text-muted-foreground">
|
|
||||||
Event ID
|
|
||||||
</Label>
|
|
||||||
<p className="text-xs font-mono break-all bg-muted p-2 rounded">
|
|
||||||
{selectedTransaction.event.id}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label className="text-xs text-muted-foreground">
|
|
||||||
Status
|
|
||||||
</Label>
|
|
||||||
<p className="text-sm">
|
|
||||||
{selectedTransaction.unlocked ? "Unlocked" : "Locked"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label className="text-xs text-muted-foreground">
|
|
||||||
Created At
|
|
||||||
</Label>
|
|
||||||
<p className="text-sm font-mono">
|
|
||||||
{formatTimestamp(
|
|
||||||
selectedTransaction.event.created_at,
|
|
||||||
"absolute",
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Raw Transaction (expandable) */}
|
|
||||||
<div className="border-t border-border pt-4 mt-4">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowRawTransaction(!showRawTransaction)}
|
|
||||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors w-full"
|
|
||||||
>
|
|
||||||
{showRawTransaction ? (
|
|
||||||
<ChevronDown className="size-4" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="size-4" />
|
|
||||||
)}
|
|
||||||
<span>Show Raw Event</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showRawTransaction && (
|
|
||||||
<div className="mt-3 space-y-2">
|
|
||||||
<div className="relative">
|
|
||||||
<pre className="text-xs font-mono bg-muted p-3 rounded overflow-x-auto max-h-60 overflow-y-auto">
|
|
||||||
{JSON.stringify(selectedTransaction.event, null, 2)}
|
|
||||||
</pre>
|
|
||||||
<CodeCopyButton
|
|
||||||
copied={copiedRawTx}
|
|
||||||
onCopy={() => {
|
|
||||||
navigator.clipboard.writeText(
|
|
||||||
JSON.stringify(
|
|
||||||
selectedTransaction.event,
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setCopiedRawTx(true);
|
|
||||||
setTimeout(() => setCopiedRawTx(false), 2000);
|
|
||||||
}}
|
|
||||||
label="Copy event JSON"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
setDetailDialogOpen(false);
|
|
||||||
setShowRawTransaction(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* Settings Dialog */}
|
|
||||||
<Dialog open={settingsOpen} onOpenChange={setSettingsOpen}>
|
|
||||||
<DialogContent className="max-w-sm">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Wallet Settings</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<Label>Keep Wallet Synced</Label>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Keep wallet unlocked and sync history automatically
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
checked={syncEnabled}
|
|
||||||
onCheckedChange={toggleSyncEnabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="space-y-0.5">
|
|
||||||
<Label>Blur Balances</Label>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Hide balance amounts for privacy
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
checked={blurred}
|
|
||||||
onCheckedChange={toggleWalletBalancesBlur}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<Button variant="outline" onClick={() => setSettingsOpen(false)}>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transaction detail dialog component
|
||||||
|
*/
|
||||||
|
function TransactionDetailDialog({
|
||||||
|
transaction,
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
showRaw,
|
||||||
|
onToggleRaw,
|
||||||
|
copiedRaw,
|
||||||
|
onCopyRaw,
|
||||||
|
blurred,
|
||||||
|
}: {
|
||||||
|
transaction: WalletHistory | null;
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
showRaw: boolean;
|
||||||
|
onToggleRaw: () => void;
|
||||||
|
copiedRaw: boolean;
|
||||||
|
onCopyRaw: () => void;
|
||||||
|
blurred: boolean;
|
||||||
|
}) {
|
||||||
|
// Subscribe to meta$ for transaction details
|
||||||
|
const meta = use$(() => transaction?.meta$, [transaction]);
|
||||||
|
|
||||||
|
const direction = meta?.direction || "in";
|
||||||
|
const amount = meta?.amount || 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="max-w-md max-h-[70vh] flex flex-col">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Transaction Details</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="overflow-y-auto max-h-[calc(70vh-8rem)] pr-2">
|
||||||
|
{transaction && (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{direction === "in" ? (
|
||||||
|
<ArrowDownLeft className="size-6 text-green-500" />
|
||||||
|
) : (
|
||||||
|
<ArrowUpRight className="size-6 text-red-500" />
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<p className="text-lg font-semibold">
|
||||||
|
{direction === "in" ? "Received" : "Sent"}
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
className={`text-2xl font-bold font-mono ${direction === "in" ? "text-green-500" : "text-red-500"}`}
|
||||||
|
>
|
||||||
|
{blurred
|
||||||
|
? "✦✦✦✦✦✦"
|
||||||
|
: `${direction === "in" ? "+" : "-"}${amount.toLocaleString()}`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs text-muted-foreground">
|
||||||
|
Event ID
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs font-mono break-all bg-muted p-2 rounded">
|
||||||
|
{transaction.event.id}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{meta?.mint && (
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs text-muted-foreground">
|
||||||
|
Mint
|
||||||
|
</Label>
|
||||||
|
<p className="text-sm font-mono">
|
||||||
|
{new URL(meta.mint).hostname}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs text-muted-foreground">
|
||||||
|
Status
|
||||||
|
</Label>
|
||||||
|
<p className="text-sm">
|
||||||
|
{transaction.unlocked ? "Unlocked" : "Locked"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs text-muted-foreground">
|
||||||
|
Created At
|
||||||
|
</Label>
|
||||||
|
<p className="text-sm font-mono">
|
||||||
|
{formatTimestamp(transaction.event.created_at, "absolute")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Raw Transaction (expandable) */}
|
||||||
|
<div className="border-t border-border pt-4 mt-4">
|
||||||
|
<button
|
||||||
|
onClick={onToggleRaw}
|
||||||
|
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors w-full"
|
||||||
|
>
|
||||||
|
{showRaw ? (
|
||||||
|
<ChevronDown className="size-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="size-4" />
|
||||||
|
)}
|
||||||
|
<span>Show Raw Event</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showRaw && (
|
||||||
|
<div className="mt-3 space-y-2">
|
||||||
|
<div className="relative">
|
||||||
|
<pre className="text-xs font-mono bg-muted p-3 rounded overflow-x-auto max-h-60 overflow-y-auto">
|
||||||
|
{JSON.stringify(transaction.event, null, 2)}
|
||||||
|
</pre>
|
||||||
|
<CodeCopyButton
|
||||||
|
copied={copiedRaw}
|
||||||
|
onCopy={onCopyRaw}
|
||||||
|
label="Copy event JSON"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
WALLET_HISTORY_KIND,
|
WALLET_HISTORY_KIND,
|
||||||
} from "@/services/nip61-wallet";
|
} from "@/services/nip61-wallet";
|
||||||
import { kinds, relaySet } from "applesauce-core/helpers";
|
import { kinds, relaySet } from "applesauce-core/helpers";
|
||||||
import { useGrimoire } from "@/core/state";
|
|
||||||
|
|
||||||
// Import casts to enable user.wallet$ property
|
// Import casts to enable user.wallet$ property
|
||||||
import "applesauce-wallet/casts";
|
import "applesauce-wallet/casts";
|
||||||
@@ -44,7 +43,6 @@ export type WalletState =
|
|||||||
*/
|
*/
|
||||||
export function useNip61Wallet() {
|
export function useNip61Wallet() {
|
||||||
const { pubkey, canSign } = useAccount();
|
const { pubkey, canSign } = useAccount();
|
||||||
const { state: appState, setCashuWalletSyncEnabled } = useGrimoire();
|
|
||||||
const [unlocking, setUnlocking] = useState(false);
|
const [unlocking, setUnlocking] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [discoveryComplete, setDiscoveryComplete] = useState(false);
|
const [discoveryComplete, setDiscoveryComplete] = useState(false);
|
||||||
@@ -190,10 +188,12 @@ export function useNip61Wallet() {
|
|||||||
return Object.fromEntries(entries);
|
return Object.fromEntries(entries);
|
||||||
}, [balance]);
|
}, [balance]);
|
||||||
|
|
||||||
// Toggle sync setting
|
// Compute effective relays (wallet relays + user outbox relays)
|
||||||
const toggleSyncEnabled = useCallback(() => {
|
const effectiveRelays = useMemo(() => {
|
||||||
setCashuWalletSyncEnabled(!appState.cashuWalletSyncEnabled);
|
const walletRelays = relays || [];
|
||||||
}, [appState.cashuWalletSyncEnabled, setCashuWalletSyncEnabled]);
|
const userOutboxes = outboxes || [];
|
||||||
|
return relaySet(walletRelays, userOutboxes);
|
||||||
|
}, [relays, outboxes]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State machine
|
// State machine
|
||||||
@@ -214,17 +214,13 @@ export function useNip61Wallet() {
|
|||||||
tokens,
|
tokens,
|
||||||
history,
|
history,
|
||||||
mints,
|
mints,
|
||||||
relays,
|
relays: effectiveRelays, // Combined wallet + outbox relays
|
||||||
received, // Received nutzap IDs
|
received, // Received nutzap IDs
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
unlock,
|
unlock,
|
||||||
unlocking,
|
unlocking,
|
||||||
|
|
||||||
// Sync setting
|
|
||||||
syncEnabled: appState.cashuWalletSyncEnabled ?? false,
|
|
||||||
toggleSyncEnabled,
|
|
||||||
|
|
||||||
// Error state
|
// Error state
|
||||||
error,
|
error,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user