|
|
|
@ -2,10 +2,11 @@ import { SecondaryPageLink } from '@/PageManager' |
|
|
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' |
|
|
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' |
|
|
|
import { CommandDialog, CommandInput, CommandItem, CommandList } from '@/components/ui/command' |
|
|
|
import { CommandDialog, CommandInput, CommandItem, CommandList } from '@/components/ui/command' |
|
|
|
import { useSearchProfiles } from '@/hooks' |
|
|
|
import { useSearchProfiles } from '@/hooks' |
|
|
|
import { toNote, toNoteList, toProfile, toProfileList } from '@/lib/link' |
|
|
|
import { toNote, toNoteList, toProfile, toProfileList, toRelay } from '@/lib/link' |
|
|
|
import { generateImageByPubkey } from '@/lib/pubkey' |
|
|
|
import { generateImageByPubkey } from '@/lib/pubkey' |
|
|
|
|
|
|
|
import { normalizeUrl } from '@/lib/url' |
|
|
|
import { TProfile } from '@/types' |
|
|
|
import { TProfile } from '@/types' |
|
|
|
import { Hash, Notebook, UserRound } from 'lucide-react' |
|
|
|
import { Hash, Notebook, Server, UserRound } from 'lucide-react' |
|
|
|
import { nip19 } from 'nostr-tools' |
|
|
|
import { nip19 } from 'nostr-tools' |
|
|
|
import { Dispatch, useEffect, useMemo, useState } from 'react' |
|
|
|
import { Dispatch, useEffect, useMemo, useState } from 'react' |
|
|
|
import { useTranslation } from 'react-i18next' |
|
|
|
import { useTranslation } from 'react-i18next' |
|
|
|
@ -15,6 +16,13 @@ export function SearchDialog({ open, setOpen }: { open: boolean; setOpen: Dispat |
|
|
|
const [input, setInput] = useState('') |
|
|
|
const [input, setInput] = useState('') |
|
|
|
const [debouncedInput, setDebouncedInput] = useState(input) |
|
|
|
const [debouncedInput, setDebouncedInput] = useState(input) |
|
|
|
const { profiles } = useSearchProfiles(debouncedInput, 10) |
|
|
|
const { profiles } = useSearchProfiles(debouncedInput, 10) |
|
|
|
|
|
|
|
const normalizedUrl = useMemo(() => { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
return normalizeUrl(debouncedInput) |
|
|
|
|
|
|
|
} catch { |
|
|
|
|
|
|
|
return undefined |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, [debouncedInput]) |
|
|
|
|
|
|
|
|
|
|
|
const list = useMemo(() => { |
|
|
|
const list = useMemo(() => { |
|
|
|
const search = input.trim() |
|
|
|
const search = input.trim() |
|
|
|
@ -47,6 +55,7 @@ export function SearchDialog({ open, setOpen }: { open: boolean; setOpen: Dispat |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<> |
|
|
|
<> |
|
|
|
|
|
|
|
{!!normalizedUrl && <RelayItem url={normalizedUrl} onClick={() => setOpen(false)} />} |
|
|
|
<NormalItem search={search} onClick={() => setOpen(false)} /> |
|
|
|
<NormalItem search={search} onClick={() => setOpen(false)} /> |
|
|
|
<HashtagItem search={search} onClick={() => setOpen(false)} /> |
|
|
|
<HashtagItem search={search} onClick={() => setOpen(false)} /> |
|
|
|
{profiles.map((profile) => ( |
|
|
|
{profiles.map((profile) => ( |
|
|
|
@ -61,7 +70,7 @@ export function SearchDialog({ open, setOpen }: { open: boolean; setOpen: Dispat |
|
|
|
)} |
|
|
|
)} |
|
|
|
</> |
|
|
|
</> |
|
|
|
) |
|
|
|
) |
|
|
|
}, [input, profiles, setOpen]) |
|
|
|
}, [input, debouncedInput, profiles, setOpen]) |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
const handler = setTimeout(() => { |
|
|
|
const handler = setTimeout(() => { |
|
|
|
@ -146,3 +155,14 @@ function ProfileItem({ profile, onClick }: { profile: TProfile; onClick?: () => |
|
|
|
</SecondaryPageLink> |
|
|
|
</SecondaryPageLink> |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function RelayItem({ url, onClick }: { url: string; onClick?: () => void }) { |
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<SecondaryPageLink to={toRelay(url)} onClick={onClick}> |
|
|
|
|
|
|
|
<CommandItem> |
|
|
|
|
|
|
|
<Server className="text-muted-foreground" /> |
|
|
|
|
|
|
|
<div className="font-semibold truncate">{url}</div> |
|
|
|
|
|
|
|
</CommandItem> |
|
|
|
|
|
|
|
</SecondaryPageLink> |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|