From c4bc3ab445bd0c9028353c03e7ab17c76e853f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Mon, 22 Dec 2025 19:43:00 +0100 Subject: [PATCH] ui: improve relay tooltip, update docs --- CLAUDE.md | 6 ++ docs/req-viewer-improvement-plan.md | 27 ++++++++- src/components/ReqViewer.tsx | 93 +++++++++++++++++------------ src/lib/relay-status-utils.tsx | 2 +- 4 files changed, 87 insertions(+), 41 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b1af184..1ed6558 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,6 +29,12 @@ Grimoire is a Nostr protocol explorer and developer tool. It's a tiling window m - Maintains failure counts, backoff states, last success/failure times - Prevents repeated connection attempts to dead relays +**Nostr Query State Machine** (`src/lib/req-state-machine.ts` + `src/hooks/useReqTimelineEnhanced.ts`): +- Accurate tracking of REQ subscriptions across multiple relays +- Distinguishes between `LIVE`, `LOADING`, `PARTIAL`, `OFFLINE`, `CLOSED`, and `FAILED` states +- Solves "LIVE with 0 relays" bug by tracking per-relay connection state and event counts +- Pattern: Subscribe to relays individually to detect per-relay EOSE and errors + **Critical**: Don't create new EventStore, RelayPool, or RelayLiveness instances - use the singletons in `src/services/` ### Window System diff --git a/docs/req-viewer-improvement-plan.md b/docs/req-viewer-improvement-plan.md index c844ec4..44e72ec 100644 --- a/docs/req-viewer-improvement-plan.md +++ b/docs/req-viewer-improvement-plan.md @@ -19,9 +19,32 @@ We'll combine two sources of truth: This hybrid approach avoids duplicate subscriptions while providing accurate status tracking. -## Implementation Tasks +## Implementation Progress -### Phase 1: Core Infrastructure +### COMPLETED: Phase 1: Core Infrastructure +- [x] Task 1.1: Create Per-Relay State Tracking Types (`src/types/req-state.ts`) +- [x] Task 1.2: Create State Derivation Logic (`src/lib/req-state-machine.ts`) +- [x] Task 1.3: Create Enhanced Timeline Hook (`src/hooks/useReqTimelineEnhanced.ts`) +- [x] Unit tests for state machine (`src/lib/req-state-machine.test.ts`) + +### COMPLETED: Phase 2: UI Integration +- [x] Task 2.1: Update ReqViewer Status Indicator with 8-state machine +- [x] Task 2.2: Enhance Relay Dropdown with Per-Relay Status and 2-column grid tooltip +- [x] Task 2.3: Add Empty/Error States (Failed, Offline, Partial) + +### PENDING: Phase 3: Testing & Polish +- [ ] Task 3.1: Add Unit Tests for `useReqTimelineEnhanced` hook +- [ ] Task 3.2: Add Integration Tests for `ReqViewer` UI +- [ ] Task 3.3: Complete Manual Testing Checklist + +### FUTURE: Phase 4: Future Enhancements +- [ ] Task 4.1: Relay Performance Metrics (Latency tracking) +- [ ] Task 4.2: Smart Relay Selection (Integrate with RelayLiveness) +- [ ] Task 4.3: Query Optimization Suggestions + +--- + +## Original Implementation Tasks (Reference) #### Task 1.1: Create Per-Relay State Tracking Types diff --git a/src/components/ReqViewer.tsx b/src/components/ReqViewer.tsx index 943cc75..84fa9b2 100644 --- a/src/components/ReqViewer.tsx +++ b/src/components/ReqViewer.tsx @@ -1057,59 +1057,76 @@ export default function ReqViewer({ // Build comprehensive tooltip content const tooltipContent = ( -
-
+
+
{url}
-
-
- Connection: - - {connIcon.label} - +
+
+
+ Connection +
+
+ {connIcon.icon} + {connIcon.label} +
-
- Auth: - - {authIcon.label} - + +
+
+ Authentication +
+
+ {authIcon.icon} + {authIcon.label} +
+ {reqState && ( <> -
- Subscription: - - {reqState.subscriptionState} - -
- {reqState.eventCount > 0 && ( -
- Events: - - {reqState.eventCount} received - +
+
+ Subscription
- )} +
+ {reqState.subscriptionState} +
+
+ +
+
+ Events +
+
+ + {reqState.eventCount} received +
+
)} + {nip65Info && ( <> {nip65Info.readers.length > 0 && ( -
- Inbox: - +
+
+ Inbox (Read) +
+
{nip65Info.readers.length} author {nip65Info.readers.length !== 1 ? "s" : ""} - +
)} {nip65Info.writers.length > 0 && ( -
- Outbox: - +
+
+ Outbox (Write) +
+
{nip65Info.writers.length} author {nip65Info.writers.length !== 1 ? "s" : ""} - +
)} @@ -1133,9 +1150,8 @@ export default function ReqViewer({
{/* Event count badge */} {reqState && reqState.eventCount > 0 && ( -
- - {reqState.eventCount} +
+ [{reqState.eventCount}]
)} @@ -1146,7 +1162,8 @@ export default function ReqViewer({ ) : ( (reqState.subscriptionState === "receiving" || - reqState.subscriptionState === "waiting") && ( + reqState.subscriptionState === + "waiting") && ( ) )} diff --git a/src/lib/relay-status-utils.tsx b/src/lib/relay-status-utils.tsx index 321c18c..a186af0 100644 --- a/src/lib/relay-status-utils.tsx +++ b/src/lib/relay-status-utils.tsx @@ -78,7 +78,7 @@ export function getAuthIcon(relay: RelayState | undefined) { }, none: { icon: , - label: "No Authentication Required", + label: "Not required", }, }; return iconMap[relay.authStatus] || iconMap.none;