Move search to its own file, validate and search by nip05
This commit is contained in:
@@ -10,6 +10,7 @@ export * from './profiles'
|
||||
export * from './relays'
|
||||
export * from './relaySelections'
|
||||
export * from './router'
|
||||
export * from './search'
|
||||
export * from './session'
|
||||
export * from './storage'
|
||||
export * from './sync'
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import {debounce} from 'throttle-debounce'
|
||||
import {derived, readable} from 'svelte/store'
|
||||
import {dec} from '@welshman/lib'
|
||||
import {readProfile, displayProfile, displayPubkey, PROFILE} from '@welshman/util'
|
||||
import type {SubscribeRequestWithHandlers} from "@welshman/net"
|
||||
import type {PublishedProfile, TrustedEvent} from "@welshman/util"
|
||||
import {deriveEventsMapped, withGetter} from '@welshman/store'
|
||||
import {repository, load} from './core'
|
||||
import {createSearch} from './util'
|
||||
import {collection} from './collection'
|
||||
import {loadRelaySelections} from './relaySelections'
|
||||
import {wotGraph} from './wot'
|
||||
|
||||
export const profiles = withGetter(
|
||||
deriveEventsMapped<PublishedProfile>(repository, {
|
||||
@@ -49,31 +45,6 @@ export const {
|
||||
},
|
||||
})
|
||||
|
||||
export const searchProfiles = debounce(500, (search: string) => {
|
||||
if (search.length > 2) {
|
||||
load({filters: [{kinds: [PROFILE], search}]})
|
||||
}
|
||||
})
|
||||
|
||||
export const profileSearch = derived(profiles, $profiles =>
|
||||
createSearch($profiles, {
|
||||
onSearch: searchProfiles,
|
||||
getValue: (profile: PublishedProfile) => profile.event.pubkey,
|
||||
sortFn: ({score, item}) => {
|
||||
if (score && score > 0.1) return -score!
|
||||
|
||||
const wotScore = wotGraph.get().get(item.event.pubkey) || 0
|
||||
|
||||
return score ? dec(score) * wotScore : -wotScore
|
||||
},
|
||||
fuseOptions: {
|
||||
keys: ["name", "display_name", {name: "about", weight: 0.3}],
|
||||
threshold: 0.3,
|
||||
shouldSort: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export const displayProfileByPubkey = (pubkey: string | undefined) =>
|
||||
pubkey
|
||||
? displayProfile(profilesByPubkey.get().get(pubkey), displayPubkey(pubkey))
|
||||
|
||||
@@ -4,7 +4,6 @@ import {ctx, groupBy, indexBy, batch, now, uniq, batcher, postJson} from '@welsh
|
||||
import type {RelayProfile} from "@welshman/util"
|
||||
import {normalizeRelayUrl} from "@welshman/util"
|
||||
import {AuthStatus, asMessage, type Connection, type SocketMessage} from '@welshman/net'
|
||||
import {createSearch} from './util'
|
||||
import {collection} from './collection'
|
||||
|
||||
export type RelayStats = {
|
||||
@@ -89,15 +88,6 @@ export const {
|
||||
}),
|
||||
})
|
||||
|
||||
export const relaySearch = derived(relays, $relays =>
|
||||
createSearch($relays, {
|
||||
getValue: (relay: Relay) => relay.url,
|
||||
fuseOptions: {
|
||||
keys: ["url", "name", {name: "description", weight: 0.3}],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
// Utilities for syncing stats from connections to relays
|
||||
|
||||
type RelayStatsUpdate = [string, (stats: RelayStats) => void]
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import Fuse from "fuse.js"
|
||||
import type {IFuseOptions, FuseResult} from "fuse.js"
|
||||
import {debounce} from 'throttle-debounce'
|
||||
import {derived} from 'svelte/store'
|
||||
import {dec, sortBy} from '@welshman/lib'
|
||||
import {PROFILE} from '@welshman/util'
|
||||
import {throttled} from '@welshman/store'
|
||||
import type {PublishedProfile} from "@welshman/util"
|
||||
import {load} from './core'
|
||||
import {wotGraph} from './wot'
|
||||
import {profiles} from './profiles'
|
||||
import {topics} from './topics'
|
||||
import type {Topic} from './topics'
|
||||
import {relays} from './relays'
|
||||
import type {Relay} from './relays'
|
||||
import {handlesByNip05} from './handles'
|
||||
|
||||
export type SearchOptions<V, T> = {
|
||||
getValue: (item: T) => V
|
||||
fuseOptions?: IFuseOptions<T>
|
||||
onSearch?: (term: string) => void
|
||||
sortFn?: (items: FuseResult<T>) => any
|
||||
}
|
||||
|
||||
export type Search<V, T> = {
|
||||
options: T[]
|
||||
getValue: (item: T) => V
|
||||
getOption: (value: V) => T | undefined
|
||||
searchOptions: (term: string) => T[]
|
||||
searchValues: (term: string) => V[]
|
||||
}
|
||||
|
||||
export const createSearch = <V, T>(options: T[], opts: SearchOptions<V, T>): Search<V, T> => {
|
||||
const fuse = new Fuse(options, {...opts.fuseOptions, includeScore: true})
|
||||
const map = new Map<V, T>(options.map(item => [opts.getValue(item), item]))
|
||||
|
||||
const search = (term: string) => {
|
||||
opts.onSearch?.(term)
|
||||
|
||||
let results = term ? fuse.search(term) : options.map(item => ({item}) as FuseResult<T>)
|
||||
|
||||
if (opts.sortFn) {
|
||||
results = sortBy(opts.sortFn, results)
|
||||
}
|
||||
|
||||
return results.map(result => result.item)
|
||||
}
|
||||
|
||||
return {
|
||||
options,
|
||||
getValue: opts.getValue,
|
||||
getOption: (value: V) => map.get(value),
|
||||
searchOptions: (term: string) => search(term),
|
||||
searchValues: (term: string) => search(term).map(opts.getValue),
|
||||
}
|
||||
}
|
||||
|
||||
export const searchProfiles = debounce(500, (search: string) => {
|
||||
if (search.length > 2) {
|
||||
load({filters: [{kinds: [PROFILE], search}]})
|
||||
}
|
||||
})
|
||||
|
||||
export const profileSearch = derived(
|
||||
[throttled(800, profiles), throttled(800, handlesByNip05)],
|
||||
([$profiles, $handlesByNip05]) => {
|
||||
// Remove invalid nip05's from profiles
|
||||
const options = $profiles
|
||||
.map(p => {
|
||||
const isNip05Valid = !p.nip05 || $handlesByNip05.get(p.nip05)?.pubkey === p.event.pubkey
|
||||
|
||||
return isNip05Valid ? p : {...p, nip05: ""}
|
||||
})
|
||||
|
||||
return createSearch(options, {
|
||||
onSearch: searchProfiles,
|
||||
getValue: (profile: PublishedProfile) => profile.event.pubkey,
|
||||
sortFn: ({score, item}) => {
|
||||
if (score && score > 0.1) return -score!
|
||||
|
||||
const wotScore = wotGraph.get().get(item.event.pubkey) || 0
|
||||
|
||||
return score ? dec(score) * wotScore : -wotScore
|
||||
},
|
||||
fuseOptions: {
|
||||
keys: [
|
||||
"nip05",
|
||||
{name: "name", weight: 0.8},
|
||||
{name: "display_name", weight: 0.5},
|
||||
{name: "about", weight: 0.3},
|
||||
],
|
||||
threshold: 0.3,
|
||||
shouldSort: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export const topicSearch = derived(topics, $topics =>
|
||||
createSearch($topics, {
|
||||
getValue: (topic: Topic) => topic.name,
|
||||
fuseOptions: {keys: ["name"]},
|
||||
}),
|
||||
)
|
||||
|
||||
export const relaySearch = derived(relays, $relays =>
|
||||
createSearch($relays, {
|
||||
getValue: (relay: Relay) => relay.url,
|
||||
fuseOptions: {
|
||||
keys: ["url", "name", {name: "description", weight: 0.3}],
|
||||
},
|
||||
}),
|
||||
)
|
||||
@@ -1,8 +1,6 @@
|
||||
import {throttle} from 'throttle-debounce'
|
||||
import {derived} from 'svelte/store'
|
||||
import {inc} from '@welshman/lib'
|
||||
import {custom} from '@welshman/store'
|
||||
import {createSearch} from './util'
|
||||
import {repository} from './core'
|
||||
|
||||
export type Topic = {
|
||||
@@ -32,10 +30,3 @@ export const topics = custom<Topic[]>(setter => {
|
||||
|
||||
return () => repository.off("update", onUpdate)
|
||||
})
|
||||
|
||||
export const topicSearch = derived(topics, $topics =>
|
||||
createSearch($topics, {
|
||||
getValue: (topic: Topic) => topic.name,
|
||||
fuseOptions: {keys: ["name"]},
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1,46 +1,4 @@
|
||||
import Fuse from "fuse.js"
|
||||
import type {IFuseOptions, FuseResult} from "fuse.js"
|
||||
import {now, int, sortBy, DAY, HOUR, MINUTE} from "@welshman/lib"
|
||||
|
||||
export type SearchOptions<V, T> = {
|
||||
getValue: (item: T) => V
|
||||
fuseOptions?: IFuseOptions<T>
|
||||
onSearch?: (term: string) => void
|
||||
sortFn?: (items: FuseResult<T>) => any
|
||||
}
|
||||
|
||||
export type Search<V, T> = {
|
||||
options: T[]
|
||||
getValue: (item: T) => V
|
||||
getOption: (value: V) => T | undefined
|
||||
searchOptions: (term: string) => T[]
|
||||
searchValues: (term: string) => V[]
|
||||
}
|
||||
|
||||
export const createSearch = <V, T>(options: T[], opts: SearchOptions<V, T>): Search<V, T> => {
|
||||
const fuse = new Fuse(options, {...opts.fuseOptions, includeScore: true})
|
||||
const map = new Map<V, T>(options.map(item => [opts.getValue(item), item]))
|
||||
|
||||
const search = (term: string) => {
|
||||
opts.onSearch?.(term)
|
||||
|
||||
let results = term ? fuse.search(term) : options.map(item => ({item}) as FuseResult<T>)
|
||||
|
||||
if (opts.sortFn) {
|
||||
results = sortBy(opts.sortFn, results)
|
||||
}
|
||||
|
||||
return results.map(result => result.item)
|
||||
}
|
||||
|
||||
return {
|
||||
options,
|
||||
getValue: opts.getValue,
|
||||
getOption: (value: V) => map.get(value),
|
||||
searchOptions: (term: string) => search(term),
|
||||
searchValues: (term: string) => search(term).map(opts.getValue),
|
||||
}
|
||||
}
|
||||
import {now, int, DAY, HOUR, MINUTE} from "@welshman/lib"
|
||||
|
||||
export const secondsToDate = (ts: number) => new Date(ts * 1000)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user