import type { ReactNode } from 'react'; import type { Balances } from './shared'; import { ArrowDownLeft, ArrowUpRight, Bitcoin, Clock, Loader2, Zap } from 'lucide-react'; import { Link } from 'react-router'; import { toneFill, toneText } from '@/components/Data/tone'; import { KIND_LABELS, TX_DIRECTION_TONE, walletSectionPath } from './shared'; import { formatSats, formatTimestamp, truncateMiddle } from './format'; import { Amount, UnitToggle } from './Amount'; import { EmptyWallet } from './EmptyWallet'; import { SyncBadge } from './SyncBadge'; import { useSelectedWallet } from './useSelectedWallet'; import { useBalances, usePermissions, useTransactions, useWalletInfo } from './useWalletData'; // The at-a-glance section: what you hold, whether the node agrees with the chain, and the last few moves. // // Every number here is a read, so the whole screen works against a locked wallet — which is its resting // state. Nothing on this page prompts for a passphrase. /** * The sub-line under the on-chain figure. Unconfirmed and frozen are both reasons the headline number is * not what a send can use, and both can be true at once, so they read as a list rather than a choice. */ function onchainHint(balances: Balances | null | undefined): string | undefined { if (!balances) return undefined; const parts: string[] = []; if (balances.onchainUnconfirmed !== 0) { const sign = balances.onchainUnconfirmed > 0 ? '+' : ''; parts.push(`${sign}${formatSats(balances.onchainUnconfirmed)} sats unconfirmed`); } if (balances.onchainFrozen > 0) parts.push(`${formatSats(balances.onchainFrozen)} sats frozen`); return parts.length ? parts.join(' · ') : undefined; } export const OverviewView = () => { const { wallet, walletId, isLoading } = useSelectedWallet(); const { capabilities } = usePermissions(walletId); const { balances, sync, isLoading: balancesLoading } = useBalances(walletId); const { info } = useWalletInfo(walletId); const { transactions } = useTransactions(walletId, 5); if (!walletId) return ; const hasLightning = balances?.lightningBalance != null; const canSend = capabilities.includes('onchainSend') || capabilities.includes('lightningSend'); const canReceive = capabilities.includes('onchainReceive') || capabilities.includes('lightningReceive'); return (

{wallet?.name}

{wallet ? KIND_LABELS[wallet.kind] : '—'} {info?.alias && ` · ${info.alias}`} {info?.version && ` · ${info.version}`}

{balancesLoading && !balances ? (
Reading balances…
) : (
} label="On-chain" value={} hint={onchainHint(balances)} /> {hasLightning && ( } label="Lightning" value={} hint={ balances?.lightningInbound != null ? `${formatSats(balances.lightningInbound)} sats inbound` : undefined } /> )} {info?.blockHeight?.toLocaleString() ?? '—'} } hint={info ? (info.synced ? 'synced' : 'syncing…') : undefined} /> {info?.network ?? wallet?.network ?? '—'}} hint={info?.pubkey ? truncateMiddle(info.pubkey, 8, 6) : undefined} />
)} {(canReceive || canSend) && (
{canReceive && ( Receive )} {canSend && ( Send )}
)}

Recent activity

all transactions
{transactions.length === 0 ? (

Nothing yet.

) : (
    {transactions.map((tx) => (
  • {tx.amount < 0 ? : }
    {tx.label ?? truncateMiddle(tx.txid)}
    {tx.confirmations <= 0 && } {tx.confirmations <= 0 ? 'pending' : formatTimestamp(tx.timestamp)}
  • ))}
)}
); }; type TileProps = { label: string; value: ReactNode; hint?: string; icon?: ReactNode }; const Tile = ({ label, value, hint, icon }: TileProps) => (
{icon} {label}
{value}
{hint && (
{hint}
)}
);