feat: nak req

This commit is contained in:
codytseng 2025-12-10 23:10:47 +08:00
parent ac196cd662
commit a6d6a19199
9 changed files with 388 additions and 25 deletions

View file

@ -284,15 +284,22 @@ const NoteList = forwardRef<
return () => {}
}
const { closer, timelineKey } = await client.subscribeTimeline(
subRequests.map(({ urls, filter }) => ({
urls,
filter: {
kinds: showKinds ?? [],
...filter,
limit: areAlgoRelays ? ALGO_LIMIT : LIMIT
const preprocessedSubRequests = await Promise.all(
subRequests.map(async ({ urls, filter }) => {
const relays = urls.length ? urls : await client.determineRelaysByFilter(filter)
return {
urls: relays,
filter: {
kinds: showKinds ?? [],
...filter,
limit: areAlgoRelays ? ALGO_LIMIT : LIMIT
}
}
})),
})
)
const { closer, timelineKey } = await client.subscribeTimeline(
preprocessedSubRequests,
{
onEvents: (events, eosed) => {
if (events.length > 0) {

View file

@ -1,6 +1,7 @@
import SearchInput from '@/components/SearchInput'
import { useSearchProfiles } from '@/hooks'
import { toExternalContent, toNote } from '@/lib/link'
import { formatFeedRequest, parseNakReqCommand } from '@/lib/nak-parser'
import { randomString } from '@/lib/random'
import { normalizeUrl } from '@/lib/url'
import { cn } from '@/lib/utils'
@ -8,7 +9,7 @@ import { useSecondaryPage } from '@/PageManager'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import modalManager from '@/services/modal-manager.service'
import { TSearchParams } from '@/types'
import { Hash, MessageSquare, Notebook, Search, Server } from 'lucide-react'
import { Hash, MessageSquare, Notebook, Search, Server, Terminal } from 'lucide-react'
import { nip19 } from 'nostr-tools'
import {
forwardRef,
@ -103,6 +104,20 @@ const SearchBar = forwardRef<
const search = input.trim()
if (!search) return
// Check if input is a nak req command
const request = parseNakReqCommand(search)
if (request) {
setSelectableOptions([
{
type: 'nak',
search: formatFeedRequest(request),
request,
input: search
}
])
return
}
if (/^[0-9a-f]{64}$/.test(search)) {
setSelectableOptions([
{ type: 'note', search },
@ -213,6 +228,16 @@ const SearchBar = forwardRef<
/>
)
}
if (option.type === 'nak') {
return (
<NakItem
key={index}
selected={selectedIndex === index}
description={option.search}
onClick={() => updateSearch(option)}
/>
)
}
if (option.type === 'profiles') {
return (
<Item
@ -468,6 +493,28 @@ function ExternalContentItem({
)
}
function NakItem({
description,
onClick,
selected
}: {
description: string
onClick?: () => void
selected?: boolean
}) {
return (
<Item onClick={onClick} selected={selected}>
<div className="size-10 flex justify-center items-center">
<Terminal className="text-muted-foreground flex-shrink-0" />
</div>
<div className="flex flex-col min-w-0 flex-1">
<div className="font-semibold truncate">REQ</div>
<div className="text-sm text-muted-foreground truncate">{description}</div>
</div>
</Item>
)
}
function Item({
className,
children,

View file

@ -32,5 +32,8 @@ export default function SearchResult({ searchParams }: { searchParams: TSearchPa
/>
)
}
if (searchParams.type === 'nak') {
return <NormalFeed subRequests={[searchParams.request]} showRelayCloseReason />
}
return <Relay url={searchParams.search} />
}

View file

@ -129,15 +129,22 @@ const UserAggregationList = forwardRef<
return () => {}
}
const { closer, timelineKey } = await client.subscribeTimeline(
subRequests.map(({ urls, filter }) => ({
urls,
filter: {
kinds: showKinds ?? [],
...filter,
limit: LIMIT
const preprocessedSubRequests = await Promise.all(
subRequests.map(async ({ urls, filter }) => {
const relays = urls.length ? urls : await client.determineRelaysByFilter(filter)
return {
urls: relays,
filter: {
kinds: showKinds ?? [],
...filter,
limit: LIMIT
}
}
})),
})
)
const { closer, timelineKey } = await client.subscribeTimeline(
preprocessedSubRequests,
{
onEvents: (events, eosed) => {
if (events.length > 0) {