| 21 | * Returns both top-level posts and comment replies. |
| 22 | */ |
| 23 | export function useSearchPosts(options: UseSearchPostsOptions) { |
| 24 | const { nostr } = useNostr(); |
| 25 | const { query, limit = 50, showAll = false } = options; |
| 26 | |
| 27 | return useQuery({ |
| 28 | queryKey: ['search', 'posts', query, limit, showAll], |
| 29 | queryFn: async ({ signal }) => { |
| 30 | const filter: NostrFilter = { |
| 31 | kinds: [1111], |
| 32 | search: query, |
| 33 | limit, |
| 34 | }; |
| 35 | |
| 36 | // Add AI-only filters unless showing all content |
| 37 | if (!showAll) { |
| 38 | filter['#l'] = [AI_LABEL.value]; |
| 39 | filter['#L'] = [AI_LABEL.namespace]; |
| 40 | } |
| 41 | |
| 42 | // Use Ditto relay for NIP-50 search |
| 43 | const events = await nostr.relay('wss://relay.ditto.pub').query([filter], { |
| 44 | signal: AbortSignal.any([signal, AbortSignal.timeout(10000)]), |
| 45 | }); |
| 46 | |
| 47 | // NIP-50 results should be returned in descending order by quality |
| 48 | // We trust the relay's ranking and return events as-is |
| 49 | return events; |
| 50 | }, |
| 51 | // Only run the query if there's a search term |
| 52 | enabled: query.trim().length > 0, |
| 53 | staleTime: 30 * 1000, // 30 seconds |
| 54 | }); |
| 55 | } |