Use app relays for loser's race instead of separate profile relays

- Removed hardcoded PROFILE_RELAYS list
- Loser's race now queries the same relays as the pool
- Gets effective relays from app config (user relays + app relays)
- Queries each relay individually with 5000ms timeout (vs 500ms EOSE in pool)
- Solves issue where profiles exist on app relays (like nos.lol) but not on hardcoded profile relays
- Now covers all relays user has configured instead of arbitrary subset

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy
2026-02-18 15:39:11 -06:00
parent 00e973cf3c
commit cb819e811f
2 changed files with 28 additions and 19 deletions
+14 -12
View File
@@ -1,15 +1,8 @@
import { type NostrEvent, type NostrMetadata, NSchema as n } from '@nostrify/nostrify';
import { useNostr } from '@nostrify/react';
import { useQuery } from '@tanstack/react-query';
/** Profile relay URLs used as fallback when the pool's EOSE timeout returns empty. */
export const PROFILE_RELAYS = [
'wss://relay.primal.net',
'wss://relay.damus.io',
'wss://relay.ditto.pub',
'wss://ditto.pub/relay',
'wss://purplepag.es',
];
import { useAppContext } from '@/hooks/useAppContext';
import { getEffectiveRelays } from '@/lib/appRelays';
/** Parse a kind-0 event into metadata + event, or return just the event on parse failure. */
export function parseAuthorEvent(event: NostrEvent): { event: NostrEvent; metadata?: NostrMetadata } {
@@ -23,6 +16,11 @@ export function parseAuthorEvent(event: NostrEvent): { event: NostrEvent; metada
export function useAuthor(pubkey: string | undefined) {
const { nostr } = useNostr();
const { config } = useAppContext();
// Get the effective relays (same ones used by the pool)
const effectiveRelays = getEffectiveRelays(config.relayMetadata, config.useAppRelays);
const readRelayUrls = effectiveRelays.relays.filter(r => r.read).map(r => r.url);
return useQuery<{ event?: NostrEvent; metadata?: NostrMetadata }>({
queryKey: ['author', pubkey ?? ''],
@@ -44,12 +42,16 @@ export function useAuthor(pubkey: string | undefined) {
}
// Slow path: pool returned empty (EOSE timeout may have cut off slower relays).
// Query each relay individually and resolve on first hit.
// Query each relay individually (same relays as pool, but with more time).
if (readRelayUrls.length === 0) {
return {};
}
return new Promise<{ event?: NostrEvent; metadata?: NostrMetadata }>((resolve) => {
let settled = false;
let pending = PROFILE_RELAYS.length;
let pending = readRelayUrls.length;
for (const url of PROFILE_RELAYS) {
for (const url of readRelayUrls) {
nostr.relay(url).query(
[{ kinds: [0], authors: [pubkey!], limit: 1 }],
{ signal: combinedSignal },
+14 -7
View File
@@ -1,7 +1,9 @@
import { type NostrEvent, type NostrMetadata } from '@nostrify/nostrify';
import { useNostr } from '@nostrify/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { parseAuthorEvent, PROFILE_RELAYS } from '@/hooks/useAuthor';
import { parseAuthorEvent } from '@/hooks/useAuthor';
import { useAppContext } from '@/hooks/useAppContext';
import { getEffectiveRelays } from '@/lib/appRelays';
export interface AuthorData {
pubkey: string;
@@ -21,6 +23,11 @@ export interface AuthorData {
export function useAuthors(pubkeys: string[]) {
const { nostr } = useNostr();
const queryClient = useQueryClient();
const { config } = useAppContext();
// Get the effective relays (same ones used by the pool)
const effectiveRelays = getEffectiveRelays(config.relayMetadata, config.useAppRelays);
const readRelayUrls = effectiveRelays.relays.filter(r => r.read).map(r => r.url);
// Deduplicate and sort for a stable query key
const uniquePubkeys = [...new Set(pubkeys)].sort();
@@ -56,16 +63,16 @@ export function useAuthors(pubkeys: string[]) {
found.add(event.pubkey);
}
// Slow path: for any pubkeys not found by the pool, query relays individually.
// This is the "loser's race" - we query specific profile relays that may have
// been cut off by the pool's EOSE timeout.
// Slow path: for any pubkeys not found by the pool, query each relay individually.
// This is the "loser's race" - we query the same relays from the pool, but
// individually with more time (5000ms vs 500ms EOSE timeout).
const missing = uniquePubkeys.filter(pk => !found.has(pk));
if (missing.length > 0) {
if (missing.length > 0 && readRelayUrls.length > 0) {
await new Promise<void>((resolve) => {
const needed = new Set(missing);
let pending = PROFILE_RELAYS.length;
let pending = readRelayUrls.length;
for (const url of PROFILE_RELAYS) {
for (const url of readRelayUrls) {
nostr.relay(url).query(
[{ kinds: [0], authors: missing, limit: missing.length }],
{ signal: combinedSignal },