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 @@
let searchResultType = $state<string | null>(null); let searchResultType = $state<string | null>(null);
let isResetting = $state(false); 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 // Internal state for cleanup
let activeSub: any = null; let activeSub: any = null;
let currentAbortController: AbortController | null = null; let currentAbortController: AbortController | null = null;
@ -83,7 +97,7 @@
// Debounced search timeout // Debounced search timeout
let searchTimeout: ReturnType<typeof setTimeout> | null = null; 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) { async function handleNip05Search(query: string) {
try { try {
const foundEvent = await searchNip05(query, ndk); const foundEvent = await searchNip05(query, ndk);
@ -145,45 +159,46 @@
// Update URL with search query for all search types // Update URL with search query for all search types
if (clearInput) { if (clearInput) {
const searchType = getSearchType(query); const detectedSearchType = getSearchType(query);
if (searchType) { if (detectedSearchType) {
const { type, term } = searchType; const { type, term } = detectedSearchType;
const encoded = encodeURIComponent(term); const encoded = encodeURIComponent(term);
let newUrl = "";
if (type === "d") { if (type === "d") {
goto(`?d=${encoded}`, { newUrl = `?d=${encoded}`;
replaceState: false, currentSearchType = "d";
keepFocus: true, currentSearchValue = term;
noScroll: true,
});
} else if (type === "t") { } else if (type === "t") {
goto(`?t=${encoded}`, { newUrl = `?t=${encoded}`;
replaceState: false, currentSearchType = "t";
keepFocus: true, currentSearchValue = term;
noScroll: true,
});
} else if (type === "n") { } else if (type === "n") {
goto(`?n=${encoded}`, { newUrl = `?n=${encoded}`;
replaceState: false, currentSearchType = "n";
keepFocus: true, currentSearchValue = term;
noScroll: true,
});
} else if (type === "nip05") { } else if (type === "nip05") {
goto(`?q=${encodeURIComponent(query)}`, { newUrl = `?q=${encodeURIComponent(query)}`;
replaceState: false, currentSearchType = "q";
keepFocus: true, currentSearchValue = query;
noScroll: true,
});
} else if (type === "event") { } else if (type === "event") {
goto(`?id=${encoded}`, { newUrl = `?id=${encoded}`;
replaceState: false, currentSearchType = "id";
keepFocus: true, currentSearchValue = term;
noScroll: true,
});
} }
goto(newUrl, {
replaceState: false,
keepFocus: true,
noScroll: true,
});
} else { } else {
// No specific search type detected, treat as general search // No specific search type detected, treat as general search
const encoded = encodeURIComponent(query); const encoded = encodeURIComponent(query);
goto(`?q=${encoded}`, { const newUrl = `?q=${encoded}`;
currentSearchType = "q";
currentSearchValue = query;
goto(newUrl, {
replaceState: false, replaceState: false,
keepFocus: true, keepFocus: true,
noScroll: true, noScroll: true,
@ -198,12 +213,23 @@
return; return;
} }
// AI-NOTE: 2025-01-24 - If no specific search type is detected, treat as event ID search // AI-NOTE: If no specific search type is detected, check if it could be an event ID
// URL navigation is now handled in the URL update logic above const trimmedQuery = query.trim();
await handleEventSearch(query); 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 { function getSearchType(query: string): { type: string; term: string } | null {
const lowerQuery = query.toLowerCase(); const lowerQuery = query.toLowerCase();
@ -226,22 +252,23 @@
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: 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: Treat plain text searches as generic searches by default
// This allows searching for names like "thebeave" or "TheBeave" without needing n: prefix // 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 ( if (
trimmedQuery && trimmedQuery &&
!trimmedQuery.startsWith("nevent") && !trimmedQuery.startsWith("nevent") &&
!trimmedQuery.startsWith("npub") && !trimmedQuery.startsWith("npub") &&
!trimmedQuery.startsWith("naddr") !trimmedQuery.startsWith("naddr")
) { ) {
return { type: "n", term: trimmedQuery }; return null; // Let handleSearchEvent treat this as a generic search
} }
return null; return null;
@ -310,15 +337,19 @@
return; return;
} }
if (searchValue && searchType) { // Use internal state if set (from user actions), otherwise use props
if (searchType === "d") { const activeSearchType = currentSearchType ?? searchType;
searchQuery = `d:${searchValue}`; const activeSearchValue = currentSearchValue ?? searchValue;
} else if (searchType === "t") {
searchQuery = `t:${searchValue}`; if (activeSearchValue && activeSearchType) {
} else if (searchType === "n") { if (activeSearchType === "d") {
searchQuery = `n:${searchValue}`; searchQuery = `d:${activeSearchValue}`;
} else if (activeSearchType === "t") {
searchQuery = `t:${activeSearchValue}`;
} else if (activeSearchType === "n") {
searchQuery = `n:${activeSearchValue}`;
} else { } else {
searchQuery = searchValue; searchQuery = activeSearchValue;
} }
} else if (!searchQuery) { } else if (!searchQuery) {
searchQuery = ""; searchQuery = "";
@ -368,30 +399,33 @@
}); });
$effect(() => { $effect(() => {
// Use internal state if set (from user actions), otherwise use props
const activeSearchType = currentSearchType ?? searchType;
const activeSearchValue = currentSearchValue ?? searchValue;
if ( if (
searchValue && activeSearchValue &&
searchType && activeSearchType &&
!searching && !searching &&
!isResetting && !isResetting &&
(searchType !== lastProcessedSearchType || (activeSearchType !== lastProcessedSearchType ||
searchValue !== lastProcessedSearchValue) activeSearchValue !== lastProcessedSearchValue)
) { ) {
console.log("EventSearch: Processing search:", {
searchType, lastProcessedSearchType = activeSearchType;
searchValue, lastProcessedSearchValue = activeSearchValue;
});
lastProcessedSearchType = searchType;
lastProcessedSearchValue = searchValue;
setTimeout(() => { setTimeout(() => {
if (!searching && !isResetting) { if (!searching && !isResetting) {
if (searchType === "d") { if (activeSearchType === "d") {
handleSearchBySubscription("d", searchValue); handleSearchBySubscription("d", activeSearchValue);
} else if (searchType === "t") { } else if (activeSearchType === "t") {
handleSearchBySubscription("t", searchValue); handleSearchBySubscription("t", activeSearchValue);
} else if (searchType === "n") { } else if (activeSearchType === "n") {
handleSearchBySubscription("n", searchValue); handleSearchBySubscription("n", activeSearchValue);
} }
// Note: "q" (generic) searches are not processed here since they're
// unstructured queries that don't require actual search execution
} }
}, 100); }, 100);
} }
@ -468,6 +502,9 @@
isProcessingSearch = false; isProcessingSearch = false;
currentProcessingSearchValue = null; currentProcessingSearchValue = null;
lastSearchValue = null; lastSearchValue = null;
// Reset internal search state
currentSearchType = null;
currentSearchValue = null;
updateSearchState(false, false, null, null); updateSearchState(false, false, null, null);
cleanupSearch(); cleanupSearch();
@ -510,16 +547,7 @@
onEventFound(event); onEventFound(event);
} }
function navigateToSearch(query: string, paramName: string) { // AI-NOTE: Main subscription search handler with improved error handling
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
async function handleSearchBySubscription( async function handleSearchBySubscription(
searchType: "d" | "t" | "n", searchType: "d" | "t" | "n",
searchTerm: string, searchTerm: string,
@ -529,7 +557,7 @@
searchTerm, 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 // No need for separate caching logic here as it's handled in profile_search.ts
isResetting = false; isResetting = false;
@ -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 // These functions are no longer needed as profile searches go through subscription_search.ts
// which delegates to the centralized profile_search.ts // which delegates to the centralized profile_search.ts
@ -553,7 +581,7 @@
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: 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
@ -565,7 +593,7 @@
retryCount++; 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 // 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;
@ -616,8 +644,8 @@
updatedResult.eventIds, updatedResult.eventIds,
updatedResult.addresses, updatedResult.addresses,
updatedResult.searchType, updatedResult.searchType,
searchValue || updatedResult.searchTerm, // AI-NOTE: 2025-01-24 - Use original search value for display searchValue || updatedResult.searchTerm, // AI-NOTE: Use original search value for display
false, // AI-NOTE: 2025-01-24 - Second-order update means search is complete false, // AI-NOTE: Second-order update means search is complete
); );
}, },
onSubscriptionCreated: (sub) => { onSubscriptionCreated: (sub) => {
@ -649,8 +677,8 @@
result.eventIds, result.eventIds,
result.addresses, result.addresses,
result.searchType, result.searchType,
searchValue || result.searchTerm, // AI-NOTE: 2025-01-24 - Use original search value for display searchValue || result.searchTerm, // AI-NOTE: Use original search value for display
false, // AI-NOTE: 2025-01-24 - Search is complete false, // AI-NOTE: Search is complete
); );
const totalCount = const totalCount =
@ -738,6 +766,9 @@
currentProcessingSearchValue = null; currentProcessingSearchValue = null;
lastSearchValue = null; lastSearchValue = null;
isWaitingForSearchResult = false; isWaitingForSearchResult = false;
// Reset internal search state
currentSearchType = null;
currentSearchValue = null;
if (searchTimeout) { if (searchTimeout) {
clearTimeout(searchTimeout); clearTimeout(searchTimeout);
@ -770,18 +801,6 @@
? `Search completed. Found 1 ${typeLabel}.` ? `Search completed. Found 1 ${typeLabel}.`
: `Search completed. Found ${searchResultCount} ${countLabel}.`; : `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> </script>
<div class="flex flex-col space-y-6"> <div class="flex flex-col space-y-6">

15
src/routes/events/+page.svelte

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

Loading…
Cancel
Save