import { useState } from "react";
import {
ChevronDown,
ChevronRight,
Radio,
FileText,
Wifi,
Filter as FilterIcon,
Circle,
} from "lucide-react";
import { useReqTimeline } from "@/hooks/useReqTimeline";
import { useGrimoire } from "@/core/state";
import { FeedEvent } from "./nostr/Feed";
import { KindBadge } from "./KindBadge";
import type { NostrFilter } from "@/types/nostr";
interface ReqViewerProps {
filter: NostrFilter;
relays?: string[];
closeOnEose?: boolean;
nip05Authors?: string[];
nip05PTags?: string[];
}
export default function ReqViewer({
filter,
relays,
closeOnEose = false,
nip05Authors,
nip05PTags,
}: ReqViewerProps) {
const { state } = useGrimoire();
// NIP-05 resolution already happened in argParser before window creation
// The filter prop already contains resolved pubkeys
// We just display the NIP-05 identifiers for user reference
// Use inbox relays if logged in and no relays specified
const defaultRelays =
relays ||
(state.activeAccount?.relays?.inbox.length
? state.activeAccount.relays.inbox.map((r) => r.url)
: ["wss://theforest.nostr1.com"]);
// Streaming is the default behavior, closeOnEose inverts it
const stream = !closeOnEose;
const { events, loading, error, eoseReceived } = useReqTimeline(
`req-${JSON.stringify(filter)}-${closeOnEose}`,
filter,
defaultRelays,
{ limit: filter.limit || 50, stream },
);
const [showRelays, setShowRelays] = useState(false);
const [showQuery, setShowQuery] = useState(false);
return (
{/* Compact Header */}
{/* Left: Status Indicator */}
{loading && !eoseReceived
? "LOADING"
: loading && eoseReceived && stream
? "LIVE"
: !loading && eoseReceived
? "CLOSED"
: "CONNECTING"}
{/* Right: Stats */}
{/* Event Count */}
{events.length}
{/* Relay Count (Clickable) */}
{/* Query (Clickable) */}
{/* Expandable Relays */}
{showRelays && (
{defaultRelays.map((relay) => (
{relay}
))}
)}
{/* Expandable Query */}
{showQuery && (
{/* Kind Badges */}
{filter.kinds && filter.kinds.length > 0 && (
Kinds:
{filter.kinds.map((kind) => (
))}
)}
{/* Authors with NIP-05 info */}
{filter.authors && filter.authors.length > 0 && (
Authors: {filter.authors.length}
{nip05Authors && nip05Authors.length > 0 && (
{nip05Authors.map((nip05) => (
→ {nip05}
))}
)}
)}
{/* #p Tags with NIP-05 info */}
{filter["#p"] && filter["#p"].length > 0 && (
#p Tags: {filter["#p"].length}
{nip05PTags && nip05PTags.length > 0 && (
{nip05PTags.map((nip05) => (
→ {nip05}
))}
)}
)}
{/* Limit */}
{filter.limit && (
Limit: {filter.limit}
)}
{/* Stream Mode */}
{stream && (
● Streaming mode enabled
)}
{/* Raw Query */}
Query Filter
{JSON.stringify(filter, null, 2)}
)}
{/* Error Display */}
{error && (
Error: {error.message}
)}
{/* Results */}
{loading && events.length === 0 && (
Loading events...
)}
{!loading && !stream && events.length === 0 && !error && (
No events found matching filter
)}
{stream && events.length === 0 && !loading && (
Waiting for events...
)}
{events.map((event) => (
))}
);
}