feat: improve mention search results
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -31,6 +31,7 @@
|
|||||||
"dataloader": "^2.2.3",
|
"dataloader": "^2.2.3",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"embla-carousel-react": "^8.5.1",
|
"embla-carousel-react": "^8.5.1",
|
||||||
|
"flexsearch": "^0.7.43",
|
||||||
"i18next": "^24.2.0",
|
"i18next": "^24.2.0",
|
||||||
"lru-cache": "^11.0.2",
|
"lru-cache": "^11.0.2",
|
||||||
"lucide-react": "^0.469.0",
|
"lucide-react": "^0.469.0",
|
||||||
@@ -5731,6 +5732,11 @@
|
|||||||
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
|
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/flexsearch": {
|
||||||
|
"version": "0.7.43",
|
||||||
|
"resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz",
|
||||||
|
"integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg=="
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
"dataloader": "^2.2.3",
|
"dataloader": "^2.2.3",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"embla-carousel-react": "^8.5.1",
|
"embla-carousel-react": "^8.5.1",
|
||||||
|
"flexsearch": "^0.7.43",
|
||||||
"i18next": "^24.2.0",
|
"i18next": "^24.2.0",
|
||||||
"lru-cache": "^11.0.2",
|
"lru-cache": "^11.0.2",
|
||||||
"lucide-react": "^0.469.0",
|
"lucide-react": "^0.469.0",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { SEARCHABLE_RELAY_URLS } from '@/constants'
|
|
||||||
import { useFeed } from '@/providers/FeedProvider'
|
import { useFeed } from '@/providers/FeedProvider'
|
||||||
import client from '@/services/client.service'
|
import client from '@/services/client.service'
|
||||||
import { TProfile } from '@/types'
|
import { TProfile } from '@/types'
|
||||||
@@ -22,13 +21,7 @@ export function useSearchProfiles(search: string, limit: number) {
|
|||||||
setIsFetching(true)
|
setIsFetching(true)
|
||||||
setProfiles([])
|
setProfiles([])
|
||||||
try {
|
try {
|
||||||
const profiles = await client.fetchProfiles(
|
const profiles = await client.searchProfilesFromIndex(search)
|
||||||
searchableRelayUrls.concat(SEARCHABLE_RELAY_URLS).slice(0, 4),
|
|
||||||
{
|
|
||||||
search,
|
|
||||||
limit
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (profiles) {
|
if (profiles) {
|
||||||
setProfiles(profiles)
|
setProfiles(profiles)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
|
|||||||
setProfileEvent(profileEvent)
|
setProfileEvent(profileEvent)
|
||||||
setProfile(getProfileFromProfileEvent(profileEvent))
|
setProfile(getProfileFromProfileEvent(profileEvent))
|
||||||
})
|
})
|
||||||
|
client.initUserIndexFromFollowings(account.pubkey)
|
||||||
}, [account])
|
}, [account])
|
||||||
|
|
||||||
const hasNostrLoginHash = () => {
|
const hasNostrLoginHash = () => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { extractPubkeysFromEventTags } from '@/lib/tag'
|
|||||||
import { TDraftEvent, TProfile, TRelayInfo, TRelayList } from '@/types'
|
import { TDraftEvent, TProfile, TRelayInfo, TRelayList } from '@/types'
|
||||||
import { sha256 } from '@noble/hashes/sha2'
|
import { sha256 } from '@noble/hashes/sha2'
|
||||||
import DataLoader from 'dataloader'
|
import DataLoader from 'dataloader'
|
||||||
|
import FlexSearch from 'flexsearch'
|
||||||
import { LRUCache } from 'lru-cache'
|
import { LRUCache } from 'lru-cache'
|
||||||
import {
|
import {
|
||||||
EventTemplate,
|
EventTemplate,
|
||||||
@@ -77,6 +78,10 @@ class ClientService extends EventTarget {
|
|||||||
fetchMethod: this._fetchFollowListEvent.bind(this)
|
fetchMethod: this._fetchFollowListEvent.bind(this)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
private userIndex = new FlexSearch.Index({
|
||||||
|
tokenize: 'full'
|
||||||
|
})
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -499,6 +504,18 @@ class ClientService extends EventTarget {
|
|||||||
return readRelays
|
return readRelays
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async searchProfilesFromIndex(query: string) {
|
||||||
|
const result = await this.userIndex.searchAsync(query, { limit: 10 })
|
||||||
|
return Promise.all(result.map((pubkey) => this.fetchProfile(pubkey as string))).then(
|
||||||
|
(profiles) => profiles.filter(Boolean) as TProfile[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async initUserIndexFromFollowings(pubkey: string) {
|
||||||
|
const followings = await this.fetchFollowings(pubkey)
|
||||||
|
await this.profileEventDataloader.loadMany(followings)
|
||||||
|
}
|
||||||
|
|
||||||
private async fetchEventById(relayUrls: string[], id: string): Promise<NEvent | undefined> {
|
private async fetchEventById(relayUrls: string[], id: string): Promise<NEvent | undefined> {
|
||||||
const event = await this.fetchEventFromDefaultRelaysDataloader.load(id)
|
const event = await this.fetchEventFromDefaultRelaysDataloader.load(id)
|
||||||
if (event) {
|
if (event) {
|
||||||
@@ -577,6 +594,7 @@ class ClientService extends EventTarget {
|
|||||||
const profileFromDefaultRelays =
|
const profileFromDefaultRelays =
|
||||||
await this.fetchProfileEventFromDefaultRelaysDataloader.load(pubkey)
|
await this.fetchProfileEventFromDefaultRelaysDataloader.load(pubkey)
|
||||||
if (profileFromDefaultRelays) {
|
if (profileFromDefaultRelays) {
|
||||||
|
this.addUsernameToIndex(profileFromDefaultRelays)
|
||||||
return profileFromDefaultRelays
|
return profileFromDefaultRelays
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,9 +611,29 @@ class ClientService extends EventTarget {
|
|||||||
this.profileEventDataloader.prime(pubkey, Promise.resolve(profileEvent))
|
this.profileEventDataloader.prime(pubkey, Promise.resolve(profileEvent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (profileEvent) {
|
||||||
|
this.addUsernameToIndex(profileEvent)
|
||||||
|
}
|
||||||
|
|
||||||
return profileEvent
|
return profileEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addUsernameToIndex(profileEvent: NEvent) {
|
||||||
|
try {
|
||||||
|
const profileObj = JSON.parse(profileEvent.content)
|
||||||
|
const text = [
|
||||||
|
profileObj.display_name?.trim() ?? '',
|
||||||
|
profileObj.name?.trim() ?? '',
|
||||||
|
profileObj.nip05?.split('@')[0]?.trim() ?? ''
|
||||||
|
].join(' ')
|
||||||
|
if (!text) return
|
||||||
|
|
||||||
|
this.userIndex.add(profileEvent.pubkey, text)
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async tryHarderToFetchEvent(
|
private async tryHarderToFetchEvent(
|
||||||
relayUrls: string[],
|
relayUrls: string[],
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
|||||||
Reference in New Issue
Block a user