import type { WalletSummary } from './shared'; import { useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router'; import { Check, Eye, KeyRound, Pencil, Star, Trash2, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { KIND_LABELS, walletSectionPath } from './shared'; import { ChainSourceCard } from './ChainSourceCard'; import { CopyField } from './CopyField'; import { EmptyWallet } from './EmptyWallet'; import { LockBadge } from './LockBadge'; import { RescanCard } from './RescanCard'; import { useSelectedWallet } from './useSelectedWallet'; import { useLockCountdown } from './useLockCountdown'; import { usePermissions, useWalletConfig, useWalletLifecycle } from './useWalletData'; import { ChangePassphraseDialog } from './dialogs/ChangePassphraseDialog'; import { ExportSeedDialog } from './dialogs/ExportSeedDialog'; import { DeleteWalletDialog } from './dialogs/DeleteWalletDialog'; // Wallet-level administration: what this wallet is, what its backend can do, and the three passphrase // operations. Nothing here needs the wallet unlocked to *read* — each destructive action asks for the // passphrase itself, because an open unlock window is not consent to change or destroy the key material. export const WalletSettingsView = () => { const navigate = useNavigate(); const { wallet, walletId, isLoading } = useSelectedWallet(); const { config } = useWalletConfig(); const { capabilities, kind } = usePermissions(walletId); const { hasSeed } = useLockCountdown(walletId); const { activate, rename } = useWalletLifecycle(); const [passphraseOpen, setPassphraseOpen] = useState(false); const [exportOpen, setExportOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); if (!wallet || walletId == null) return ; const xpubs = Object.entries(wallet.xpubs ?? {}); return (
rename.mutateAsync({ walletId, name })} isPending={rename.isPending} />

{KIND_LABELS[wallet.kind]} · {wallet.network} · added {wallet.createdAt.slice(0, 10)}

{kind ?? wallet.kind} BIP{wallet.defaultBip} {wallet.fingerprint && ( {wallet.fingerprint} )} {wallet.isActive ? 'yes' : 'no'} {hasSeed === true ? 'yes' : hasSeed === false ? 'no' : '—'}
{!wallet.isActive && ( )}
{xpubs.length > 0 && (

Account keys

Extended public keys. They can watch this wallet but never spend it — safe to hand to a block explorer or an accounting tool.

{xpubs.map(([path, xpub]) => ( ))}
)}

What this backend can do

{capabilities.length === 0 ? (

No capabilities reported.

) : (
{capabilities.map((cap) => ( {cap} ))}
)}
{hasSeed === true && (

Key material

Both of these ask for the passphrase on their own, whether or not the wallet is currently unlocked.

)} {config && (

Deployment

{config.network} {formatMinutes(config.unlockTtlSec)} {config.storeKeyConfigured ? 'configured' : missing}
)}

Danger

{wallet.hasSeed ? 'Deleting erases the encrypted seed. Only your written recovery phrase can bring the coins back.' : 'Deleting removes the connection. The node itself is untouched.'}

{passphraseOpen && ( )} {exportOpen && } {deleteOpen && ( navigate(walletSectionPath('overview'), { replace: true })} /> )}
); }; /** The wallet's name is the one field here that is purely a label — editing it in place, not in a dialog. */ const MAX_NAME = 64; type WalletNameFieldProps = { wallet: WalletSummary; onSave: (name: string) => Promise; isPending: boolean; }; const WalletNameField = ({ wallet, onSave, isPending }: WalletNameFieldProps) => { const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(wallet.name); const inputRef = useRef(null); // Switching wallets while the editor is open would otherwise rename the NEW wallet to the old one's draft. useEffect(() => { setEditing(false); setDraft(wallet.name); }, [wallet.id, wallet.name]); useEffect(() => { if (editing) inputRef.current?.select(); }, [editing]); const trimmed = draft.trim(); const dirty = trimmed !== wallet.name; const commit = async (ev?: React.FormEvent) => { ev?.preventDefault(); if (!trimmed || !dirty) { setDraft(wallet.name); setEditing(false); return; } try { await onSave(trimmed); setEditing(false); } catch { // The mutation already toasted; keep the editor open so the typed name isn't lost. } }; const cancel = () => { setDraft(wallet.name); setEditing(false); }; if (!editing) { return (

{wallet.name}

); } return (
setDraft(ev.target.value)} onKeyDown={(ev) => { if (ev.key === 'Escape') cancel(); }} className="h-7 max-w-56 text-sm font-semibold" aria-label="Wallet name" autoComplete="off" spellCheck={false} />
); }; const formatMinutes = (seconds: number) => (seconds >= 60 ? `${Math.round(seconds / 60)} min` : `${seconds}s`); const Row = ({ label, children }: { label: string; children: React.ReactNode }) => (
{label}
{children}
);