refactor: normalize hardcoded relay URLs and reorganize relay dropdown UI

**Normalize All Relay URLs:**
- Added trailing slashes to AGGREGATOR_RELAYS constants
- Ensures consistency with RelayStateManager's normalization
- Fixes fallback relay connection state tracking issue
- All hardcoded relay URLs now match normalized keys in relayStates

**Reorganize Relay Item UI:**
- Removed type indicator icons (LinkIcon/Sparkles/Inbox) from individual relay items
- Strategy type is already shown in header, no need to repeat per-item
- Moved inbox/outbox indicators from right side to left side of relay URL
- Left side now shows: inbox count (Mail icon) and/or outbox count (Send icon)
- Right side shows: event count, EOSE indicator, auth status, connection status
- Cleaner, more semantic layout with better visual hierarchy

**Why This Matters:**
The relay URL normalization fix ensures that fallback relays (AGGREGATOR_RELAYS)
now show accurate connection state in the UI. Previously, the non-normalized
URLs couldn't match keys in relayStates, making them appear disconnected even
when connected. This was the root cause of the "fallback relays not tracking"
issue.

All 639 tests passing.
This commit is contained in:
Claude
2025-12-22 18:02:41 +00:00
parent b5e1cffc91
commit b95ce25955
2 changed files with 40 additions and 60 deletions

View File

@@ -1056,33 +1056,46 @@ export default function ReqViewer({
// Find NIP-65 info for this relay (if using outbox)
const nip65Info = reasoning?.find((r) => r.relay === url);
// Determine relay type
const relayType = relays
? "explicit"
: nip65Info && !nip65Info.isFallback
? "outbox"
: "fallback";
// Type indicator icon (smaller, on left)
const typeIcon = {
explicit: (
<LinkIcon className="size-3 text-muted-foreground/60 flex-shrink-0" />
),
outbox: (
<Sparkles className="size-3 text-muted-foreground/60 flex-shrink-0" />
),
fallback: (
<Inbox className="size-3 text-muted-foreground/60 flex-shrink-0" />
),
}[relayType];
return (
<div
key={url}
className="flex items-center gap-2 text-xs py-1 px-3 hover:bg-accent/5"
>
{/* Type icon on left */}
{typeIcon}
{/* Left side: Inbox/Outbox indicators (if available) */}
<div className="flex items-center gap-1 flex-shrink-0 text-muted-foreground/70">
{nip65Info && nip65Info.readers.length > 0 && (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-0.5">
<Mail className="w-3 h-3" />
<span className="text-[10px]">
{nip65Info.readers.length}
</span>
</div>
</TooltipTrigger>
<TooltipContent>
Inbox for {nip65Info.readers.length} author
{nip65Info.readers.length !== 1 ? "s" : ""}
</TooltipContent>
</Tooltip>
)}
{nip65Info && nip65Info.writers.length > 0 && (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-0.5">
<Send className="w-3 h-3" />
<span className="text-[10px]">
{nip65Info.writers.length}
</span>
</div>
</TooltipTrigger>
<TooltipContent>
Outbox for {nip65Info.writers.length} author
{nip65Info.writers.length !== 1 ? "s" : ""}
</TooltipContent>
</Tooltip>
)}
</div>
{/* Relay URL */}
<RelayLink
@@ -1126,40 +1139,6 @@ export default function ReqViewer({
</Tooltip>
)}
{/* NIP-65 inbox/outbox indicators (if available) */}
{nip65Info && nip65Info.readers.length > 0 && (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-0.5">
<Mail className="w-3 h-3" />
<span className="text-[10px]">
{nip65Info.readers.length}
</span>
</div>
</TooltipTrigger>
<TooltipContent>
Inbox for {nip65Info.readers.length} author
{nip65Info.readers.length !== 1 ? "s" : ""}
</TooltipContent>
</Tooltip>
)}
{nip65Info && nip65Info.writers.length > 0 && (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-0.5">
<Send className="w-3 h-3" />
<span className="text-[10px]">
{nip65Info.writers.length}
</span>
</div>
</TooltipTrigger>
<TooltipContent>
Outbox for {nip65Info.writers.length} author
{nip65Info.writers.length !== 1 ? "s" : ""}
</TooltipContent>
</Tooltip>
)}
{/* Auth icon */}
{authIcon && (
<Tooltip>

View File

@@ -52,11 +52,12 @@ function extractRelayContext(event: NostrEvent): {
}
// Aggregator relays for better event discovery
// IMPORTANT: URLs must be normalized (trailing slash, lowercase) to match RelayStateManager keys
export const AGGREGATOR_RELAYS = [
"wss://relay.nostr.band",
"wss://nos.lol",
"wss://purplepag.es",
"wss://relay.primal.net",
"wss://relay.nostr.band/",
"wss://nos.lol/",
"wss://purplepag.es/",
"wss://relay.primal.net/",
];
// Base event loader (used internally)