diff --git a/src/modules/events/components/EventSearchOverlay.vue b/src/modules/events/components/EventSearchOverlay.vue index abe32d3..ffb9670 100644 --- a/src/modules/events/components/EventSearchOverlay.vue +++ b/src/modules/events/components/EventSearchOverlay.vue @@ -7,8 +7,13 @@ import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { Search, X, MapPin, Calendar } from 'lucide-vue-next' import { useFuzzySearch, type FuzzySearchOptions } from '@/composables/useFuzzySearch' +import { tryInjectService, SERVICE_TOKENS } from '@/core/di-container' +import type { ProfileService } from '@/modules/base/nostr/ProfileService' import type { Event } from '../types/event' +/** Event enriched with its resolved organizer display name for search. */ +type SearchableEvent = Event & { organizerName: string } + const props = defineProps<{ events: Event[] }>() @@ -22,12 +27,13 @@ const { dateLocale } = useDateLocale() const isOpen = ref(false) const inputRef = ref(null) -const searchOptions: FuzzySearchOptions = { +const searchOptions: FuzzySearchOptions = { fuseOptions: { keys: [ { name: 'title', weight: 0.5 }, { name: 'summary', weight: 0.2 }, { name: 'description', weight: 0.15 }, + { name: 'organizerName', weight: 0.1 }, { name: 'location', weight: 0.1 }, { name: 'tags', weight: 0.05 }, ], @@ -39,7 +45,20 @@ const searchOptions: FuzzySearchOptions = { resultLimit: 8, } -const eventsRef = computed(() => props.events) +// Organizer display names aren't stored on the event (they're fetched +// per-pubkey into the shared ProfileService cache). Read the resolved +// name from that same reactive cache so search matches it; the corpus +// recomputes as kind-0 metadata lands. +const profileService = tryInjectService(SERVICE_TOKENS.PROFILE_SERVICE) + +function organizerNameFor(pubkey: string): string { + const p = profileService?.profiles.get(pubkey) + return p?.display_name ?? p?.name ?? '' +} + +const searchCorpus = computed(() => + props.events.map((e) => ({ ...e, organizerName: organizerNameFor(e.organizer.pubkey) })), +) const { searchQuery, @@ -47,7 +66,7 @@ const { isSearching, clearSearch, setSearchQuery, -} = useFuzzySearch(eventsRef, searchOptions) +} = useFuzzySearch(searchCorpus, searchOptions) const showResults = computed(() => isOpen.value && isSearching.value && filteredItems.value.length > 0) const showNoResults = computed(() => isOpen.value && isSearching.value && filteredItems.value.length === 0) @@ -94,6 +113,18 @@ function handleClickOutside(e: MouseEvent) { watch(isOpen, (open) => { if (open) { document.addEventListener('click', handleClickOutside) + // Warm the shared profile cache for every organizer in the current + // set so their names become searchable (fetches dedupe in the + // service; the corpus reacts as kind-0 metadata arrives). + if (profileService) { + const seen = new Set() + for (const e of props.events) { + const pk = e.organizer.pubkey + if (seen.has(pk) || profileService.profiles.get(pk)) continue + seen.add(pk) + void profileService.getProfile(pk) + } + } } else { document.removeEventListener('click', handleClickOutside) }