diff --git a/src/components/EventLogViewer.tsx b/src/components/EventLogViewer.tsx index 2726fd7..b81929b 100644 --- a/src/components/EventLogViewer.tsx +++ b/src/components/EventLogViewer.tsx @@ -4,7 +4,7 @@ * Compact log of relay operations for debugging and introspection. */ -import { useState, useMemo } from "react"; +import { useState, useMemo, useCallback } from "react"; import { Check, X, @@ -19,7 +19,9 @@ import { Trash2, ChevronDown, ChevronRight, + AlertTriangle, } from "lucide-react"; +import { Virtuoso } from "react-virtuoso"; import { Button } from "./ui/button"; import { Tabs, TabsList, TabsTrigger } from "./ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; @@ -30,8 +32,10 @@ import { type EventLogType, type PublishLogEntry, type ConnectLogEntry, + type ErrorLogEntry, type AuthLogEntry, type NoticeLogEntry, + type RelayStatusEntry, } from "@/services/event-log"; import { formatTimestamp } from "@/hooks/useLocale"; import { cn } from "@/lib/utils"; @@ -48,7 +52,7 @@ type TabFilter = "all" | "publish" | "connect" | "auth" | "notice"; const TAB_TYPE_MAP: Record = { all: undefined, publish: ["PUBLISH"], - connect: ["CONNECT", "DISCONNECT"], + connect: ["CONNECT", "DISCONNECT", "ERROR"], auth: ["AUTH"], notice: ["NOTICE"], }; @@ -72,6 +76,20 @@ const AUTH_STATUS_TOOLTIP: Record = { rejected: "Auth rejected", }; +// ============================================================================ +// Helpers +// ============================================================================ + +/** Format relay response time relative to publish start */ +function formatRelayTime( + publishTimestamp: number, + relayUpdatedAt: number, +): string { + const ms = relayUpdatedAt - publishTimestamp; + if (ms < 1000) return `${ms}ms`; + return `${(ms / 1000).toFixed(1)}s`; +} + // ============================================================================ // Shared row layout // ============================================================================ @@ -143,12 +161,16 @@ function EntryRow({ function PublishRelayRow({ relay, status, + publishTimestamp, onRetry, }: { relay: string; - status: { status: string; error?: string }; + status: RelayStatusEntry; + publishTimestamp: number; onRetry?: () => void; }) { + const isTerminal = status.status === "success" || status.status === "error"; + return (
@@ -166,6 +188,11 @@ function PublishRelayRow({ showInboxOutbox={false} className="flex-1 min-w-0" /> + {isTerminal && ( + + {formatRelayTime(publishTimestamp, status.updatedAt)} + + )} {status.status === "error" && onRetry && (
)} +
+ Time: + + {formatTimestamp(entry.timestamp / 1000, "absolute")} + +
} > @@ -350,7 +430,17 @@ function NoticeEntry({ entry }: { entry: NoticeLogEntry }) { expanded={expanded} onToggle={() => setExpanded(!expanded)} details={ -
{entry.message}
+
+
+ {entry.message} +
+
+ Time: + + {formatTimestamp(entry.timestamp / 1000, "absolute")} + +
+
} > @@ -379,6 +469,8 @@ function LogEntryRenderer({ case "CONNECT": case "DISCONNECT": return ; + case "ERROR": + return ; case "AUTH": return ; case "NOTICE": @@ -415,6 +507,17 @@ export function EventLogViewer() { typeCounts, } = useEventLog({ types: filterTypes }); + const renderItem = useCallback( + (_index: number, entry: LogEntry) => ( + + ), + [retryFailedRelays, retryRelay], + ); + return (
{/* Header */} @@ -456,20 +559,17 @@ export function EventLogViewer() {
{/* Log entries */} -
+
{entries.length === 0 ? (

No events logged yet

) : ( - entries.map((entry) => ( - - )) + )}