// Right-pane for Contacts — profile card for the selected contact. // Shows identity, balance, device count, linked action buttons: // Open chat (switch to Messages section), Transfer, View posts (switch // to Feed author wall), Block (local only for now). import React, { useEffect, useState } from 'react'; import { useStore } from '@/lib/store'; import { getIdentity, fetchDevices, getBalance } from '@/lib/api'; import { shortAddr } from '@/lib/crypto'; import type { IdentityInfo } from '@/lib/api'; function formatT(ut: number): string { return (ut / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 3 }); } export function ContactsDetail(): React.ReactElement { const sel = useStore(s => s.selectedContact); const contact = useStore(s => s.contacts.find(c => c.address === sel)); const setSection = useStore(s => s.setSection); const setActive = useStore(s => s.setActiveChat); const setFeedTab = useStore(s => s.setFeedTab); const [identity, setIdentity] = useState(null); const [balance, setBalance] = useState(null); const [deviceCount, setDeviceCount] = useState(null); useEffect(() => { if (!sel) return; let cancelled = false; (async () => { const [id, bal, devs] = await Promise.all([ getIdentity(sel), getBalance(sel), fetchDevices(sel), ]); if (cancelled) return; setIdentity(id); setBalance(bal); setDeviceCount(devs.length); })(); return () => { cancelled = true; }; }, [sel]); if (!sel || !contact) { return (
Pick a contact on the left to view their profile.
); } const displayName = contact.username ? `@${contact.username}` : (identity?.nickname ? `@${identity.nickname}` : (contact.alias ?? shortAddr(contact.address, 8))); const openChat = () => { setActive(contact.address); setSection('messages'); }; const viewPosts = () => { setFeedTab({ kind: 'author', pub: contact.address }); setSection('feed'); }; const copy = (s: string) => navigator.clipboard.writeText(s).catch(() => {}); return (
{/* Header card */}
{displayName.replace(/^@/, '').charAt(0).toUpperCase()}
{displayName}
{contact.address}
{/* Actions */}
Open chat View posts copy(contact.address)}>Copy address
{/* Stats grid */}
{/* Identity details */} {identity && (
Identity
copy(identity.address)} /> {identity.nickname && } {identity.x25519_pub && ( copy(identity.x25519_pub)} /> )} {typeof identity.device_count === 'number' && ( )}
)}
); } function Btn({ children, onClick, primary }: { children: React.ReactNode; onClick: () => void; primary?: boolean; }) { return ( ); } function Stat({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); } function Row({ k, v, copyable, onCopy, }: { k: string; v: string; copyable?: boolean; onCopy?: () => void }) { return (
{k}
{v}
{copyable && ( )}
); }