feat: add support for commenting and reacting on external content
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import SearchInput from '@/components/SearchInput'
|
||||
import { useSearchProfiles } from '@/hooks'
|
||||
import { toNote } from '@/lib/link'
|
||||
import { toExternalContent, toNote } from '@/lib/link'
|
||||
import { randomString } from '@/lib/random'
|
||||
import { normalizeUrl } from '@/lib/url'
|
||||
import { cn } from '@/lib/utils'
|
||||
@@ -8,7 +8,7 @@ import { useSecondaryPage } from '@/PageManager'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import modalManager from '@/services/modal-manager.service'
|
||||
import { TSearchParams } from '@/types'
|
||||
import { Hash, Notebook, Search, Server } from 'lucide-react'
|
||||
import { Hash, MessageSquare, Notebook, Search, Server } from 'lucide-react'
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import {
|
||||
forwardRef,
|
||||
@@ -45,6 +45,9 @@ const SearchBar = forwardRef<
|
||||
if (['w', 'ws', 'ws:', 'ws:/', 'wss', 'wss:', 'wss:/'].includes(input)) {
|
||||
return undefined
|
||||
}
|
||||
if (!input.includes('.')) {
|
||||
return undefined
|
||||
}
|
||||
try {
|
||||
return normalizeUrl(input)
|
||||
} catch {
|
||||
@@ -89,6 +92,8 @@ const SearchBar = forwardRef<
|
||||
|
||||
if (params.type === 'note') {
|
||||
push(toNote(params.search))
|
||||
} else if (params.type === 'externalContent') {
|
||||
push(toExternalContent(params.search))
|
||||
} else {
|
||||
onSearch(params)
|
||||
}
|
||||
@@ -128,8 +133,9 @@ const SearchBar = forwardRef<
|
||||
|
||||
setSelectableOptions([
|
||||
{ type: 'notes', search },
|
||||
{ type: 'hashtag', search: hashtag, input: `#${hashtag}` },
|
||||
...(normalizedUrl ? [{ type: 'relay', search: normalizedUrl, input: normalizedUrl }] : []),
|
||||
{ type: 'externalContent', search, input },
|
||||
{ type: 'hashtag', search: hashtag, input: `#${hashtag}` },
|
||||
...profiles.map((profile) => ({
|
||||
type: 'profile',
|
||||
search: profile.npub,
|
||||
@@ -197,6 +203,16 @@ const SearchBar = forwardRef<
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (option.type === 'externalContent') {
|
||||
return (
|
||||
<ExternalContentItem
|
||||
key={index}
|
||||
selected={selectedIndex === index}
|
||||
search={option.search}
|
||||
onClick={() => updateSearch(option)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (option.type === 'profiles') {
|
||||
return (
|
||||
<Item
|
||||
@@ -322,10 +338,16 @@ function NormalItem({
|
||||
onClick?: () => void
|
||||
selected?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Item onClick={onClick} selected={selected}>
|
||||
<Search className="text-muted-foreground" />
|
||||
<div className="font-semibold truncate">{search}</div>
|
||||
<div className="size-10 flex justify-center items-center">
|
||||
<Search className="text-muted-foreground flex-shrink-0" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
<div className="font-semibold truncate">{search}</div>
|
||||
<div className="text-sm text-muted-foreground">{t('Search for notes')}</div>
|
||||
</div>
|
||||
</Item>
|
||||
)
|
||||
}
|
||||
@@ -339,10 +361,16 @@ function HashtagItem({
|
||||
onClick?: () => void
|
||||
selected?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Item onClick={onClick} selected={selected}>
|
||||
<Hash className="text-muted-foreground" />
|
||||
<div className="font-semibold truncate">{hashtag}</div>
|
||||
<div className="size-10 flex justify-center items-center">
|
||||
<Hash className="text-muted-foreground flex-shrink-0" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
<div className="font-semibold truncate">#{hashtag}</div>
|
||||
<div className="text-sm text-muted-foreground">{t('Search for hashtag')}</div>
|
||||
</div>
|
||||
</Item>
|
||||
)
|
||||
}
|
||||
@@ -356,10 +384,16 @@ function NoteItem({
|
||||
onClick?: () => void
|
||||
selected?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Item onClick={onClick} selected={selected}>
|
||||
<Notebook className="text-muted-foreground" />
|
||||
<div className="font-semibold truncate">{id}</div>
|
||||
<div className="size-10 flex justify-center items-center">
|
||||
<Notebook className="text-muted-foreground flex-shrink-0" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
<div className="font-semibold truncate font-mono text-sm">{id}</div>
|
||||
<div className="text-sm text-muted-foreground">{t('Go to note')}</div>
|
||||
</div>
|
||||
</Item>
|
||||
)
|
||||
}
|
||||
@@ -397,10 +431,39 @@ function RelayItem({
|
||||
onClick?: () => void
|
||||
selected?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Item onClick={onClick} selected={selected}>
|
||||
<Server className="text-muted-foreground" />
|
||||
<div className="font-semibold truncate">{url}</div>
|
||||
<div className="size-10 flex justify-center items-center">
|
||||
<Server className="text-muted-foreground flex-shrink-0" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
<div className="font-semibold truncate">{url}</div>
|
||||
<div className="text-sm text-muted-foreground">{t('Go to relay')}</div>
|
||||
</div>
|
||||
</Item>
|
||||
)
|
||||
}
|
||||
|
||||
function ExternalContentItem({
|
||||
search,
|
||||
onClick,
|
||||
selected
|
||||
}: {
|
||||
search: string
|
||||
onClick?: () => void
|
||||
selected?: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Item onClick={onClick} selected={selected}>
|
||||
<div className="size-10 flex justify-center items-center">
|
||||
<MessageSquare className="text-muted-foreground flex-shrink-0" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
<div className="font-semibold truncate">{search}</div>
|
||||
<div className="text-sm text-muted-foreground">{t('View discussions about this')}</div>
|
||||
</div>
|
||||
</Item>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user