Browse Source

Fix a bug where search type failed to change as expected

Also add a placeholder generic search query to be used with future fuzzy/full-text/semantic search capabilities.
master
buttercat1791 7 months ago
parent
commit
fef9ddaba8
  1. 203
      src/lib/components/EventSearch.svelte
  2. 15
      src/routes/events/+page.svelte

203
src/lib/components/EventSearch.svelte

@ -62,6 +62,20 @@ @@ -62,6 +62,20 @@
let searchResultType = $state<string | null>(null);
let isResetting = $state(false);
// Track current search type for internal logic
let currentSearchType = $state<SearchType | null>(searchType);
let currentSearchValue = $state<string | null>(searchValue);
// Sync internal state with props when they change externally
$effect(() => {
if (searchType !== undefined) {
currentSearchType = searchType;
}
if (searchValue !== undefined) {
currentSearchValue = searchValue;
}
});
// Internal state for cleanup
let activeSub: any = null;
let currentAbortController: AbortController | null = null;
@ -83,7 +97,7 @@ @@ -83,7 +97,7 @@
// Debounced search timeout
let searchTimeout: ReturnType<typeof setTimeout> | null = null;
// AI-NOTE: 2025-01-24 - Core search handlers extracted for better organization
// AI-NOTE: Core search handlers extracted for better organization
async function handleNip05Search(query: string) {
try {
const foundEvent = await searchNip05(query, ndk);
@ -145,45 +159,46 @@ @@ -145,45 +159,46 @@
// Update URL with search query for all search types
if (clearInput) {
const searchType = getSearchType(query);
if (searchType) {
const { type, term } = searchType;
const detectedSearchType = getSearchType(query);
if (detectedSearchType) {
const { type, term } = detectedSearchType;
const encoded = encodeURIComponent(term);
let newUrl = "";
if (type === "d") {
goto(`?d=${encoded}`, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
newUrl = `?d=${encoded}`;
currentSearchType = "d";
currentSearchValue = term;
} else if (type === "t") {
goto(`?t=${encoded}`, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
newUrl = `?t=${encoded}`;
currentSearchType = "t";
currentSearchValue = term;
} else if (type === "n") {
goto(`?n=${encoded}`, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
newUrl = `?n=${encoded}`;
currentSearchType = "n";
currentSearchValue = term;
} else if (type === "nip05") {
goto(`?q=${encodeURIComponent(query)}`, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
newUrl = `?q=${encodeURIComponent(query)}`;
currentSearchType = "q";
currentSearchValue = query;
} else if (type === "event") {
goto(`?id=${encoded}`, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
newUrl = `?id=${encoded}`;
currentSearchType = "id";
currentSearchValue = term;
}
goto(newUrl, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
} else {
// No specific search type detected, treat as general search
const encoded = encodeURIComponent(query);
goto(`?q=${encoded}`, {
const newUrl = `?q=${encoded}`;
currentSearchType = "q";
currentSearchValue = query;
goto(newUrl, {
replaceState: false,
keepFocus: true,
noScroll: true,
@ -198,12 +213,23 @@ @@ -198,12 +213,23 @@
return;
}
// AI-NOTE: 2025-01-24 - If no specific search type is detected, treat as event ID search
// URL navigation is now handled in the URL update logic above
await handleEventSearch(query);
// AI-NOTE: If no specific search type is detected, check if it could be an event ID
const trimmedQuery = query.trim();
if (trimmedQuery && isEventId(trimmedQuery)) {
// Looks like an event ID, treat as event search
await handleEventSearch(query);
} else {
// AI-NOTE: Doesn't look like an event ID, treat as generic search
// The URL update logic above should have set currentSearchType = "q"
// For generic "q" searches, we don't perform actual searches since they're
// unstructured queries. We just update the URL for shareability and show completion
// TODO: Handle generic "q" searches with a semantic search capability (when available).
updateSearchState(false, true, 0, "q");
}
}
// AI-NOTE: 2025-01-24 - Helper functions for better code organization
// AI-NOTE: Helper functions for better code organization
function getSearchType(query: string): { type: string; term: string } | null {
const lowerQuery = query.toLowerCase();
@ -226,22 +252,23 @@ @@ -226,22 +252,23 @@
return { type: "nip05", term: query };
}
// AI-NOTE: 2025-01-24 - 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
const trimmedQuery = query.trim();
if (trimmedQuery && isEventId(trimmedQuery)) {
return { type: "event", term: trimmedQuery };
}
// 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
// AI-NOTE: Treat plain text searches as generic searches by default
// This allows for flexible searching without assuming it's always a profile search
// Users can still use n: prefix for explicit name/profile searches
if (
trimmedQuery &&
!trimmedQuery.startsWith("nevent") &&
!trimmedQuery.startsWith("npub") &&
!trimmedQuery.startsWith("naddr")
) {
return { type: "n", term: trimmedQuery };
return null; // Let handleSearchEvent treat this as a generic search
}
return null;
@ -310,15 +337,19 @@ @@ -310,15 +337,19 @@
return;
}
if (searchValue && searchType) {
if (searchType === "d") {
searchQuery = `d:${searchValue}`;
} else if (searchType === "t") {
searchQuery = `t:${searchValue}`;
} else if (searchType === "n") {
searchQuery = `n:${searchValue}`;
// Use internal state if set (from user actions), otherwise use props
const activeSearchType = currentSearchType ?? searchType;
const activeSearchValue = currentSearchValue ?? searchValue;
if (activeSearchValue && activeSearchType) {
if (activeSearchType === "d") {
searchQuery = `d:${activeSearchValue}`;
} else if (activeSearchType === "t") {
searchQuery = `t:${activeSearchValue}`;
} else if (activeSearchType === "n") {
searchQuery = `n:${activeSearchValue}`;
} else {
searchQuery = searchValue;
searchQuery = activeSearchValue;
}
} else if (!searchQuery) {
searchQuery = "";
@ -368,30 +399,33 @@ @@ -368,30 +399,33 @@
});
$effect(() => {
// Use internal state if set (from user actions), otherwise use props
const activeSearchType = currentSearchType ?? searchType;
const activeSearchValue = currentSearchValue ?? searchValue;
if (
searchValue &&
searchType &&
activeSearchValue &&
activeSearchType &&
!searching &&
!isResetting &&
(searchType !== lastProcessedSearchType ||
searchValue !== lastProcessedSearchValue)
(activeSearchType !== lastProcessedSearchType ||
activeSearchValue !== lastProcessedSearchValue)
) {
console.log("EventSearch: Processing search:", {
searchType,
searchValue,
});
lastProcessedSearchType = searchType;
lastProcessedSearchValue = searchValue;
lastProcessedSearchType = activeSearchType;
lastProcessedSearchValue = activeSearchValue;
setTimeout(() => {
if (!searching && !isResetting) {
if (searchType === "d") {
handleSearchBySubscription("d", searchValue);
} else if (searchType === "t") {
handleSearchBySubscription("t", searchValue);
} else if (searchType === "n") {
handleSearchBySubscription("n", searchValue);
if (activeSearchType === "d") {
handleSearchBySubscription("d", activeSearchValue);
} else if (activeSearchType === "t") {
handleSearchBySubscription("t", activeSearchValue);
} else if (activeSearchType === "n") {
handleSearchBySubscription("n", activeSearchValue);
}
// Note: "q" (generic) searches are not processed here since they're
// unstructured queries that don't require actual search execution
}
}, 100);
}
@ -468,6 +502,9 @@ @@ -468,6 +502,9 @@
isProcessingSearch = false;
currentProcessingSearchValue = null;
lastSearchValue = null;
// Reset internal search state
currentSearchType = null;
currentSearchValue = null;
updateSearchState(false, false, null, null);
cleanupSearch();
@ -510,16 +547,7 @@ @@ -510,16 +547,7 @@
onEventFound(event);
}
function navigateToSearch(query: string, paramName: string) {
const encoded = encodeURIComponent(query);
goto(`?${paramName}=${encoded}`, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
}
// AI-NOTE: 2025-01-24 - Main subscription search handler with improved error handling
// AI-NOTE: Main subscription search handler with improved error handling
async function handleSearchBySubscription(
searchType: "d" | "t" | "n",
searchTerm: string,
@ -529,7 +557,7 @@ @@ -529,7 +557,7 @@
searchTerm,
});
// AI-NOTE: 2025-01-24 - Profile search caching is now handled by centralized searchProfiles function
// AI-NOTE: Profile search caching is now handled by centralized searchProfiles function
// No need for separate caching logic here as it's handled in profile_search.ts
isResetting = false;
@ -545,7 +573,7 @@ @@ -545,7 +573,7 @@
}
}
// AI-NOTE: 2025-01-24 - Profile search is now handled by centralized searchProfiles function
// AI-NOTE: Profile search is now handled by centralized searchProfiles function
// These functions are no longer needed as profile searches go through subscription_search.ts
// which delegates to the centralized profile_search.ts
@ -553,7 +581,7 @@ @@ -553,7 +581,7 @@
let retryCount = 0;
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: Wait for any relays to be available, not just specific types
// This ensures searches can proceed even if some relay types are not available
while (retryCount < maxRetries) {
// Check if we have any relays in the NDK pool
@ -565,7 +593,7 @@ @@ -565,7 +593,7 @@
retryCount++;
}
// AI-NOTE: 2025-01-24 - Don't fail if no relays are available, let the search functions handle fallbacks
// AI-NOTE: 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
const poolRelayCount = ndk?.pool?.relays?.size || 0;
@ -616,8 +644,8 @@ @@ -616,8 +644,8 @@
updatedResult.eventIds,
updatedResult.addresses,
updatedResult.searchType,
searchValue || updatedResult.searchTerm, // AI-NOTE: 2025-01-24 - Use original search value for display
false, // AI-NOTE: 2025-01-24 - Second-order update means search is complete
searchValue || updatedResult.searchTerm, // AI-NOTE: Use original search value for display
false, // AI-NOTE: Second-order update means search is complete
);
},
onSubscriptionCreated: (sub) => {
@ -649,8 +677,8 @@ @@ -649,8 +677,8 @@
result.eventIds,
result.addresses,
result.searchType,
searchValue || result.searchTerm, // AI-NOTE: 2025-01-24 - Use original search value for display
false, // AI-NOTE: 2025-01-24 - Search is complete
searchValue || result.searchTerm, // AI-NOTE: Use original search value for display
false, // AI-NOTE: Search is complete
);
const totalCount =
@ -738,6 +766,9 @@ @@ -738,6 +766,9 @@
currentProcessingSearchValue = null;
lastSearchValue = null;
isWaitingForSearchResult = false;
// Reset internal search state
currentSearchType = null;
currentSearchValue = null;
if (searchTimeout) {
clearTimeout(searchTimeout);
@ -770,18 +801,6 @@ @@ -770,18 +801,6 @@
? `Search completed. Found 1 ${typeLabel}.`
: `Search completed. Found ${searchResultCount} ${countLabel}.`;
}
function getNeventUrl(event: NDKEvent): string {
return neventEncode(event, $activeInboxRelays);
}
function getNaddrUrl(event: NDKEvent): string {
return naddrEncode(event, $activeInboxRelays);
}
function getNprofileUrl(pubkey: string): string {
return nprofileEncode(pubkey, $activeInboxRelays);
}
</script>
<div class="flex flex-col space-y-6">

15
src/routes/events/+page.svelte

@ -174,12 +174,15 @@ @@ -174,12 +174,15 @@
// Use Svelte 5 idiomatic effect to update searchValue and searchType based on URL parameters
$effect(() => {
const url = $page.url.searchParams;
const idParam = url.get("id");
const dParam = url.get("d");
const tParam = url.get("t");
const nParam = url.get("n");
const qParam = url.get("q");
// Ensure we have the full URL object to trigger reactivity
const url = $page.url;
const searchParams = url.searchParams;
const idParam = searchParams.get("id");
const dParam = searchParams.get("d");
const tParam = searchParams.get("t");
const nParam = searchParams.get("n");
const qParam = searchParams.get("q");
if (idParam) {
searchValue = idParam;

Loading…
Cancel
Save