|
|
|
|
@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
|
|
|
|
|
<script lang="ts"> |
|
|
|
|
import { goto } from '$app/navigation'; |
|
|
|
|
import { goto } from "$app/navigation"; |
|
|
|
|
import { Input, Button } from "flowbite-svelte"; |
|
|
|
|
import { Spinner } from "flowbite-svelte"; |
|
|
|
|
import type { NDKEvent } from "$lib/utils/nostrUtils"; |
|
|
|
|
@ -8,19 +8,22 @@
@@ -8,19 +8,22 @@
|
|
|
|
|
searchBySubscription, |
|
|
|
|
searchNip05, |
|
|
|
|
} from "$lib/utils/search_utility"; |
|
|
|
|
import type { SearchCallbacks } from "$lib/utils/search_types"; |
|
|
|
|
import { neventEncode, naddrEncode, nprofileEncode } from "$lib/utils"; |
|
|
|
|
import { activeInboxRelays, activeOutboxRelays, getNdkContext } from "$lib/ndk"; |
|
|
|
|
import { |
|
|
|
|
activeInboxRelays, |
|
|
|
|
activeOutboxRelays, |
|
|
|
|
getNdkContext, |
|
|
|
|
} from "$lib/ndk"; |
|
|
|
|
import { getMatchingTags, toNpub } from "$lib/utils/nostrUtils"; |
|
|
|
|
import { isEventId } from "$lib/utils/nostr_identifiers"; |
|
|
|
|
import type NDK from '@nostr-dev-kit/ndk'; |
|
|
|
|
import type { SearchType } from "$lib/models/search_type"; |
|
|
|
|
|
|
|
|
|
// Props definition |
|
|
|
|
let { |
|
|
|
|
loading, |
|
|
|
|
error, |
|
|
|
|
searchValue, |
|
|
|
|
dTagValue, |
|
|
|
|
searchType, |
|
|
|
|
onEventFound, |
|
|
|
|
onSearchResults, |
|
|
|
|
event, |
|
|
|
|
@ -30,7 +33,7 @@
@@ -30,7 +33,7 @@
|
|
|
|
|
loading: boolean; |
|
|
|
|
error: string | null; |
|
|
|
|
searchValue: string | null; |
|
|
|
|
dTagValue: string | null; |
|
|
|
|
searchType: SearchType | null; |
|
|
|
|
onEventFound: (event: NDKEvent) => void; |
|
|
|
|
onSearchResults: ( |
|
|
|
|
firstOrder: NDKEvent[], |
|
|
|
|
@ -70,7 +73,7 @@
@@ -70,7 +73,7 @@
|
|
|
|
|
|
|
|
|
|
// Track last processed values to prevent loops |
|
|
|
|
let lastProcessedSearchValue = $state<string | null>(null); |
|
|
|
|
let lastProcessedDTagValue = $state<string | null>(null); |
|
|
|
|
let lastProcessedSearchType = $state<SearchType | null>(null); |
|
|
|
|
let isProcessingSearch = $state(false); |
|
|
|
|
let currentProcessingSearchValue = $state<string | null>(null); |
|
|
|
|
let lastSearchValue = $state<string | null>(null); |
|
|
|
|
@ -110,7 +113,10 @@
@@ -110,7 +113,10 @@
|
|
|
|
|
updateSearchState(false, true, 1, "event"); |
|
|
|
|
} |
|
|
|
|
} catch (err) { |
|
|
|
|
handleSearchError(err, "Error fetching event. Please check the ID and try again."); |
|
|
|
|
handleSearchError( |
|
|
|
|
err, |
|
|
|
|
"Error fetching event. Please check the ID and try again.", |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -129,7 +135,9 @@
@@ -129,7 +135,9 @@
|
|
|
|
|
isResetting = false; |
|
|
|
|
isUserEditing = false; |
|
|
|
|
|
|
|
|
|
const query = (queryOverride !== undefined ? queryOverride || "" : searchQuery || "").trim(); |
|
|
|
|
const query = ( |
|
|
|
|
queryOverride !== undefined ? queryOverride || "" : searchQuery || "" |
|
|
|
|
).trim(); |
|
|
|
|
if (!query) { |
|
|
|
|
updateSearchState(false, false, null, null); |
|
|
|
|
return; |
|
|
|
|
@ -181,7 +189,12 @@
@@ -181,7 +189,12 @@
|
|
|
|
|
|
|
|
|
|
// AI-NOTE: 2025-01-24 - Treat plain text searches as profile searches by default |
|
|
|
|
// This allows searching for names like "thebeave" or "TheBeave" without needing n: prefix |
|
|
|
|
if (trimmedQuery && !trimmedQuery.startsWith("nevent") && !trimmedQuery.startsWith("npub") && !trimmedQuery.startsWith("naddr")) { |
|
|
|
|
if ( |
|
|
|
|
trimmedQuery && |
|
|
|
|
!trimmedQuery.startsWith("nevent") && |
|
|
|
|
!trimmedQuery.startsWith("npub") && |
|
|
|
|
!trimmedQuery.startsWith("naddr") |
|
|
|
|
) { |
|
|
|
|
return { type: "n", term: trimmedQuery }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -191,7 +204,7 @@
@@ -191,7 +204,7 @@
|
|
|
|
|
async function handleSearchByType( |
|
|
|
|
searchType: { type: string; term: string }, |
|
|
|
|
query: string, |
|
|
|
|
clearInput: boolean |
|
|
|
|
clearInput: boolean, |
|
|
|
|
) { |
|
|
|
|
const { type, term } = searchType; |
|
|
|
|
|
|
|
|
|
@ -250,10 +263,16 @@
@@ -250,10 +263,16 @@
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (dTagValue) { |
|
|
|
|
searchQuery = `d:${dTagValue}`; |
|
|
|
|
} else if (searchValue) { |
|
|
|
|
searchQuery = searchValue; |
|
|
|
|
if (searchValue && searchType) { |
|
|
|
|
if (searchType === "d") { |
|
|
|
|
searchQuery = `d:${searchValue}`; |
|
|
|
|
} else if (searchType === "t") { |
|
|
|
|
searchQuery = `t:${searchValue}`; |
|
|
|
|
} else if (searchType === "n") { |
|
|
|
|
searchQuery = `n:${searchValue}`; |
|
|
|
|
} else { |
|
|
|
|
searchQuery = searchValue; |
|
|
|
|
} |
|
|
|
|
} else if (!searchQuery) { |
|
|
|
|
searchQuery = ""; |
|
|
|
|
} |
|
|
|
|
@ -303,17 +322,29 @@
@@ -303,17 +322,29 @@
|
|
|
|
|
|
|
|
|
|
$effect(() => { |
|
|
|
|
if ( |
|
|
|
|
dTagValue && |
|
|
|
|
searchValue && |
|
|
|
|
searchType && |
|
|
|
|
!searching && |
|
|
|
|
!isResetting && |
|
|
|
|
dTagValue !== lastProcessedDTagValue |
|
|
|
|
(searchType !== lastProcessedSearchType || |
|
|
|
|
searchValue !== lastProcessedSearchValue) |
|
|
|
|
) { |
|
|
|
|
console.log("EventSearch: Processing dTagValue:", dTagValue); |
|
|
|
|
lastProcessedDTagValue = dTagValue; |
|
|
|
|
console.log("EventSearch: Processing search:", { |
|
|
|
|
searchType, |
|
|
|
|
searchValue, |
|
|
|
|
}); |
|
|
|
|
lastProcessedSearchType = searchType; |
|
|
|
|
lastProcessedSearchValue = searchValue; |
|
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
if (!searching && !isResetting) { |
|
|
|
|
handleSearchBySubscription("d", dTagValue); |
|
|
|
|
if (searchType === "d") { |
|
|
|
|
handleSearchBySubscription("d", searchValue); |
|
|
|
|
} else if (searchType === "t") { |
|
|
|
|
handleSearchBySubscription("t", searchValue); |
|
|
|
|
} else if (searchType === "n") { |
|
|
|
|
handleSearchBySubscription("n", searchValue); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, 100); |
|
|
|
|
} |
|
|
|
|
@ -386,7 +417,7 @@
@@ -386,7 +417,7 @@
|
|
|
|
|
foundEvent = null; |
|
|
|
|
localError = null; |
|
|
|
|
lastProcessedSearchValue = null; |
|
|
|
|
lastProcessedDTagValue = null; |
|
|
|
|
lastProcessedSearchType = null; |
|
|
|
|
isProcessingSearch = false; |
|
|
|
|
currentProcessingSearchValue = null; |
|
|
|
|
lastSearchValue = null; |
|
|
|
|
@ -421,6 +452,10 @@
@@ -421,6 +452,10 @@
|
|
|
|
|
lastSearchValue = searchValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (searchType) { |
|
|
|
|
lastProcessedSearchType = searchType; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
isProcessingSearch = false; |
|
|
|
|
currentProcessingSearchValue = null; |
|
|
|
|
isWaitingForSearchResult = false; |
|
|
|
|
@ -476,18 +511,10 @@
@@ -476,18 +511,10 @@
|
|
|
|
|
while (retryCount < maxRetries) { |
|
|
|
|
// Check if we have any relays in the NDK pool |
|
|
|
|
if (ndk && ndk.pool && ndk.pool.relays && ndk.pool.relays.size > 0) { |
|
|
|
|
console.debug(`EventSearch: Found ${ndk.pool.relays.size} relays in NDK pool`); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Also check active relay stores as fallback |
|
|
|
|
if ($activeInboxRelays.length > 0 || $activeOutboxRelays.length > 0) { |
|
|
|
|
console.debug(`EventSearch: Found active relays - inbox: ${$activeInboxRelays.length}, outbox: ${$activeOutboxRelays.length}`); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
console.debug(`EventSearch: Waiting for relays... (attempt ${retryCount + 1}/${maxRetries})`); |
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 500)); |
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 500)); |
|
|
|
|
retryCount++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -499,18 +526,30 @@
@@ -499,18 +526,30 @@
|
|
|
|
|
poolRelayCount, |
|
|
|
|
inboxCount: $activeInboxRelays.length, |
|
|
|
|
outboxCount: $activeOutboxRelays.length, |
|
|
|
|
willUseAllRelays: poolRelayCount > 0 || $activeInboxRelays.length > 0 || $activeOutboxRelays.length > 0 |
|
|
|
|
willUseAllRelays: |
|
|
|
|
poolRelayCount > 0 || |
|
|
|
|
$activeInboxRelays.length > 0 || |
|
|
|
|
$activeOutboxRelays.length > 0, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// If we have any relays available, proceed with search |
|
|
|
|
if (poolRelayCount > 0 || $activeInboxRelays.length > 0 || $activeOutboxRelays.length > 0) { |
|
|
|
|
if ( |
|
|
|
|
poolRelayCount > 0 || |
|
|
|
|
$activeInboxRelays.length > 0 || |
|
|
|
|
$activeOutboxRelays.length > 0 |
|
|
|
|
) { |
|
|
|
|
console.log("EventSearch: Relays available, proceeding with search"); |
|
|
|
|
} else { |
|
|
|
|
console.warn("EventSearch: No relays detected, but proceeding with search - fallback relays will be used"); |
|
|
|
|
console.warn( |
|
|
|
|
"EventSearch: No relays detected, but proceeding with search - fallback relays will be used", |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async function performSubscriptionSearch(searchType: "d" | "t" | "n", searchTerm: string): Promise<void> { |
|
|
|
|
async function performSubscriptionSearch( |
|
|
|
|
searchType: "d" | "t" | "n", |
|
|
|
|
searchTerm: string, |
|
|
|
|
): Promise<void> { |
|
|
|
|
if (currentAbortController) { |
|
|
|
|
currentAbortController.abort(); |
|
|
|
|
} |
|
|
|
|
@ -547,11 +586,13 @@
@@ -547,11 +586,13 @@
|
|
|
|
|
|
|
|
|
|
const timeoutPromise = new Promise((_, reject) => { |
|
|
|
|
setTimeout(() => { |
|
|
|
|
reject(new Error("Search timeout: No results received within 30 seconds")); |
|
|
|
|
reject( |
|
|
|
|
new Error("Search timeout: No results received within 30 seconds"), |
|
|
|
|
); |
|
|
|
|
}, 30000); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
const result = await Promise.race([searchPromise, timeoutPromise]) as any; |
|
|
|
|
const result = (await Promise.race([searchPromise, timeoutPromise])) as any; |
|
|
|
|
console.log("EventSearch: Search completed:", result); |
|
|
|
|
|
|
|
|
|
onSearchResults( |
|
|
|
|
@ -565,7 +606,10 @@
@@ -565,7 +606,10 @@
|
|
|
|
|
false, // AI-NOTE: 2025-01-24 - Search is complete |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const totalCount = result.events.length + result.secondOrder.length + result.tTagEvents.length; |
|
|
|
|
const totalCount = |
|
|
|
|
result.events.length + |
|
|
|
|
result.secondOrder.length + |
|
|
|
|
result.tTagEvents.length; |
|
|
|
|
localError = null; |
|
|
|
|
|
|
|
|
|
cleanupSearch(); |
|
|
|
|
@ -590,10 +634,15 @@
@@ -590,10 +634,15 @@
|
|
|
|
|
console.error("EventSearch: Search failed:", error); |
|
|
|
|
|
|
|
|
|
if (error instanceof Error) { |
|
|
|
|
if (error.message.includes("timeout") || error.message.includes("connection")) { |
|
|
|
|
localError = "Search timed out. The relays may be temporarily unavailable. Please try again."; |
|
|
|
|
if ( |
|
|
|
|
error.message.includes("timeout") || |
|
|
|
|
error.message.includes("connection") |
|
|
|
|
) { |
|
|
|
|
localError = |
|
|
|
|
"Search timed out. The relays may be temporarily unavailable. Please try again."; |
|
|
|
|
} else if (error.message.includes("NDK not initialized")) { |
|
|
|
|
localError = "Nostr client not initialized. Please refresh the page and try again."; |
|
|
|
|
localError = |
|
|
|
|
"Nostr client not initialized. Please refresh the page and try again."; |
|
|
|
|
} else { |
|
|
|
|
localError = `Search failed: ${error.message}`; |
|
|
|
|
} |
|
|
|
|
@ -610,6 +659,10 @@
@@ -610,6 +659,10 @@
|
|
|
|
|
if (searchValue) { |
|
|
|
|
lastProcessedSearchValue = searchValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (searchType) { |
|
|
|
|
lastProcessedSearchType = searchType; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// AI-NOTE: 2025-01-24 - Background profile search is now handled by centralized searchProfiles function |
|
|
|
|
|