diff --git a/packages/client/src/blockedRelayLists.ts b/packages/client/src/blockedRelayLists.ts index 1aafa64..b71012e 100644 --- a/packages/client/src/blockedRelayLists.ts +++ b/packages/client/src/blockedRelayLists.ts @@ -9,7 +9,7 @@ import { removeFromList, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import {Router} from "./router.js" import {User} from "./user.js" @@ -21,7 +21,7 @@ import type {IClient} from "./client.js" * so it depends on the relay-list collection. Feeds `RelayStats.getQuality` so * blocked relays are never selected. */ -export class BlockedRelayLists extends RepositoryCollection> { +export class BlockedRelayLists extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [BLOCKED_RELAYS]}], diff --git a/packages/client/src/blossom.ts b/packages/client/src/blossom.ts index d5282cc..dbf5a00 100644 --- a/packages/client/src/blossom.ts +++ b/packages/client/src/blossom.ts @@ -1,6 +1,6 @@ import {BLOSSOM_SERVERS, asDecryptedEvent, readList} from "@welshman/util" import type {TrustedEvent} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import type {IClient} from "./client.js" @@ -8,7 +8,7 @@ import type {IClient} from "./client.js" * Blossom server lists (kind 10063), keyed by pubkey. Loaded via the outbox * model (the author's write relays), so it depends on the relay-list collection. */ -export class BlossomServerLists extends RepositoryCollection> { +export class BlossomServerLists extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [BLOSSOM_SERVERS]}], diff --git a/packages/client/src/clientData.ts b/packages/client/src/clientData.ts index d00c2db..952c98a 100644 --- a/packages/client/src/clientData.ts +++ b/packages/client/src/clientData.ts @@ -18,10 +18,10 @@ export class ClientData { protected index = writable(new Map()) protected getIndex = getter(this.index) protected itemSubscribers: ((key: string, value: Maybe) => void)[] = [] - public derive: (key?: string, ...args: any[]) => Readable> + public derived: (key?: string, ...args: any[]) => Readable> constructor(protected readonly ctx: IClient) { - this.derive = makeDeriveItem(this.index) + this.derived = makeDeriveItem(this.index) } subscribe = this.index.subscribe @@ -83,7 +83,7 @@ export class ClientData { /** * A `ClientData` collection that knows how to lazily load items by key from the - * network. Subclasses implement `fetch`; `load`/`forceLoad`/`derive` are derived + * network. Subclasses implement `fetch`; `load`/`forceLoad`/`derived` are derived * from it (with per-key caching and backoff via `makeLoadItem`). */ export abstract class LoadableData extends ClientData { @@ -102,6 +102,6 @@ export abstract class LoadableData extends ClientData { this.load = makeLoadItem(fetch, this.get, options) this.forceLoad = makeForceLoadItem(fetch, this.get) - this.derive = makeDeriveItem(this.index, this.load) + this.derived = makeDeriveItem(this.index, this.load) } } diff --git a/packages/client/src/repositoryCollection.ts b/packages/client/src/collection.ts similarity index 84% rename from packages/client/src/repositoryCollection.ts rename to packages/client/src/collection.ts index 961d2dc..75753f2 100644 --- a/packages/client/src/repositoryCollection.ts +++ b/packages/client/src/collection.ts @@ -6,7 +6,7 @@ import type {EventToItem, ItemsByKey, MakeLoadItemOptions} from "@welshman/store import type {IClient} from "./client.js" import {Stores} from "./stores.js" -export type RepositoryCollectionOptions = { +export type CollectionOptions = { filters: Filter[] eventToItem: EventToItem getKey: (item: T) => string @@ -22,7 +22,7 @@ export type RepositoryCollectionOptions = { * * Like `ClientData`, subclasses depend only on the `IClient` seam. */ -export abstract class RepositoryCollection { +export abstract class Collection { byKey: Readable> all: Readable subscribe: Readable>["subscribe"] @@ -32,9 +32,6 @@ export abstract class RepositoryCollection { values: () => IterableIterator load: (key: string, ...args: any[]) => Promise> forceLoad: (key: string, ...args: any[]) => Promise> - // Reactive view of a single key that also triggers a load - derive: (key?: string, ...args: any[]) => Readable> - // Reactive view of a single key that does not trigger a load derived: (key?: string, ...args: any[]) => Readable> private getByKey: () => ItemsByKey @@ -42,7 +39,7 @@ export abstract class RepositoryCollection { constructor( protected readonly ctx: IClient, - options: RepositoryCollectionOptions, + options: CollectionOptions, ) { const fetch = (key: string, ...args: any[]) => this.fetch(key, ...args) @@ -60,8 +57,7 @@ export abstract class RepositoryCollection { this.values = () => this.getByKey().values() this.load = makeLoadItem(fetch, this.get, options.loadOptions) this.forceLoad = makeForceLoadItem(fetch, this.get) - this.derive = makeDeriveItem(this.byKey, this.load) - this.derived = makeDeriveItem(this.byKey) + this.derived = makeDeriveItem(this.byKey, this.load) } // Convenience views of the current user's own item (replaces the old @@ -73,7 +69,7 @@ export abstract class RepositoryCollection { return pubkey ? this.get(pubkey) : undefined } - deriveForUser = (...args: any[]) => this.derive(this.ctx.user?.pubkey, ...args) + deriveForUser = (...args: any[]) => this.derived(this.ctx.user?.pubkey, ...args) loadForUser = (...args: any[]) => { const pubkey = this.ctx.user?.pubkey diff --git a/packages/client/src/feeds.ts b/packages/client/src/feeds.ts index eaa74e5..451b201 100644 --- a/packages/client/src/feeds.ts +++ b/packages/client/src/feeds.ts @@ -26,11 +26,11 @@ export class Feeds { case Scope.Self: return [$pubkey] case Scope.Follows: - return this.ctx.use(Wot).getFollows($pubkey) + return this.ctx.use(Wot).deriveFollows($pubkey).get() case Scope.Network: - return this.ctx.use(Wot).getNetwork($pubkey) + return this.ctx.use(Wot).deriveNetwork($pubkey).get() case Scope.Followers: - return this.ctx.use(Wot).getFollowers($pubkey) + return this.ctx.use(Wot).deriveFollowers($pubkey).get() default: return [] } @@ -38,11 +38,11 @@ export class Feeds { getPubkeysForWOTRange = (min: number, max: number): string[] => { const pubkeys = [] - const $maxWot = this.ctx.use(Wot).getMaxWot() ?? 0 + const $maxWot = this.ctx.use(Wot).max.get() ?? 0 const thresholdMin = $maxWot * min const thresholdMax = $maxWot * max - for (const [tpk, score] of this.ctx.use(Wot).getWotGraph().entries()) { + for (const [tpk, score] of this.ctx.use(Wot).graph.get().entries()) { if (score >= thresholdMin && score <= thresholdMax) { pubkeys.push(tpk) } diff --git a/packages/client/src/follows.ts b/packages/client/src/follows.ts index 56dd66e..93cc291 100644 --- a/packages/client/src/follows.ts +++ b/packages/client/src/follows.ts @@ -7,7 +7,7 @@ import { removeFromList, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import {Thunks} from "./thunk.js" import {User} from "./user.js" @@ -17,7 +17,7 @@ import type {IClient} from "./client.js" * Kind-3 follow lists, keyed by pubkey. Loaded via the outbox model (the * author's write relays), so it depends on the relay-list collection. */ -export class FollowLists extends RepositoryCollection> { +export class FollowLists extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [FOLLOWS]}], diff --git a/packages/client/src/handles.ts b/packages/client/src/handles.ts index 3d3f6fd..00d9b47 100644 --- a/packages/client/src/handles.ts +++ b/packages/client/src/handles.ts @@ -65,7 +65,7 @@ export class Handles extends LoadableData { this.loadForPubkey(pubkey, relays) return deriveDeduplicated( - [this.index, this.ctx.use(Profiles).derive(pubkey, relays)], + [this.index, this.ctx.use(Profiles).derived(pubkey, relays)], ([$handlesByNip05, $profile]) => { if (!$profile?.nip05) return undefined diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 3bda5a1..a1bbe2e 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -3,7 +3,7 @@ export * from "./policies.js" export * from "./network.js" export * from "./stores.js" export * from "./clientData.js" -export * from "./repositoryCollection.js" +export * from "./collection.js" export * from "./user.js" export * from "./router.js" export * from "./relays.js" diff --git a/packages/client/src/messagingRelayLists.ts b/packages/client/src/messagingRelayLists.ts index 3aafd29..72f6b61 100644 --- a/packages/client/src/messagingRelayLists.ts +++ b/packages/client/src/messagingRelayLists.ts @@ -8,7 +8,7 @@ import { removeFromList, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import {Router} from "./router.js" import {User} from "./user.js" @@ -20,7 +20,7 @@ import type {IClient} from "./client.js" * outbox model (the author's write relays), so it depends on the relay-list * collection. */ -export class MessagingRelayLists extends RepositoryCollection> { +export class MessagingRelayLists extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [MESSAGING_RELAYS]}], diff --git a/packages/client/src/mutes.ts b/packages/client/src/mutes.ts index 6a0cb4c..8baadf1 100644 --- a/packages/client/src/mutes.ts +++ b/packages/client/src/mutes.ts @@ -9,7 +9,7 @@ import { updateList, } from "@welshman/util" import type {TrustedEvent, PublishedList} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import type {IClient} from "./client.js" import {Network} from "./network.js" import {Thunks} from "./thunk.js" @@ -20,7 +20,7 @@ import {User} from "./user.js" * Kind-10000 mute lists, keyed by pubkey. Mute lists carry private entries in * encrypted content, so decoding goes through the plaintext cache. */ -export class MuteLists extends RepositoryCollection { +export class MuteLists extends Collection { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [MUTES]}], diff --git a/packages/client/src/pins.ts b/packages/client/src/pins.ts index 75443a7..819865c 100644 --- a/packages/client/src/pins.ts +++ b/packages/client/src/pins.ts @@ -7,7 +7,7 @@ import { removeFromList, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import {Thunks} from "./thunk.js" import {User} from "./user.js" @@ -17,7 +17,7 @@ import type {IClient} from "./client.js" * NIP-51 pin lists (kind 10001), keyed by pubkey. Loaded via the outbox model * (the author's write relays), so it depends on the relay-list collection. */ -export class PinLists extends RepositoryCollection> { +export class PinLists extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [PINS]}], diff --git a/packages/client/src/profiles.ts b/packages/client/src/profiles.ts index a55ff7c..ef2035b 100644 --- a/packages/client/src/profiles.ts +++ b/packages/client/src/profiles.ts @@ -9,7 +9,7 @@ import { PROFILE, } from "@welshman/util" import type {Profile} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import {Router} from "./router.js" import {Thunks} from "./thunk.js" @@ -19,7 +19,7 @@ import type {IClient} from "./client.js" * Kind-0 profiles, keyed by pubkey. Loaded via the outbox model (the author's * write relays), resolved through the relay-list collection at fetch time. */ -export class Profiles extends RepositoryCollection> { +export class Profiles extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [PROFILE]}], @@ -45,7 +45,7 @@ export class Profiles extends RepositoryCollection pubkey - ? derived(this.derive(pubkey, ...args), $profile => + ? derived(this.derived(pubkey, ...args), $profile => displayProfile($profile, displayPubkey(pubkey)), ) : readable("") diff --git a/packages/client/src/relayLists.ts b/packages/client/src/relayLists.ts index 6e71166..6b1bf81 100644 --- a/packages/client/src/relayLists.ts +++ b/packages/client/src/relayLists.ts @@ -12,7 +12,7 @@ import { makeEvent, } from "@welshman/util" import type {TrustedEvent, PublishedList} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Router, addMinimalFallbacks} from "./router.js" import {Network} from "./network.js" import {User} from "./user.js" @@ -23,7 +23,7 @@ import type {IClient} from "./client.js" * NIP-65 relay lists, keyed by pubkey. This is the routing substrate every other * outbox-model load depends on (see `Network.loadUsingOutbox`). */ -export class RelayLists extends RepositoryCollection { +export class RelayLists extends Collection { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [RELAYS]}], diff --git a/packages/client/src/search.ts b/packages/client/src/search.ts index c35cb5b..9f2a10b 100644 --- a/packages/client/src/search.ts +++ b/packages/client/src/search.ts @@ -82,9 +82,9 @@ export class Searches { onSearch: this.searchProfiles, getValue: (profile: PublishedProfile) => profile.event.pubkey, sortFn: ({score = 1, item}) => { - const wotScore = this.ctx.use(Wot).getWotGraph().get(item.event.pubkey) || 0 + const wotScore = this.ctx.use(Wot).graph.get().get(item.event.pubkey) || 0 - return dec(score) * inc(wotScore / (this.ctx.use(Wot).getMaxWot() || 1)) + return dec(score) * inc(wotScore / (this.ctx.use(Wot).max.get() || 1)) }, fuseOptions: { keys: [ diff --git a/packages/client/src/searchRelayLists.ts b/packages/client/src/searchRelayLists.ts index 67b5a6b..89819ff 100644 --- a/packages/client/src/searchRelayLists.ts +++ b/packages/client/src/searchRelayLists.ts @@ -8,7 +8,7 @@ import { removeFromList, } from "@welshman/util" import type {TrustedEvent} from "@welshman/util" -import {RepositoryCollection} from "./repositoryCollection.js" +import {Collection} from "./collection.js" import {Network} from "./network.js" import {Router} from "./router.js" import {User} from "./user.js" @@ -20,7 +20,7 @@ import type {IClient} from "./client.js" * outbox model (the author's write relays), so it depends on the relay-list * collection. */ -export class SearchRelayLists extends RepositoryCollection> { +export class SearchRelayLists extends Collection> { constructor(ctx: IClient) { super(ctx, { filters: [{kinds: [SEARCH_RELAYS]}], diff --git a/packages/client/src/topics.ts b/packages/client/src/topics.ts index 5d6fab4..eef6392 100644 --- a/packages/client/src/topics.ts +++ b/packages/client/src/topics.ts @@ -2,6 +2,7 @@ import {readable} from "svelte/store" import type {Readable} from "svelte/store" import {on} from "@welshman/lib" import {getTopicTagValues} from "@welshman/util" +import type {RepositoryUpdate} from "@welshman/net" import {deriveItems} from "@welshman/store" import type {IClient} from "./client.js" @@ -38,7 +39,7 @@ export class Topics { } this.byName = readable(topicsByName, set => - on(ctx.repository, "update", ({added}: {added: {tags: string[][]}[]}) => { + on(ctx.repository, "update", ({added}: RepositoryUpdate) => { let dirty = false for (const event of added) { diff --git a/packages/client/src/wot.ts b/packages/client/src/wot.ts index 5571643..48f15bb 100644 --- a/packages/client/src/wot.ts +++ b/packages/client/src/wot.ts @@ -1,132 +1,177 @@ -import {derived, writable} from "svelte/store" -import type {Readable, Writable} from "svelte/store" +import {readable, derived} from "svelte/store" import {max, throttle, addToMapKey, inc, dec} from "@welshman/lib" import {getListTags, getPubkeyTagValues} from "@welshman/util" -import {throttled, getter} from "@welshman/store" +import type {List} from "@welshman/util" +import {withGetter} from "@welshman/store" +import type {ReadableWithGetter} from "@welshman/store" import type {IClient} from "./client.js" import {FollowLists} from "./follows.js" import {MuteLists} from "./mutes.js" +const listPubkeys = (list: List | undefined) => getPubkeyTagValues(getListTags(list)) + /** * Web-of-trust scoring derived from follow and mute lists. The trust graph is * built from the perspective of the client's user (or, with no user, the union * of every known follow list) and updated reactively as lists change. + * + * Every query is exposed as a reactive store — the aggregate `*ByPubkey`/`graph`/ + * `max` fields and the parameterized `derive*` methods. All of them are wrapped + * in `withGetter`, so a synchronous snapshot is just `.get()` / + * `derive*(...).get()`. */ export class Wot { - followersByPubkey: Readable>> - mutersByPubkey: Readable>> - wotGraph: Writable> - maxWot: Readable - - private getFollowersByPubkeyStore: () => Map> - private getMutersByPubkeyStore: () => Map> - private getWotGraphStore: () => Map - private getMaxWotStore: () => number | undefined + followersByPubkey: ReadableWithGetter>> + mutersByPubkey: ReadableWithGetter>> + graph: ReadableWithGetter> + max: ReadableWithGetter constructor(readonly ctx: IClient) { - const followLists = this.ctx.use(FollowLists) - const muteLists = this.ctx.use(MuteLists) + this.followersByPubkey = withGetter( + readable(new Map>(), set => + this.ctx.use(FollowLists).subscribe( + throttle(1000, lists => { + const $followersByPubkey = new Map>() - this.followersByPubkey = derived(throttled(1000, followLists.all), lists => { - const $followersByPubkey = new Map>() + for (const list of lists.values()) { + for (const pubkey of getPubkeyTagValues(getListTags(list))) { + addToMapKey($followersByPubkey, pubkey, list.event.pubkey) + } + } - for (const list of lists) { - for (const pubkey of getPubkeyTagValues(getListTags(list))) { - addToMapKey($followersByPubkey, pubkey, list.event.pubkey) - } - } + set($followersByPubkey) + }), + ), + ), + ) - return $followersByPubkey - }) + this.mutersByPubkey = withGetter( + readable(new Map>(), set => + this.ctx.use(MuteLists).subscribe( + throttle(1000, lists => { + const $mutersByPubkey = new Map>() - this.mutersByPubkey = derived(throttled(1000, muteLists.all), lists => { - const $mutersByPubkey = new Map>() + for (const list of lists.values()) { + for (const pubkey of getPubkeyTagValues(getListTags(list))) { + addToMapKey($mutersByPubkey, pubkey, list.event.pubkey) + } + } - for (const list of lists) { - for (const pubkey of getPubkeyTagValues(getListTags(list))) { - addToMapKey($mutersByPubkey, pubkey, list.event.pubkey) - } - } + set($mutersByPubkey) + }), + ), + ), + ) - return $mutersByPubkey - }) + this.graph = withGetter( + readable(new Map(), set => { + const rebuild = throttle(1000, () => { + const followLists = this.ctx.use(FollowLists) + const muteLists = this.ctx.use(MuteLists) + const $pubkey = this.ctx.user?.pubkey + const $graph = new Map() + const $follows = $pubkey + ? listPubkeys(followLists.get($pubkey)) + : Array.from(followLists.keys()) - this.wotGraph = writable(new Map()) + for (const follow of $follows) { + for (const pubkey of listPubkeys(followLists.get(follow))) { + $graph.set(pubkey, inc($graph.get(pubkey))) + } - this.maxWot = derived(this.wotGraph, $g => max(Array.from($g.values()))) + for (const pubkey of listPubkeys(muteLists.get(follow))) { + $graph.set(pubkey, dec($graph.get(pubkey))) + } + } - this.getFollowersByPubkeyStore = getter(this.followersByPubkey) - this.getMutersByPubkeyStore = getter(this.mutersByPubkey) - this.getWotGraphStore = getter(this.wotGraph) - this.getMaxWotStore = getter(this.maxWot) + set($graph) + }) - followLists.subscribe(this.buildGraph) - muteLists.subscribe(this.buildGraph) + const unsubscribers = [ + this.ctx.use(FollowLists).subscribe(rebuild), + this.ctx.use(MuteLists).subscribe(rebuild), + ] + + return () => unsubscribers.forEach(unsubscribe => unsubscribe()) + }), + ) + + this.max = withGetter(derived(this.graph, $g => max(Array.from($g.values())))) } - getFollows = (pubkey: string) => - getPubkeyTagValues(getListTags(this.ctx.use(FollowLists).get(pubkey))) + deriveFollows = (pubkey: string) => + withGetter(derived(this.ctx.use(FollowLists), $lists => listPubkeys($lists.get(pubkey)))) - getMutes = (pubkey: string) => - getPubkeyTagValues(getListTags(this.ctx.use(MuteLists).get(pubkey))) + deriveMutes = (pubkey: string) => + withGetter(derived(this.ctx.use(MuteLists), $lists => listPubkeys($lists.get(pubkey)))) - getNetwork = (pubkey: string) => { - const pubkeys = new Set(this.getFollows(pubkey)) - const network = new Set() + deriveNetwork = (pubkey: string) => + withGetter( + derived(this.ctx.use(FollowLists), $lists => { + const pubkeys = new Set(listPubkeys($lists.get(pubkey))) + const network = new Set() - for (const follow of pubkeys) { - for (const tpk of this.getFollows(follow)) { - if (!pubkeys.has(tpk)) { - network.add(tpk) + for (const follow of pubkeys) { + for (const tpk of listPubkeys($lists.get(follow))) { + if (!pubkeys.has(tpk)) { + network.add(tpk) + } + } } - } - } - return Array.from(network) - } + return Array.from(network) + }), + ) - getFollowersByPubkey = () => this.getFollowersByPubkeyStore() + deriveFollowers = (pubkey: string) => + withGetter(derived(this.followersByPubkey, $followers => Array.from($followers.get(pubkey) || []))) - getMutersByPubkey = () => this.getMutersByPubkeyStore() + deriveMuters = (pubkey: string) => + withGetter(derived(this.mutersByPubkey, $muters => Array.from($muters.get(pubkey) || []))) - getFollowers = (pubkey: string) => Array.from(this.getFollowersByPubkey().get(pubkey) || []) + deriveFollowsWhoFollow = (pubkey: string, target: string) => + withGetter( + derived(this.ctx.use(FollowLists), $lists => + listPubkeys($lists.get(pubkey)).filter(other => + listPubkeys($lists.get(other)).includes(target), + ), + ), + ) - getMuters = (pubkey: string) => Array.from(this.getMutersByPubkey().get(pubkey) || []) + deriveFollowsWhoMute = (pubkey: string, target: string) => + withGetter( + derived([this.ctx.use(FollowLists), this.ctx.use(MuteLists)], ([$follows, $mutes]) => + listPubkeys($follows.get(pubkey)).filter(other => + listPubkeys($mutes.get(other)).includes(target), + ), + ), + ) - getFollowsWhoFollow = (pubkey: string, target: string) => - this.getFollows(pubkey).filter(other => this.getFollows(other).includes(target)) + deriveWotScore = (pubkey: string, target: string) => + withGetter( + derived( + [ + this.ctx.use(FollowLists), + this.ctx.use(MuteLists), + this.followersByPubkey, + this.mutersByPubkey, + ], + ([$follows, $mutes, $followers, $muters]) => { + let follows: string[] + let mutes: string[] - getFollowsWhoMute = (pubkey: string, target: string) => - this.getFollows(pubkey).filter(other => this.getMutes(other).includes(target)) + if (pubkey) { + const theirFollows = listPubkeys($follows.get(pubkey)) - getWotGraph = () => this.getWotGraphStore() + follows = theirFollows.filter(other => listPubkeys($follows.get(other)).includes(target)) + mutes = theirFollows.filter(other => listPubkeys($mutes.get(other)).includes(target)) + } else { + follows = Array.from($followers.get(target) || []) + mutes = Array.from($muters.get(target) || []) + } - getMaxWot = () => this.getMaxWotStore() - - buildGraph = throttle(1000, () => { - const $pubkey = this.ctx.user?.pubkey - const $graph = new Map() - const $follows = $pubkey - ? this.getFollows($pubkey) - : Array.from(this.ctx.use(FollowLists).keys()) - - for (const follow of $follows) { - for (const pubkey of this.getFollows(follow)) { - $graph.set(pubkey, inc($graph.get(pubkey))) - } - - for (const pubkey of this.getMutes(follow)) { - $graph.set(pubkey, dec($graph.get(pubkey))) - } - } - - this.wotGraph.set($graph) - }) - - getWotScore = (pubkey: string, target: string) => { - const follows = pubkey ? this.getFollowsWhoFollow(pubkey, target) : this.getFollowers(target) - const mutes = pubkey ? this.getFollowsWhoMute(pubkey, target) : this.getMuters(target) - - return follows.length - mutes.length - } + return follows.length - mutes.length + }, + ), + ) } diff --git a/packages/client/src/zappers.ts b/packages/client/src/zappers.ts index 69f819c..03a2090 100644 --- a/packages/client/src/zappers.ts +++ b/packages/client/src/zappers.ts @@ -71,7 +71,7 @@ export class Zappers extends LoadableData { this.loadForPubkey(pubkey, relays) return deriveDeduplicated( - [this.index, this.ctx.use(Profiles).derive(pubkey, relays)], + [this.index, this.ctx.use(Profiles).derived(pubkey, relays)], ([$zappersByLnurl, $profile]) => $profile?.lnurl ? $zappersByLnurl.get($profile.lnurl) : undefined, ) @@ -116,7 +116,7 @@ export class Zappers extends LoadableData { const stores: Readable[] = [ this.index, - ...splits.map(split => profiles.derive(split.pubkey)), + ...splits.map(split => profiles.derived(split.pubkey)), ] return deriveDeduplicatedByValue(stores, (values: any[]) => {