import { useState, useEffect } from 'react'; import { getTransaction, formatToshis } from '../lib/api'; import type { Transaction } from '../lib/api'; import { Stack, Divider } from './ui/Layout'; import { Terminal, Section, LabelValue, DataTable, DataRow, ActionBar, Message, } from './ui/Terminal'; interface Props { txId: string; } const handleCache = new Map(); async function resolveHandle(did: string): Promise { if (handleCache.has(did)) { return handleCache.get(did)!; } try { // For did:web, extract the domain directly if (did.startsWith('did:web:')) { const domain = did.replace('did:web:', ''); handleCache.set(did, domain); return domain; } // For did:plc, use PLC directory if (did.startsWith('did:plc:')) { const didRes = await fetch(`https://plc.directory/${did}`); if (didRes.ok) { const doc = await didRes.json(); const handle = doc.alsoKnownAs?.[0]?.replace('at://', '') || did; handleCache.set(did, handle); return handle; } } } catch { // Fall back to DID } handleCache.set(did, did); return did; } export default function TransactionDetail({ txId }: Props) { const [tx, setTx] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [resolvedHandles, setResolvedHandles] = useState>(new Map()); useEffect(() => { async function fetchTx() { try { const result = await getTransaction(txId); setTx(result.transaction); const dids = new Set(); result.transaction.outputs.forEach(o => dids.add(o.owner)); // Handle both trigger types const trigger = result.transaction.trigger; if (trigger) { if ('followerDid' in trigger) { dids.add(trigger.followerDid); } else if ('senderDid' in trigger) { dids.add(trigger.senderDid); } } const handles = new Map(); await Promise.all( Array.from(dids).map(async did => { const handle = await resolveHandle(did); handles.set(did, handle); }) ); setResolvedHandles(handles); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to fetch transaction'); } finally { setLoading(false); } } fetchTx(); }, [txId]); if (loading) { return (
); } if (error || !tx) { return (
{error || 'Transaction not found'}
[BACK TO WALLET] [HOME]
); } const formatDid = (did: string) => { const handle = resolvedHandles.get(did); if (handle && handle !== did) { return `@${handle}`; } return did.length > 32 ? `${did.slice(0, 32)}...` : did; }; const totalOutput = tx.outputs.reduce((sum, o) => sum + o.amount, 0); return (
{tx.trigger && 'followerDid' in tx.trigger && ( <> )} {tx.trigger && 'type' in tx.trigger && (tx.trigger as { type: string }).type === 'cashtag' && ( <> )}
{tx.outputs.map((output, i) => ( ))}
{tx.type === 'transfer' && tx.inputs.length > 0 && ( <>
{tx.inputs.map((input, i) => ( ))}
)} [BACK TO WALLET] [HOME]
); }