Browse Source

Consolidate some state values in search

master
buttercat1791 7 months ago
parent
commit
8b61566322
  1. 201
      src/lib/components/EventSearch.svelte
  2. 1
      src/lib/models/search_type.d.ts
  3. 1135
      src/routes/events/+page.svelte

201
src/lib/components/EventSearch.svelte

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
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 type { NDKEvent } from "$lib/utils/nostrUtils";
@ -8,19 +8,22 @@
searchBySubscription, searchBySubscription,
searchNip05, searchNip05,
} from "$lib/utils/search_utility"; } from "$lib/utils/search_utility";
import type { SearchCallbacks } from "$lib/utils/search_types";
import { neventEncode, naddrEncode, nprofileEncode } from "$lib/utils"; 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 { getMatchingTags, toNpub } from "$lib/utils/nostrUtils";
import { isEventId } from "$lib/utils/nostr_identifiers"; 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 // Props definition
let { let {
loading, loading,
error, error,
searchValue, searchValue,
dTagValue, searchType,
onEventFound, onEventFound,
onSearchResults, onSearchResults,
event, event,
@ -30,7 +33,7 @@
loading: boolean; loading: boolean;
error: string | null; error: string | null;
searchValue: string | null; searchValue: string | null;
dTagValue: string | null; searchType: SearchType | null;
onEventFound: (event: NDKEvent) => void; onEventFound: (event: NDKEvent) => void;
onSearchResults: ( onSearchResults: (
firstOrder: NDKEvent[], firstOrder: NDKEvent[],
@ -70,7 +73,7 @@
// Track last processed values to prevent loops // Track last processed values to prevent loops
let lastProcessedSearchValue = $state<string | null>(null); let lastProcessedSearchValue = $state<string | null>(null);
let lastProcessedDTagValue = $state<string | null>(null); let lastProcessedSearchType = $state<SearchType | null>(null);
let isProcessingSearch = $state(false); let isProcessingSearch = $state(false);
let currentProcessingSearchValue = $state<string | null>(null); let currentProcessingSearchValue = $state<string | null>(null);
let lastSearchValue = $state<string | null>(null); let lastSearchValue = $state<string | null>(null);
@ -110,7 +113,10 @@
updateSearchState(false, true, 1, "event"); updateSearchState(false, true, 1, "event");
} }
} catch (err) { } 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 @@
isResetting = false; isResetting = false;
isUserEditing = false; isUserEditing = false;
const query = (queryOverride !== undefined ? queryOverride || "" : searchQuery || "").trim(); const query = (
queryOverride !== undefined ? queryOverride || "" : searchQuery || ""
).trim();
if (!query) { if (!query) {
updateSearchState(false, false, null, null); updateSearchState(false, false, null, null);
return; return;
@ -152,67 +160,72 @@
// AI-NOTE: 2025-01-24 - Helper functions for better code organization // AI-NOTE: 2025-01-24 - Helper functions for better code organization
function getSearchType(query: string): { type: string; term: string } | null { function getSearchType(query: string): { type: string; term: string } | null {
const lowerQuery = query.toLowerCase(); const lowerQuery = query.toLowerCase();
if (lowerQuery.startsWith("d:")) { if (lowerQuery.startsWith("d:")) {
const dTag = query.slice(2).trim().toLowerCase(); const dTag = query.slice(2).trim().toLowerCase();
return dTag ? { type: "d", term: dTag } : null; return dTag ? { type: "d", term: dTag } : null;
} }
if (lowerQuery.startsWith("t:")) { if (lowerQuery.startsWith("t:")) {
const searchTerm = query.slice(2).trim(); const searchTerm = query.slice(2).trim();
return searchTerm ? { type: "t", term: searchTerm } : null; return searchTerm ? { type: "t", term: searchTerm } : null;
} }
if (lowerQuery.startsWith("n:")) { if (lowerQuery.startsWith("n:")) {
const searchTerm = query.slice(2).trim(); const searchTerm = query.slice(2).trim();
return searchTerm ? { type: "n", term: searchTerm } : null; return searchTerm ? { type: "n", term: searchTerm } : null;
} }
if (query.includes("@")) { if (query.includes("@")) {
return { type: "nip05", term: query }; return { type: "nip05", term: query };
} }
// AI-NOTE: 2025-01-24 - Detect hex IDs (64-character hex strings with no spaces) // AI-NOTE: 2025-01-24 - 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(); const trimmedQuery = query.trim();
if (trimmedQuery && isEventId(trimmedQuery)) { if (trimmedQuery && isEventId(trimmedQuery)) {
return { type: "event", term: trimmedQuery }; return { type: "event", term: trimmedQuery };
} }
// AI-NOTE: 2025-01-24 - Treat plain text searches as profile searches by default // 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 // 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 }; return { type: "n", term: trimmedQuery };
} }
return null; return null;
} }
async function handleSearchByType( async function handleSearchByType(
searchType: { type: string; term: string }, searchType: { type: string; term: string },
query: string, query: string,
clearInput: boolean clearInput: boolean,
) { ) {
const { type, term } = searchType; const { type, term } = searchType;
if (type === "d") { if (type === "d") {
console.log("EventSearch: Processing d-tag search:", term); console.log("EventSearch: Processing d-tag search:", term);
navigateToSearch(term, "d"); navigateToSearch(term, "d");
updateSearchState(false, false, null, null); updateSearchState(false, false, null, null);
return; return;
} }
if (type === "nip05") { if (type === "nip05") {
await handleNip05Search(term); await handleNip05Search(term);
return; return;
} }
if (type === "event") { if (type === "event") {
console.log("EventSearch: Processing event ID search:", term); console.log("EventSearch: Processing event ID search:", term);
await handleEventSearch(term); await handleEventSearch(term);
return; return;
} }
if (type === "t" || type === "n") { if (type === "t" || type === "n") {
await handleSearchBySubscription(type as "t" | "n", term); await handleSearchBySubscription(type as "t" | "n", term);
return; return;
@ -237,7 +250,7 @@
} }
activeSub = null; activeSub = null;
} }
if (currentAbortController) { if (currentAbortController) {
currentAbortController.abort(); currentAbortController.abort();
currentAbortController = null; currentAbortController = null;
@ -250,10 +263,16 @@
return; return;
} }
if (dTagValue) { if (searchValue && searchType) {
searchQuery = `d:${dTagValue}`; if (searchType === "d") {
} else if (searchValue) { searchQuery = `d:${searchValue}`;
searchQuery = searchValue; } else if (searchType === "t") {
searchQuery = `t:${searchValue}`;
} else if (searchType === "n") {
searchQuery = `n:${searchValue}`;
} else {
searchQuery = searchValue;
}
} else if (!searchQuery) { } else if (!searchQuery) {
searchQuery = ""; searchQuery = "";
} }
@ -282,7 +301,7 @@
if (searchTimeout) { if (searchTimeout) {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
} }
searchTimeout = setTimeout(() => { searchTimeout = setTimeout(() => {
isProcessingSearch = true; isProcessingSearch = true;
isWaitingForSearchResult = true; isWaitingForSearchResult = true;
@ -303,17 +322,29 @@
$effect(() => { $effect(() => {
if ( if (
dTagValue && searchValue &&
searchType &&
!searching && !searching &&
!isResetting && !isResetting &&
dTagValue !== lastProcessedDTagValue (searchType !== lastProcessedSearchType ||
searchValue !== lastProcessedSearchValue)
) { ) {
console.log("EventSearch: Processing dTagValue:", dTagValue); console.log("EventSearch: Processing search:", {
lastProcessedDTagValue = dTagValue; searchType,
searchValue,
});
lastProcessedSearchType = searchType;
lastProcessedSearchValue = searchValue;
setTimeout(() => { setTimeout(() => {
if (!searching && !isResetting) { 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); }, 100);
} }
@ -336,13 +367,13 @@
try { try {
currentNevent = neventEncode(event, $activeInboxRelays); currentNevent = neventEncode(event, $activeInboxRelays);
} catch {} } catch {}
try { try {
currentNaddr = getMatchingTags(event, "d")[0]?.[1] currentNaddr = getMatchingTags(event, "d")[0]?.[1]
? naddrEncode(event, $activeInboxRelays) ? naddrEncode(event, $activeInboxRelays)
: null; : null;
} catch {} } catch {}
try { try {
currentNpub = event.kind === 0 ? toNpub(event.pubkey) : null; currentNpub = event.kind === 0 ? toNpub(event.pubkey) : null;
} catch {} } catch {}
@ -386,7 +417,7 @@
foundEvent = null; foundEvent = null;
localError = null; localError = null;
lastProcessedSearchValue = null; lastProcessedSearchValue = null;
lastProcessedDTagValue = null; lastProcessedSearchType = null;
isProcessingSearch = false; isProcessingSearch = false;
currentProcessingSearchValue = null; currentProcessingSearchValue = null;
lastSearchValue = null; lastSearchValue = null;
@ -421,6 +452,10 @@
lastSearchValue = searchValue; lastSearchValue = searchValue;
} }
if (searchType) {
lastProcessedSearchType = searchType;
}
isProcessingSearch = false; isProcessingSearch = false;
currentProcessingSearchValue = null; currentProcessingSearchValue = null;
isWaitingForSearchResult = false; isWaitingForSearchResult = false;
@ -453,9 +488,9 @@
isResetting = false; isResetting = false;
localError = null; localError = null;
updateSearchState(true, false); updateSearchState(true, false);
await waitForRelays(); await waitForRelays();
try { try {
await performSubscriptionSearch(searchType, searchTerm); await performSubscriptionSearch(searchType, searchTerm);
} catch (error) { } catch (error) {
@ -470,52 +505,56 @@
async function waitForRelays(): Promise<void> { async function waitForRelays(): Promise<void> {
let retryCount = 0; let retryCount = 0;
const maxRetries = 10; // Reduced retry count since we'll use all available relays const maxRetries = 10; // Reduced retry count since we'll use all available relays
// AI-NOTE: 2025-01-24 - Wait for any relays to be available, not just specific types // AI-NOTE: 2025-01-24 - Wait for any relays to be available, not just specific types
// This ensures searches can proceed even if some relay types are not available // This ensures searches can proceed even if some relay types are not available
while (retryCount < maxRetries) { while (retryCount < maxRetries) {
// Check if we have any relays in the NDK pool // Check if we have any relays in the NDK pool
if (ndk && ndk.pool && ndk.pool.relays && ndk.pool.relays.size > 0) { 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; break;
} }
// Also check active relay stores as fallback await new Promise((resolve) => setTimeout(resolve, 500));
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));
retryCount++; retryCount++;
} }
// AI-NOTE: 2025-01-24 - Don't fail if no relays are available, let the search functions handle fallbacks // AI-NOTE: 2025-01-24 - Don't fail if no relays are available, let the search functions handle fallbacks
// The search functions will use all available relays including fallback relays // The search functions will use all available relays including fallback relays
const poolRelayCount = ndk?.pool?.relays?.size || 0; const poolRelayCount = ndk?.pool?.relays?.size || 0;
console.log("EventSearch: Relay status for search:", { console.log("EventSearch: Relay status for search:", {
poolRelayCount, poolRelayCount,
inboxCount: $activeInboxRelays.length, inboxCount: $activeInboxRelays.length,
outboxCount: $activeOutboxRelays.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 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"); console.log("EventSearch: Relays available, proceeding with search");
} else { } 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) { if (currentAbortController) {
currentAbortController.abort(); currentAbortController.abort();
} }
currentAbortController = new AbortController(); currentAbortController = new AbortController();
const searchPromise = searchBySubscription( const searchPromise = searchBySubscription(
searchType, searchType,
searchTerm, searchTerm,
@ -544,16 +583,18 @@
}, },
currentAbortController.signal, currentAbortController.signal,
); );
const timeoutPromise = new Promise((_, reject) => { const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => { setTimeout(() => {
reject(new Error("Search timeout: No results received within 30 seconds")); reject(
new Error("Search timeout: No results received within 30 seconds"),
);
}, 30000); }, 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); console.log("EventSearch: Search completed:", result);
onSearchResults( onSearchResults(
result.events, result.events,
result.secondOrder, result.secondOrder,
@ -564,16 +605,19 @@
searchValue || result.searchTerm, // AI-NOTE: 2025-01-24 - Use original search value for display searchValue || result.searchTerm, // AI-NOTE: 2025-01-24 - Use original search value for display
false, // AI-NOTE: 2025-01-24 - Search is complete 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; localError = null;
cleanupSearch(); cleanupSearch();
updateSearchState(false, true, totalCount, searchType); updateSearchState(false, true, totalCount, searchType);
isProcessingSearch = false; isProcessingSearch = false;
currentProcessingSearchValue = null; currentProcessingSearchValue = null;
isWaitingForSearchResult = false; isWaitingForSearchResult = false;
if (searchValue) { if (searchValue) {
lastProcessedSearchValue = searchValue; lastProcessedSearchValue = searchValue;
} }
@ -586,30 +630,39 @@
isWaitingForSearchResult = false; isWaitingForSearchResult = false;
return; return;
} }
console.error("EventSearch: Search failed:", error); console.error("EventSearch: Search failed:", error);
if (error instanceof Error) { if (error instanceof Error) {
if (error.message.includes("timeout") || error.message.includes("connection")) { if (
localError = "Search timed out. The relays may be temporarily unavailable. Please try again."; 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")) { } 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 { } else {
localError = `Search failed: ${error.message}`; localError = `Search failed: ${error.message}`;
} }
} else { } else {
localError = "Search failed"; localError = "Search failed";
} }
cleanupSearch(); cleanupSearch();
updateSearchState(false, false, null, null); updateSearchState(false, false, null, null);
isProcessingSearch = false; isProcessingSearch = false;
currentProcessingSearchValue = null; currentProcessingSearchValue = null;
isWaitingForSearchResult = false; isWaitingForSearchResult = false;
if (searchValue) { if (searchValue) {
lastProcessedSearchValue = searchValue; lastProcessedSearchValue = searchValue;
} }
if (searchType) {
lastProcessedSearchType = searchType;
}
} }
// AI-NOTE: 2025-01-24 - Background profile search is now handled by centralized searchProfiles function // AI-NOTE: 2025-01-24 - Background profile search is now handled by centralized searchProfiles function

1
src/lib/models/search_type.d.ts vendored

@ -0,0 +1 @@
export type SearchType = "id" | "d" | "t" | "n";

1135
src/routes/events/+page.svelte

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save