mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-14 16:49:10 +02:00
Revert automatic relay selection for COUNT command
Simplify COUNT by requiring explicit relay specification: - Restore relay requirement validation in count-parser.ts - Remove useOutboxRelays and NIP-45 auto-filtering from CountViewer - Update man page documentation to reflect required relays - Keep NIP-45 support detection for better error messages This keeps the feature simpler for now; automatic relay selection can be added later when the UX is better understood.
This commit is contained in:
@@ -14,11 +14,8 @@ import {
|
||||
import { firstValueFrom, timeout, catchError, of } from "rxjs";
|
||||
import { useGrimoire } from "@/core/state";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { useOutboxRelays } from "@/hooks/useOutboxRelays";
|
||||
import pool from "@/services/relay-pool";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { getRelayInfo } from "@/lib/nip11";
|
||||
import { normalizeRelayURL } from "@/lib/relay-url";
|
||||
import { RelayLink } from "./nostr/RelayLink";
|
||||
import { FilterSummaryBadges } from "./nostr/FilterSummaryBadges";
|
||||
import { KindBadge } from "./KindBadge";
|
||||
@@ -43,7 +40,7 @@ import type { Filter } from "nostr-tools";
|
||||
|
||||
interface CountViewerProps {
|
||||
filter: NostrFilter;
|
||||
relays?: string[]; // Optional - uses outbox model if not specified
|
||||
relays: string[]; // Required - at least one relay
|
||||
needsAccount?: boolean;
|
||||
}
|
||||
|
||||
@@ -283,16 +280,12 @@ function SingleRelayResult({ result }: { result: RelayCountResult }) {
|
||||
|
||||
export default function CountViewer({
|
||||
filter: rawFilter,
|
||||
relays: explicitRelays,
|
||||
relays,
|
||||
needsAccount,
|
||||
}: CountViewerProps) {
|
||||
const { state } = useGrimoire();
|
||||
const accountPubkey = state.activeAccount?.pubkey;
|
||||
const { copy: handleCopy, copied } = useCopy();
|
||||
const [nip45FilteredRelays, setNip45FilteredRelays] = useState<string[]>([]);
|
||||
const [nip45FilterPhase, setNip45FilterPhase] = useState<
|
||||
"idle" | "filtering" | "ready"
|
||||
>("idle");
|
||||
|
||||
// Create pointer for contact list (kind 3) if we need to resolve $contacts
|
||||
const contactPointer = useMemo(
|
||||
@@ -324,84 +317,7 @@ export default function CountViewer({
|
||||
[needsAccount, rawFilter, accountPubkey, contacts],
|
||||
);
|
||||
|
||||
// Fallback relays for outbox selection (user's read relays or aggregators)
|
||||
const fallbackRelays = useMemo(
|
||||
() =>
|
||||
state.activeAccount?.relays?.filter((r) => r.read).map((r) => r.url) ||
|
||||
AGGREGATOR_RELAYS,
|
||||
[state.activeAccount?.relays],
|
||||
);
|
||||
|
||||
// Outbox options for relay selection
|
||||
const outboxOptions = useMemo(
|
||||
() => ({
|
||||
fallbackRelays,
|
||||
timeout: 1000,
|
||||
maxRelays: 20, // Fewer relays for COUNT since it's one-shot
|
||||
}),
|
||||
[fallbackRelays],
|
||||
);
|
||||
|
||||
// Use outbox model for relay selection when no explicit relays
|
||||
const { relays: selectedRelays, phase: relaySelectionPhase } =
|
||||
useOutboxRelays(explicitRelays ? {} : filter, outboxOptions);
|
||||
|
||||
// Filter relays by NIP-45 support
|
||||
useEffect(() => {
|
||||
// Skip if explicit relays provided or selection not ready
|
||||
if (explicitRelays) {
|
||||
setNip45FilteredRelays(explicitRelays);
|
||||
setNip45FilterPhase("ready");
|
||||
return;
|
||||
}
|
||||
|
||||
if (relaySelectionPhase !== "ready" || selectedRelays.length === 0) {
|
||||
setNip45FilterPhase("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter by NIP-45 support
|
||||
setNip45FilterPhase("filtering");
|
||||
|
||||
const filterByNip45 = async () => {
|
||||
const results = await Promise.all(
|
||||
selectedRelays.map(async (url) => {
|
||||
const supported = await checkNip45Support(url);
|
||||
// Include if supported or unknown (will error at request time)
|
||||
return { url, include: supported !== false };
|
||||
}),
|
||||
);
|
||||
|
||||
const filtered = results
|
||||
.filter((r) => r.include)
|
||||
.map((r) => {
|
||||
try {
|
||||
return normalizeRelayURL(r.url);
|
||||
} catch {
|
||||
return r.url;
|
||||
}
|
||||
});
|
||||
|
||||
// Use fallback aggregators if no NIP-45 relays found
|
||||
setNip45FilteredRelays(
|
||||
filtered.length > 0 ? filtered : AGGREGATOR_RELAYS.slice(0, 3),
|
||||
);
|
||||
setNip45FilterPhase("ready");
|
||||
};
|
||||
|
||||
filterByNip45();
|
||||
}, [explicitRelays, selectedRelays, relaySelectionPhase]);
|
||||
|
||||
// Final relays to use
|
||||
const relays = nip45FilteredRelays;
|
||||
const isSelectingRelays =
|
||||
!explicitRelays &&
|
||||
(relaySelectionPhase !== "ready" || nip45FilterPhase !== "ready");
|
||||
|
||||
const { results, loading, refresh } = useCount(
|
||||
filter,
|
||||
isSelectingRelays ? [] : relays,
|
||||
);
|
||||
const { results, loading, refresh } = useCount(filter, relays);
|
||||
|
||||
const isSingleRelay = relays.length === 1;
|
||||
const singleResult = isSingleRelay ? results.get(relays[0]) : null;
|
||||
@@ -659,22 +575,8 @@ export default function CountViewer({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Selecting Relays */}
|
||||
{(!needsAccount || accountPubkey) && isSelectingRelays && (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-muted-foreground text-center">
|
||||
<Loader2 className="size-8 mx-auto mb-3 animate-spin" />
|
||||
<p className="text-sm">
|
||||
{nip45FilterPhase === "filtering"
|
||||
? "Filtering relays by NIP-45 support..."
|
||||
: "Selecting relays..."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Results */}
|
||||
{(!needsAccount || accountPubkey) && !isSelectingRelays && (
|
||||
{(!needsAccount || accountPubkey) && (
|
||||
<div className="flex-1 overflow-auto">
|
||||
{isSingleRelay && singleResult ? (
|
||||
<SingleRelayResult result={singleResult} />
|
||||
|
||||
@@ -44,7 +44,7 @@ function parseCommaSeparated<T>(
|
||||
/**
|
||||
* Parse COUNT command arguments into a Nostr filter
|
||||
* Similar to REQ but:
|
||||
* - Requires at least one relay (no automatic relay selection)
|
||||
* - Requires at least one relay
|
||||
* - No --limit flag (COUNT returns total, not a subset)
|
||||
* - No --close-on-eose flag (COUNT is inherently one-shot)
|
||||
* - No --view flag (COUNT doesn't render events)
|
||||
@@ -335,7 +335,10 @@ export function parseCountCommand(args: string[]): ParsedCountCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Relays are optional - will use outbox model if not specified
|
||||
// Validate that at least one relay is specified
|
||||
if (relays.length === 0) {
|
||||
throw new Error("At least one relay is required for COUNT");
|
||||
}
|
||||
|
||||
// Convert accumulated sets to filter arrays
|
||||
if (kinds.size > 0) filter.kinds = Array.from(kinds);
|
||||
|
||||
@@ -325,14 +325,14 @@ export const manPages: Record<string, ManPageEntry> = {
|
||||
count: {
|
||||
name: "count",
|
||||
section: "1",
|
||||
synopsis: "count [relay...] [options]",
|
||||
synopsis: "count <relay...> [options]",
|
||||
description:
|
||||
"Count events on Nostr relays using the NIP-45 COUNT verb. Returns event counts matching specified filter criteria. If no relays specified, automatically selects relays using the outbox model (NIP-65) filtered by NIP-45 support. Checks NIP-11 relay info to detect NIP-45 support before querying. Can be saved as a spell for quick access.",
|
||||
"Count events on Nostr relays using the NIP-45 COUNT verb. Returns event counts matching specified filter criteria. Requires at least one relay. Checks NIP-11 relay info to detect NIP-45 support before querying. Can be saved as a spell for quick access.",
|
||||
options: [
|
||||
{
|
||||
flag: "[relay...]",
|
||||
flag: "<relay...>",
|
||||
description:
|
||||
"Relay URLs to query (optional). If omitted, uses outbox model with NIP-45 filtering. Can appear anywhere in the command. Supports wss://relay.com or shorthand: relay.com",
|
||||
"Relay URLs to query (required). At least one relay must be specified. Can appear anywhere in the command. Supports wss://relay.com or shorthand: relay.com",
|
||||
},
|
||||
{
|
||||
flag: "-k, --kind <number>",
|
||||
@@ -390,12 +390,11 @@ export const manPages: Record<string, ManPageEntry> = {
|
||||
},
|
||||
],
|
||||
examples: [
|
||||
"count -k 1 -a fiatjaf.com Count posts (auto relay selection)",
|
||||
"count -k 3 -p $me Count followers (auto relay selection)",
|
||||
"count relay.damus.io -k 3 -p npub1... Count followers on specific relay",
|
||||
"count nos.lol relay.damus.io -k 1 -a fiatjaf.com Compare counts across relays",
|
||||
"count -k 9735 -p $me --since 30d Count zaps received in last month",
|
||||
"count -t nostr,bitcoin Count events with hashtags",
|
||||
"count relay.damus.io -k 1 -a fiatjaf.com Count posts from author",
|
||||
"count nos.lol -k 3 -p npub1... Count followers on specific relay",
|
||||
"count nos.lol relay.damus.io -k 1 -a npub1... Compare counts across relays",
|
||||
"count relay.damus.io -k 9735 -p $me --since 30d Count zaps received in last month",
|
||||
"count nos.lol -t nostr,bitcoin Count events with hashtags",
|
||||
],
|
||||
seeAlso: ["req", "nip"],
|
||||
appId: "count",
|
||||
|
||||
Reference in New Issue
Block a user