diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts index 686c70d0..32e79484 100644 --- a/src/hooks/useEvent.ts +++ b/src/hooks/useEvent.ts @@ -2,19 +2,33 @@ import { useNostr } from '@nostrify/react'; import { useQuery } from '@tanstack/react-query'; import type { NostrEvent } from '@nostrify/nostrify'; -/** Fetches a single Nostr event by its hex ID. */ -export function useEvent(eventId: string | undefined) { +/** Fetches a single Nostr event by its hex ID, optionally querying relay hints. */ +export function useEvent(eventId: string | undefined, relays?: string[]) { const { nostr } = useNostr(); return useQuery({ queryKey: ['event', eventId ?? ''], queryFn: async ({ signal }) => { if (!eventId) return null; - const events = await nostr.query( - [{ ids: [eventId], limit: 1 }], - { signal: AbortSignal.any([signal, AbortSignal.timeout(5000)]) }, - ); - return events[0] ?? null; + const querySignal = AbortSignal.any([signal, AbortSignal.timeout(5000)]); + const filter = [{ ids: [eventId], limit: 1 }]; + + // Query the user's configured relays first + const events = await nostr.query(filter, { signal: querySignal }); + if (events.length > 0) return events[0]; + + // If not found and we have relay hints, try those relays directly + if (relays && relays.length > 0) { + try { + const hintSignal = AbortSignal.any([signal, AbortSignal.timeout(5000)]); + const hintEvents = await nostr.group(relays).query(filter, { signal: hintSignal }); + if (hintEvents.length > 0) return hintEvents[0]; + } catch { + // relay hint query failed — fall through + } + } + + return null; }, enabled: !!eventId, staleTime: 5 * 60 * 1000, @@ -28,19 +42,33 @@ export interface AddrCoords { identifier: string; } -/** Fetches a single addressable Nostr event by kind + pubkey + d-tag. */ -export function useAddrEvent(addr: AddrCoords | undefined) { +/** Fetches a single addressable Nostr event by kind + pubkey + d-tag, optionally querying relay hints. */ +export function useAddrEvent(addr: AddrCoords | undefined, relays?: string[]) { const { nostr } = useNostr(); return useQuery({ queryKey: ['addr-event', addr?.kind ?? 0, addr?.pubkey ?? '', addr?.identifier ?? ''], queryFn: async ({ signal }) => { if (!addr) return null; - const events = await nostr.query( - [{ kinds: [addr.kind], authors: [addr.pubkey], '#d': [addr.identifier], limit: 1 }], - { signal: AbortSignal.any([signal, AbortSignal.timeout(5000)]) }, - ); - return events[0] ?? null; + const querySignal = AbortSignal.any([signal, AbortSignal.timeout(5000)]); + const filter = [{ kinds: [addr.kind], authors: [addr.pubkey], '#d': [addr.identifier], limit: 1 }]; + + // Query the user's configured relays first + const events = await nostr.query(filter, { signal: querySignal }); + if (events.length > 0) return events[0]; + + // If not found and we have relay hints, try those relays directly + if (relays && relays.length > 0) { + try { + const hintSignal = AbortSignal.any([signal, AbortSignal.timeout(5000)]); + const hintEvents = await nostr.group(relays).query(filter, { signal: hintSignal }); + if (hintEvents.length > 0) return hintEvents[0]; + } catch { + // relay hint query failed — fall through + } + } + + return null; }, enabled: !!addr, staleTime: 5 * 60 * 1000, diff --git a/src/pages/NIP19Page.tsx b/src/pages/NIP19Page.tsx index 91dcbb02..113bb135 100644 --- a/src/pages/NIP19Page.tsx +++ b/src/pages/NIP19Page.tsx @@ -29,12 +29,14 @@ export function NIP19Page() { case 'note': return ; - case 'nevent': - return ; + case 'nevent': { + const neventData = decoded.data as { id: string; relays?: string[] }; + return ; + } case 'naddr': { const addr = decoded.data as AddressPointer; - return ; + return ; } default: diff --git a/src/pages/PostDetailPage.tsx b/src/pages/PostDetailPage.tsx index 5484fa13..1462ff24 100644 --- a/src/pages/PostDetailPage.tsx +++ b/src/pages/PostDetailPage.tsx @@ -36,10 +36,12 @@ import NotFound from './NotFound'; interface PostDetailPageProps { eventId: string; + relays?: string[]; } interface AddrPostDetailPageProps { addr: AddrCoords; + relays?: string[]; } /** Formats a sats amount into a compact human-readable string. */ @@ -123,8 +125,8 @@ function getParentEventId(event: NostrEvent): string | undefined { return undefined; } -export function PostDetailPage({ eventId }: PostDetailPageProps) { - const { data: event, isLoading, isError } = useEvent(eventId); +export function PostDetailPage({ eventId, relays }: PostDetailPageProps) { + const { data: event, isLoading, isError } = useEvent(eventId, relays); useSeoMeta({ title: event ? 'Post Details - Mew' : 'Loading... - Mew', @@ -154,8 +156,8 @@ export function PostDetailPage({ eventId }: PostDetailPageProps) { } /** Detail page for addressable events (naddr). Same layout as PostDetailPage. */ -export function AddrPostDetailPage({ addr }: AddrPostDetailPageProps) { - const { data: event, isLoading, isError } = useAddrEvent(addr); +export function AddrPostDetailPage({ addr, relays }: AddrPostDetailPageProps) { + const { data: event, isLoading, isError } = useAddrEvent(addr, relays); useSeoMeta({ title: event