feat(ui): cosmetic tweaks — profile pencil, pills, search, ticket count, map icon, avatar trigger, no overlay animations #105
1 changed files with 34 additions and 3 deletions
feat(events): include organizer name in event fuzzy search
Search only indexed title/summary/description/location/tags, so typing an organizer's name found nothing. Organizer display names aren't stored on the event (they're resolved per-pubkey into the shared ProfileService cache), so enrich the search corpus with the resolved name read from that same reactive cache and add it as a Fuse key. Opening the search overlay warms the cache for any organizers not yet fetched, and the corpus recomputes as kind-0 metadata arrives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit
79e20f1e07
|
|
@ -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<HTMLInputElement | null>(null)
|
||||
|
||||
const searchOptions: FuzzySearchOptions<Event> = {
|
||||
const searchOptions: FuzzySearchOptions<SearchableEvent> = {
|
||||
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<Event> = {
|
|||
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<ProfileService>(SERVICE_TOKENS.PROFILE_SERVICE)
|
||||
|
||||
function organizerNameFor(pubkey: string): string {
|
||||
const p = profileService?.profiles.get(pubkey)
|
||||
return p?.display_name ?? p?.name ?? ''
|
||||
}
|
||||
|
||||
const searchCorpus = computed<SearchableEvent[]>(() =>
|
||||
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<string>()
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue