feat: add note search to profile page

This commit is contained in:
codytseng
2025-10-15 22:35:24 +08:00
parent f5ce41dc54
commit 72d43cceec
2 changed files with 50 additions and 12 deletions

View File

@@ -1,13 +1,14 @@
import KindFilter from '@/components/KindFilter' import KindFilter from '@/components/KindFilter'
import NoteList, { TNoteListRef } from '@/components/NoteList' import NoteList, { TNoteListRef } from '@/components/NoteList'
import Tabs from '@/components/Tabs' import Tabs from '@/components/Tabs'
import { BIG_RELAY_URLS, MAX_PINNED_NOTES } from '@/constants' import { BIG_RELAY_URLS, MAX_PINNED_NOTES, SEARCHABLE_RELAY_URLS } from '@/constants'
import { generateBech32IdFromETag } from '@/lib/tag' import { generateBech32IdFromETag } from '@/lib/tag'
import { isTouchDevice } from '@/lib/utils' import { isTouchDevice } from '@/lib/utils'
import { useKindFilter } from '@/providers/KindFilterProvider' import { useKindFilter } from '@/providers/KindFilterProvider'
import { useNostr } from '@/providers/NostrProvider' import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service' import client from '@/services/client.service'
import storage from '@/services/local-storage.service' import storage from '@/services/local-storage.service'
import relayInfoService from '@/services/relay-info.service'
import { TFeedSubRequest, TNoteListMode } from '@/types' import { TFeedSubRequest, TNoteListMode } from '@/types'
import { NostrEvent } from 'nostr-tools' import { NostrEvent } from 'nostr-tools'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
@@ -15,10 +16,12 @@ import { RefreshButton } from '../RefreshButton'
export default function ProfileFeed({ export default function ProfileFeed({
pubkey, pubkey,
topSpace = 0 topSpace = 0,
search = ''
}: { }: {
pubkey: string pubkey: string
topSpace?: number topSpace?: number
search?: string
}) { }) {
const { pubkey: myPubkey, pinListEvent: myPinListEvent } = useNostr() const { pubkey: myPubkey, pinListEvent: myPinListEvent } = useNostr()
const { showKinds } = useKindFilter() const { showKinds } = useKindFilter()
@@ -106,6 +109,20 @@ export default function ProfileFeed({
} }
const relayList = await client.fetchRelayList(pubkey) const relayList = await client.fetchRelayList(pubkey)
if (search) {
const writeRelays = relayList.write.slice(0, 8)
const relayInfos = await relayInfoService.getRelayInfos(writeRelays)
const searchableRelays = writeRelays.filter((_, index) =>
relayInfos[index]?.supported_nips?.includes(50)
)
setSubRequests([
{
urls: searchableRelays.concat(SEARCHABLE_RELAY_URLS).slice(0, 8),
filter: { authors: [pubkey], search }
}
])
} else {
setSubRequests([ setSubRequests([
{ {
urls: relayList.write.concat(BIG_RELAY_URLS).slice(0, 8), urls: relayList.write.concat(BIG_RELAY_URLS).slice(0, 8),
@@ -115,8 +132,9 @@ export default function ProfileFeed({
} }
]) ])
} }
}
init() init()
}, [pubkey, listMode]) }, [pubkey, listMode, search])
const handleListModeChange = (mode: TNoteListMode) => { const handleListModeChange = (mode: TNoteListMode) => {
setListMode(mode) setListMode(mode)
@@ -150,7 +168,7 @@ export default function ProfileFeed({
showKinds={temporaryShowKinds} showKinds={temporaryShowKinds}
hideReplies={listMode === 'posts'} hideReplies={listMode === 'posts'}
filterMutedNotes={false} filterMutedNotes={false}
pinnedEventIds={listMode === 'you' ? [] : pinnedEventIds} pinnedEventIds={listMode === 'you' || !!search ? [] : pinnedEventIds}
/> />
</> </>
) )

View File

@@ -21,6 +21,7 @@ import { Link, Zap } from 'lucide-react'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import NotFound from '../NotFound' import NotFound from '../NotFound'
import SearchInput from '../SearchInput'
import FollowedBy from './FollowedBy' import FollowedBy from './FollowedBy'
import Followings from './Followings' import Followings from './Followings'
import ProfileFeed from './ProfileFeed' import ProfileFeed from './ProfileFeed'
@@ -32,6 +33,8 @@ export default function Profile({ id }: { id?: string }) {
const { profile, isFetching } = useFetchProfile(id) const { profile, isFetching } = useFetchProfile(id)
const { pubkey: accountPubkey } = useNostr() const { pubkey: accountPubkey } = useNostr()
const { mutePubkeySet } = useMuteList() const { mutePubkeySet } = useMuteList()
const [searchInput, setSearchInput] = useState('')
const [debouncedInput, setDebouncedInput] = useState(searchInput)
const { followings } = useFetchFollowings(profile?.pubkey) const { followings } = useFetchFollowings(profile?.pubkey)
const isFollowingYou = useMemo(() => { const isFollowingYou = useMemo(() => {
return ( return (
@@ -51,6 +54,16 @@ export default function Profile({ id }: { id?: string }) {
} }
}, []) }, [])
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedInput(searchInput.trim())
}, 1000)
return () => {
clearTimeout(handler)
}
}, [searchInput])
useEffect(() => { useEffect(() => {
if (!profile?.pubkey) return if (!profile?.pubkey) return
@@ -185,8 +198,15 @@ export default function Profile({ id }: { id?: string }) {
</div> </div>
</div> </div>
</div> </div>
<div className="px-4 pt-2 pb-0.5">
<SearchInput
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
placeholder={t('Search')}
/>
</div> </div>
<ProfileFeed pubkey={pubkey} topSpace={topContainerHeight + 100} /> </div>
<ProfileFeed pubkey={pubkey} topSpace={topContainerHeight + 100} search={debouncedInput} />
</> </>
) )
} }