Add wot calculation to app
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import {FOLLOWS, asDecryptedEvent, readList} from '@welshman/util'
|
import {FOLLOWS, getListValues, asDecryptedEvent, readList} from '@welshman/util'
|
||||||
import {type TrustedEvent, type PublishedList} from '@welshman/util'
|
import {type TrustedEvent, type PublishedList} from '@welshman/util'
|
||||||
import {type SubscribeRequestWithHandlers} from "@welshman/net"
|
import {type SubscribeRequestWithHandlers} from "@welshman/net"
|
||||||
import {deriveEventsMapped, withGetter} from '@welshman/store'
|
import {deriveEventsMapped, withGetter} from '@welshman/store'
|
||||||
@@ -33,3 +33,6 @@ export const {
|
|||||||
await load({...request, filters: [{kinds: [FOLLOWS], authors: [pubkey]}]})
|
await load({...request, filters: [{kinds: [FOLLOWS], authors: [pubkey]}]})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const getFollows = (pubkey: string) =>
|
||||||
|
getListValues("p", followsByPubkey.get().get(pubkey))
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ export * from './storage'
|
|||||||
export * from './thunk'
|
export * from './thunk'
|
||||||
export * from './topics'
|
export * from './topics'
|
||||||
export * from './util'
|
export * from './util'
|
||||||
|
export * from './wot'
|
||||||
export * from './zappers'
|
export * from './zappers'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {MUTES, asDecryptedEvent, readList} from '@welshman/util'
|
import {MUTES, getListValues, asDecryptedEvent, readList} from '@welshman/util'
|
||||||
import {type TrustedEvent, type PublishedList} from '@welshman/util'
|
import {type TrustedEvent, type PublishedList} from '@welshman/util'
|
||||||
import {type SubscribeRequestWithHandlers} from "@welshman/net"
|
import {type SubscribeRequestWithHandlers} from "@welshman/net"
|
||||||
import {deriveEventsMapped, withGetter} from '@welshman/store'
|
import {deriveEventsMapped, withGetter} from '@welshman/store'
|
||||||
@@ -33,3 +33,8 @@ export const {
|
|||||||
await load({...request, filters: [{kinds: [MUTES], authors: [pubkey]}]})
|
await load({...request, filters: [{kinds: [MUTES], authors: [pubkey]}]})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export const getMutes = (pubkey: string) =>
|
||||||
|
getListValues("p", mutesByPubkey.get().get(pubkey))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import {debounce} from 'throttle-debounce'
|
||||||
import {derived, readable} from 'svelte/store'
|
import {derived, readable} from 'svelte/store'
|
||||||
|
import {dec} from '@welshman/lib'
|
||||||
import {readProfile, displayProfile, displayPubkey, PROFILE} from '@welshman/util'
|
import {readProfile, displayProfile, displayPubkey, PROFILE} from '@welshman/util'
|
||||||
import type {SubscribeRequestWithHandlers} from "@welshman/net"
|
import type {SubscribeRequestWithHandlers} from "@welshman/net"
|
||||||
import type {PublishedProfile, TrustedEvent} from "@welshman/util"
|
import type {PublishedProfile, TrustedEvent} from "@welshman/util"
|
||||||
@@ -7,6 +9,7 @@ import {repository, load} from './core'
|
|||||||
import {createSearch} from './util'
|
import {createSearch} from './util'
|
||||||
import {collection} from './collection'
|
import {collection} from './collection'
|
||||||
import {loadRelaySelections} from './relaySelections'
|
import {loadRelaySelections} from './relaySelections'
|
||||||
|
import {getUserWotScore} from './wot'
|
||||||
|
|
||||||
export const profiles = withGetter(
|
export const profiles = withGetter(
|
||||||
deriveEventsMapped<PublishedProfile>(repository, {
|
deriveEventsMapped<PublishedProfile>(repository, {
|
||||||
@@ -45,11 +48,22 @@ export const {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const searchProfiles = debounce(500, (search: string) => {
|
||||||
|
if (search.length > 2) {
|
||||||
|
load({filters: [{kinds: [PROFILE], search}]})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export const profileSearch = derived(profiles, $profiles =>
|
export const profileSearch = derived(profiles, $profiles =>
|
||||||
createSearch($profiles, {
|
createSearch($profiles, {
|
||||||
|
onSearch: searchProfiles,
|
||||||
getValue: (profile: PublishedProfile) => profile.event.pubkey,
|
getValue: (profile: PublishedProfile) => profile.event.pubkey,
|
||||||
|
sortFn: ({score, item}) =>
|
||||||
|
score! < 0.1 ? dec(score!) * getUserWotScore(item.event.pubkey) : -score!,
|
||||||
fuseOptions: {
|
fuseOptions: {
|
||||||
keys: ["name", "display_name", {name: "about", weight: 0.3}],
|
keys: ["name", "display_name", {name: "about", weight: 0.3}],
|
||||||
|
threshold: 0.3,
|
||||||
|
shouldSort: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {sortBy} from "@welshman/lib"
|
|||||||
export type SearchOptions<V, T> = {
|
export type SearchOptions<V, T> = {
|
||||||
getValue: (item: T) => V
|
getValue: (item: T) => V
|
||||||
fuseOptions?: IFuseOptions<T>
|
fuseOptions?: IFuseOptions<T>
|
||||||
|
onSearch?: (term: string) => void
|
||||||
sortFn?: (items: FuseResult<T>) => any
|
sortFn?: (items: FuseResult<T>) => any
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ export const createSearch = <V, T>(options: T[], opts: SearchOptions<V, T>): Sea
|
|||||||
const map = new Map<V, T>(options.map(item => [opts.getValue(item), item]))
|
const map = new Map<V, T>(options.map(item => [opts.getValue(item), item]))
|
||||||
|
|
||||||
const search = (term: string) => {
|
const search = (term: string) => {
|
||||||
|
opts.onSearch?.(term)
|
||||||
|
|
||||||
let results = term ? fuse.search(term) : options.map(item => ({item, score: 1}) as FuseResult<T>)
|
let results = term ? fuse.search(term) : options.map(item => ({item, score: 1}) as FuseResult<T>)
|
||||||
|
|
||||||
if (opts.sortFn) {
|
if (opts.sortFn) {
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import {throttle} from 'throttle-debounce'
|
||||||
|
import {derived, writable} from 'svelte/store'
|
||||||
|
import {addToMapKey, inc, dec} from '@welshman/lib'
|
||||||
|
import {getListValues} from '@welshman/util'
|
||||||
|
import {throttled, withGetter} from '@welshman/store'
|
||||||
|
import {pubkey} from './session'
|
||||||
|
import {follows, getFollows, followsByPubkey} from './follows'
|
||||||
|
import {mutes, getMutes} from './mutes'
|
||||||
|
|
||||||
|
export const followersByPubkey = withGetter(
|
||||||
|
derived(
|
||||||
|
throttled(1000, follows),
|
||||||
|
lists => {
|
||||||
|
const $followersByPubkey = new Map<string, Set<string>>()
|
||||||
|
|
||||||
|
for (const list of lists) {
|
||||||
|
for (const pubkey of getListValues("p", list)) {
|
||||||
|
addToMapKey($followersByPubkey, pubkey, list.event.pubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $followersByPubkey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
export const mutersByPubkey = withGetter(
|
||||||
|
derived(
|
||||||
|
throttled(1000, mutes),
|
||||||
|
lists => {
|
||||||
|
const $mutersByPubkey = new Map<string, Set<string>>()
|
||||||
|
|
||||||
|
for (const list of lists) {
|
||||||
|
for (const pubkey of getListValues("p", list)) {
|
||||||
|
addToMapKey($mutersByPubkey, pubkey, list.event.pubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mutersByPubkey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getFollowers = (pubkey: string) =>
|
||||||
|
Array.from(followersByPubkey.get().get(pubkey) || [])
|
||||||
|
|
||||||
|
export const getMuters = (pubkey: string) =>
|
||||||
|
Array.from(mutersByPubkey.get().get(pubkey) || [])
|
||||||
|
|
||||||
|
export const getFollowsWhoFollow = (pubkey: string, target: string) =>
|
||||||
|
getFollows(pubkey).filter(other => getFollows(other).includes(target))
|
||||||
|
|
||||||
|
export const getFollowsWhoMute = (pubkey: string, target: string) =>
|
||||||
|
getFollows(pubkey).filter(other => getMutes(other).includes(target))
|
||||||
|
|
||||||
|
export const wotGraph = withGetter(writable(new Map<string, number>()))
|
||||||
|
|
||||||
|
const buildGraph = throttle(1000, () => {
|
||||||
|
const $pubkey = pubkey.get()
|
||||||
|
const $graph = new Map<string, number>()
|
||||||
|
const $follows = $pubkey ? getFollows($pubkey) : followsByPubkey.get().keys()
|
||||||
|
|
||||||
|
for (const follow of $follows) {
|
||||||
|
for (const pubkey of getFollows(follow)) {
|
||||||
|
$graph.set(pubkey, inc($graph.get(pubkey)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pubkey of getMutes(follow)) {
|
||||||
|
$graph.set(pubkey, dec($graph.get(pubkey)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wotGraph.set($graph)
|
||||||
|
})
|
||||||
|
|
||||||
|
pubkey.subscribe(buildGraph)
|
||||||
|
follows.subscribe(buildGraph)
|
||||||
|
mutes.subscribe(buildGraph)
|
||||||
|
|
||||||
|
export const getWotScore = (pubkey: string, target: string) => {
|
||||||
|
const follows = pubkey ? getFollowsWhoFollow(pubkey, target) : getFollowers(target)
|
||||||
|
const mutes = pubkey ? getFollowsWhoMute(pubkey, target) : getMuters(target)
|
||||||
|
|
||||||
|
return follows.length - mutes.length
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserWotScore = (pubkey: string) => wotGraph.get().get(pubkey) || 0
|
||||||
@@ -65,5 +65,5 @@ export function cached<T, V, Args extends any[]>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function simpleCache<V, Args extends any[]>(getValue: (args: Args) => V) {
|
export function simpleCache<V, Args extends any[]>(getValue: (args: Args) => V) {
|
||||||
return cached({maxSize: 10**10, getKey: xs => xs.join(':'), getValue})
|
return cached({maxSize: 10**5, getKey: xs => xs.join(':'), getValue})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user