import * as React from 'react' import { getNoteBech32Id } from '@/lib/event' import client from '@/services/client.service' import { searchEventsForPicker, type PickerSearchMode } from '@/services/mention-event-search.service' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { SimpleUsername } from '@/components/Username' import { nip19, type Event as NEvent } from 'nostr-tools' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Skeleton } from '@/components/ui/skeleton' import { Search } from 'lucide-react' import type { Editor } from '@tiptap/core' import { OPEN_NEVENT_PICKER_EVENT, extendMentionRangeToEndOfWord } from './suggestion' type NeventNaddrPickerDialogProps = { open: boolean onOpenChange: (open: boolean) => void onSelect: (nostrLink: string) => void /** When provided, the dialog opens with this tab selected (e.g. from @naddr vs @nevent). */ initialMode?: PickerSearchMode } function NeventNaddrPickerDialog({ open, onOpenChange, onSelect, initialMode }: NeventNaddrPickerDialogProps) { const { t } = useTranslation() const [mode, setMode] = useState(initialMode ?? 'nevent') const [query, setQuery] = useState('') const [debouncedQuery, setDebouncedQuery] = useState('') const [events, setEvents] = useState([]) const [loading, setLoading] = useState(false) useEffect(() => { if (!open) return setQuery('') setDebouncedQuery('') setEvents([]) if (initialMode !== undefined) setMode(initialMode) }, [open, initialMode]) useEffect(() => { if (!open) return const t = setTimeout(() => setDebouncedQuery(query.trim()), 300) return () => clearTimeout(t) }, [open, query]) useEffect(() => { if (!open || !debouncedQuery) { setEvents([]) setLoading(false) return } let cancelled = false setLoading(true) searchEventsForPicker(debouncedQuery, 20, mode, undefined) .then((list) => { if (cancelled) return setEvents(list.slice(0, 15) as NEvent[]) }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [open, debouncedQuery, mode]) const handleSelect = useCallback( (event: NEvent) => { client.addEventToCache(event) try { const bech32 = getNoteBech32Id(event) onSelect(`nostr:${bech32}`) onOpenChange(false) } catch { onSelect(`nostr:${nip19.noteEncode(event.id)}`) onOpenChange(false) } }, [onSelect, onOpenChange] ) return ( {t('Search for event or address…')}
setQuery(e.target.value)} className="pl-9" autoFocus />
{loading && (
{Array.from({ length: 6 }).map((_, i) => ( ))}
)} {!loading && debouncedQuery && events.length === 0 && (

{t('No events found')}

)} {!loading && events.map((ev: NEvent) => ( ))}
) } type NeventPickerContextValue = { openNeventPicker: (onSelected: (nostrLink: string) => void, initialMode?: PickerSearchMode) => void } export const NeventPickerContext = React.createContext(null) export function NeventPickerProvider({ children }: { children: React.ReactNode }) { const [open, setOpen] = useState(false) const [onSelectedRef, setOnSelectedRef] = useState<((link: string) => void) | null>(null) const [initialMode, setInitialMode] = useState('nevent') useEffect(() => { const handler = (e: Event) => { const { editor, range, initialMode: detailMode } = (e as CustomEvent<{ editor: Editor range: { from: number; to: number } initialMode?: PickerSearchMode }>).detail const to = extendMentionRangeToEndOfWord(editor, range) setOnSelectedRef(() => (link: string) => { editor.chain().focus().insertContentAt({ from: range.from, to }, link + ' ').run() }) setInitialMode(detailMode ?? 'nevent') setOpen(true) } window.addEventListener(OPEN_NEVENT_PICKER_EVENT, handler) return () => window.removeEventListener(OPEN_NEVENT_PICKER_EVENT, handler) }, []) const openNeventPicker = useCallback((onSelected: (nostrLink: string) => void, mode?: PickerSearchMode) => { setOnSelectedRef(() => onSelected) setInitialMode(mode ?? 'nevent') setOpen(true) }, []) const handleSelect = useCallback( (link: string) => { onSelectedRef?.(link) setOnSelectedRef(null) }, [onSelectedRef] ) const handleOpenChange = useCallback((next: boolean) => { if (!next) { setOnSelectedRef(null) setInitialMode('nevent') } setOpen(next) }, []) const value = React.useMemo(() => ({ openNeventPicker }), [openNeventPicker]) return ( {children} ) }