|
|
|
@ -2,13 +2,14 @@ |
|
|
|
import { goto } from "$app/navigation"; |
|
|
|
import { goto } from "$app/navigation"; |
|
|
|
import { Input, Button } from "flowbite-svelte"; |
|
|
|
import { Input, Button } from "flowbite-svelte"; |
|
|
|
import { Spinner } from "flowbite-svelte"; |
|
|
|
import { Spinner } from "flowbite-svelte"; |
|
|
|
import type { NDKEvent } from "$lib/utils/nostrUtils"; |
|
|
|
import { NDKEvent } from "@nostr-dev-kit/ndk"; |
|
|
|
import { |
|
|
|
import { |
|
|
|
searchEvent, |
|
|
|
searchEvent, |
|
|
|
searchBySubscription, |
|
|
|
searchBySubscription, |
|
|
|
searchNip05, |
|
|
|
searchNip05, |
|
|
|
} from "$lib/utils/search_utility"; |
|
|
|
} from "$lib/utils/search_utility"; |
|
|
|
import { neventEncode, naddrEncode, nprofileEncode } from "$lib/utils"; |
|
|
|
import { neventEncode, naddrEncode, nprofileEncode } from "$lib/utils"; |
|
|
|
|
|
|
|
import { nip19 } from "nostr-tools"; |
|
|
|
import { |
|
|
|
import { |
|
|
|
activeInboxRelays, |
|
|
|
activeInboxRelays, |
|
|
|
activeOutboxRelays, |
|
|
|
activeOutboxRelays, |
|
|
|
@ -113,6 +114,59 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function handleProfileSearch(query: string) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
console.log("EventSearch: Starting profile search for:", query); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Use the profile search service to find the profile |
|
|
|
|
|
|
|
const { searchProfiles } = await import("$lib/utils/profile_search"); |
|
|
|
|
|
|
|
const result = await searchProfiles(query, ndk); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result.profiles && result.profiles.length > 0) { |
|
|
|
|
|
|
|
// Get the npub from the profile, or use the original query if profile doesn't have pubkey |
|
|
|
|
|
|
|
let npub = result.profiles[0].pubkey || query; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Convert npub to hex pubkey |
|
|
|
|
|
|
|
let hexPubkey = ""; |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
if (npub.startsWith('npub')) { |
|
|
|
|
|
|
|
const decoded = nip19.decode(npub); |
|
|
|
|
|
|
|
if (decoded.type === 'npub') { |
|
|
|
|
|
|
|
hexPubkey = decoded.data; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
hexPubkey = npub; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
|
|
console.warn("Failed to decode npub to hex:", error); |
|
|
|
|
|
|
|
cleanupSearch(); |
|
|
|
|
|
|
|
updateSearchState(false, true, 0, "profile"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fetch the actual profile event from relays |
|
|
|
|
|
|
|
const profileEvent = await ndk.fetchEvent({ |
|
|
|
|
|
|
|
kinds: [0], |
|
|
|
|
|
|
|
authors: [hexPubkey], |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (profileEvent) { |
|
|
|
|
|
|
|
handleFoundEvent(profileEvent); |
|
|
|
|
|
|
|
updateSearchState(false, true, 1, "profile"); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
cleanupSearch(); |
|
|
|
|
|
|
|
updateSearchState(false, true, 0, "profile"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
console.log("EventSearch: No profile found for:", query); |
|
|
|
|
|
|
|
cleanupSearch(); |
|
|
|
|
|
|
|
updateSearchState(false, true, 0, "profile"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
|
|
handleSearchError(error, "Profile lookup failed"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async function handleEventSearch(query: string) { |
|
|
|
async function handleEventSearch(query: string) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
const foundEvent = await searchEvent(query, ndk); |
|
|
|
const foundEvent = await searchEvent(query, ndk); |
|
|
|
@ -252,9 +306,22 @@ |
|
|
|
return { type: "nip05", term: query }; |
|
|
|
return { type: "nip05", term: query }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// AI-NOTE: Detect Nostr identifiers (npub, nevent, naddr, nprofile) |
|
|
|
|
|
|
|
const trimmedQuery = query.trim(); |
|
|
|
|
|
|
|
if (trimmedQuery.startsWith("npub") || trimmedQuery.startsWith("nprofile")) { |
|
|
|
|
|
|
|
return { type: "profile", term: trimmedQuery }; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (trimmedQuery.startsWith("nevent") || trimmedQuery.startsWith("note")) { |
|
|
|
|
|
|
|
return { type: "event", term: trimmedQuery }; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (trimmedQuery.startsWith("naddr")) { |
|
|
|
|
|
|
|
return { type: "event", term: trimmedQuery }; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// AI-NOTE: Detect hex IDs (64-character hex strings with no spaces) |
|
|
|
// AI-NOTE: Detect hex IDs (64-character hex strings with no spaces) |
|
|
|
// These are likely event IDs and should be searched as events |
|
|
|
// These are likely event IDs and should be searched as events |
|
|
|
const trimmedQuery = query.trim(); |
|
|
|
|
|
|
|
if (trimmedQuery && isEventId(trimmedQuery)) { |
|
|
|
if (trimmedQuery && isEventId(trimmedQuery)) { |
|
|
|
return { type: "event", term: trimmedQuery }; |
|
|
|
return { type: "event", term: trimmedQuery }; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -262,12 +329,7 @@ |
|
|
|
// AI-NOTE: Treat plain text searches as generic searches by default |
|
|
|
// AI-NOTE: Treat plain text searches as generic searches by default |
|
|
|
// This allows for flexible searching without assuming it's always a profile search |
|
|
|
// This allows for flexible searching without assuming it's always a profile search |
|
|
|
// Users can still use n: prefix for explicit name/profile searches |
|
|
|
// Users can still use n: prefix for explicit name/profile searches |
|
|
|
if ( |
|
|
|
if (trimmedQuery) { |
|
|
|
trimmedQuery && |
|
|
|
|
|
|
|
!trimmedQuery.startsWith("nevent") && |
|
|
|
|
|
|
|
!trimmedQuery.startsWith("npub") && |
|
|
|
|
|
|
|
!trimmedQuery.startsWith("naddr") |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
return null; // Let handleSearchEvent treat this as a generic search |
|
|
|
return null; // Let handleSearchEvent treat this as a generic search |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -293,7 +355,13 @@ |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (type === "event") { |
|
|
|
if (type === "profile") { |
|
|
|
|
|
|
|
console.log("EventSearch: Processing profile search:", term); |
|
|
|
|
|
|
|
await handleProfileSearch(term); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (type === "event" || type === "id") { |
|
|
|
console.log("EventSearch: Processing event ID search:", term); |
|
|
|
console.log("EventSearch: Processing event ID search:", term); |
|
|
|
// URL navigation is now handled in handleSearchEvent |
|
|
|
// URL navigation is now handled in handleSearchEvent |
|
|
|
await handleEventSearch(term); |
|
|
|
await handleEventSearch(term); |
|
|
|
@ -423,6 +491,8 @@ |
|
|
|
handleSearchBySubscription("t", activeSearchValue); |
|
|
|
handleSearchBySubscription("t", activeSearchValue); |
|
|
|
} else if (activeSearchType === "n") { |
|
|
|
} else if (activeSearchType === "n") { |
|
|
|
handleSearchBySubscription("n", activeSearchValue); |
|
|
|
handleSearchBySubscription("n", activeSearchValue); |
|
|
|
|
|
|
|
} else if (activeSearchType === "id") { |
|
|
|
|
|
|
|
handleEventSearch(activeSearchValue); |
|
|
|
} |
|
|
|
} |
|
|
|
// Note: "q" (generic) searches are not processed here since they're |
|
|
|
// Note: "q" (generic) searches are not processed here since they're |
|
|
|
// unstructured queries that don't require actual search execution |
|
|
|
// unstructured queries that don't require actual search execution |
|
|
|
|