diff --git a/packages/client/src/plugins/base.ts b/packages/client/src/plugins/base.ts index 193fb19..41f601a 100644 --- a/packages/client/src/plugins/base.ts +++ b/packages/client/src/plugins/base.ts @@ -17,14 +17,19 @@ export type Projection = { export const projection = ($: Readable, get = getter($)) => ({$, get}) +/** + * Build a `Projection` derived from another `Projection`: re-read `src` + * reactively via `.$` or synchronously via `.get()`. + */ +export const projectFrom = (src: Projection, read: ($: S) => U): Projection => + projection(derived(src.$, read), () => read(src.get())) + /** * Synchronous read access to a keyed collection's current value. Shared by * collections that own a map (`MapPlugin`) and those that are read-only views * over the repository (`DerivedPlugin`). */ export interface ReadableMap { - keys(): IterableIterator - values(): IterableIterator get(key: string): Maybe } @@ -81,12 +86,11 @@ export class MapPlugin implements Mappable, Derivable { this.one = makeDeriveItem(this.store) } - keys = () => this.index.get().keys() - - values = () => this.index.get().values() - get = (key: string) => this.index.get().get(key) + project = (key: string, read: (item: Maybe) => U): Projection => + projection(derived(this.one(key), read), () => read(this.get(key))) + set = (key: string, value: T) => { this.store.update($items => { $items.set(key, value) @@ -207,16 +211,8 @@ export abstract class DerivedPlugin implements ReadableMap, Loadable, D this.one = makeDeriveItem(index, this.load) } - keys = () => this.index.get().keys() - - values = () => this.index.get().values() - get = (key: string) => this.index.get().get(key) - /** - * Build a per-key `Projection` over this collection: snapshot synchronously - * with `.get()`, or subscribe via `.$` (which lazily loads the key). - */ - protected project = (key: string, read: (item: Maybe) => U): Projection => + project = (key: string, read: (item: Maybe) => U): Projection => projection(derived(this.one(key), read), () => read(this.get(key))) } diff --git a/packages/client/src/plugins/network.ts b/packages/client/src/plugins/network.ts index 1debaca..6eeec30 100644 --- a/packages/client/src/plugins/network.ts +++ b/packages/client/src/plugins/network.ts @@ -11,7 +11,8 @@ import type { PullOptions, PushOptions, } from "@welshman/net" -import {Router, addMinimalFallbacks} from "./router.js" +import {addMinimalFallbacks} from "@welshman/router" +import {Router} from "./router.js" import {RelayLists} from "./relayLists.js" import type {IClient} from "../client.js" diff --git a/packages/client/src/plugins/relayLists.ts b/packages/client/src/plugins/relayLists.ts index 2de0b24..8ad18fa 100644 --- a/packages/client/src/plugins/relayLists.ts +++ b/packages/client/src/plugins/relayLists.ts @@ -14,7 +14,8 @@ import { import type {TrustedEvent, PublishedList} from "@welshman/util" import {DerivedPlugin} from "./base.js" import type {Projection} from "./base.js" -import {Router, addMinimalFallbacks} from "./router.js" +import {addMinimalFallbacks} from "@welshman/router" +import {Router} from "./router.js" import {Network} from "./network.js" import {User} from "../user.js" import {Thunks} from "./thunk.js" diff --git a/packages/client/src/plugins/relays.ts b/packages/client/src/plugins/relays.ts index b1690a5..253c016 100644 --- a/packages/client/src/plugins/relays.ts +++ b/packages/client/src/plugins/relays.ts @@ -1,9 +1,8 @@ -import {derived} from "svelte/store" import {fetchJson} from "@welshman/lib" import type {Maybe} from "@welshman/lib" import {displayRelayUrl, displayRelayProfile} from "@welshman/util" import type {RelayProfile} from "@welshman/util" -import {LoadableMapPlugin, projection} from "./base.js" +import {LoadableMapPlugin} from "./base.js" import type {Projection} from "./base.js" /** @@ -37,11 +36,8 @@ export class Relays extends LoadableMapPlugin { } } - display = (url: string): Projection => { - const read = ($relay: Maybe) => displayRelayProfile($relay, displayRelayUrl(url)) - - return projection(derived(this.one(url), read), () => read(this.get(url))) - } + display = (url: string): Projection => + this.project(url, $relay => displayRelayProfile($relay, displayRelayUrl(url))) hasNegentropy = async (url: string) => { const relay = await this.load(url) diff --git a/packages/client/src/plugins/thunk.ts b/packages/client/src/plugins/thunk.ts index d339173..28bcde5 100644 --- a/packages/client/src/plugins/thunk.ts +++ b/packages/client/src/plugins/thunk.ts @@ -15,7 +15,8 @@ import {PublishStatus, PublishResult, PublishOptions, PublishResultsByRelay} fro import {Nip01Signer, Nip59} from "@welshman/signer" import type {IClient} from "../client.js" import {Network} from "./network.js" -import {Router, addMinimalFallbacks} from "./router.js" +import {addMinimalFallbacks} from "@welshman/router" +import {Router} from "./router.js" import {User} from "../user.js" export type ThunkOptions = Override< diff --git a/packages/client/src/plugins/wot.ts b/packages/client/src/plugins/wot.ts index a04541e..94f4f23 100644 --- a/packages/client/src/plugins/wot.ts +++ b/packages/client/src/plugins/wot.ts @@ -3,7 +3,7 @@ import {max, throttle, addToMapKey, inc, dec} from "@welshman/lib" import {getListTags, getPubkeyTagValues} from "@welshman/util" import type {List} from "@welshman/util" import type {IClient} from "../client.js" -import {projection} from "./base.js" +import {projection, projectFrom} from "./base.js" import type {Projection} from "./base.js" import {FollowLists} from "./follows.js" import {MuteLists} from "./mutes.js" @@ -95,24 +95,14 @@ export class Wot { this.max = projection(maxStore) } - follows = (pubkey: string): Projection => { - const read = ($lists: ReadonlyMap) => listPubkeys($lists.get(pubkey)) + follows = (pubkey: string): Projection => + projectFrom(this.ctx.use(FollowLists).index, $lists => listPubkeys($lists.get(pubkey))) - return projection(derived(this.ctx.use(FollowLists).index.$, read), () => - read(this.ctx.use(FollowLists).index.get()), - ) - } + mutes = (pubkey: string): Projection => + projectFrom(this.ctx.use(MuteLists).index, $lists => listPubkeys($lists.get(pubkey))) - mutes = (pubkey: string): Projection => { - const read = ($lists: ReadonlyMap) => listPubkeys($lists.get(pubkey)) - - return projection(derived(this.ctx.use(MuteLists).index.$, read), () => - read(this.ctx.use(MuteLists).index.get()), - ) - } - - network = (pubkey: string): Projection => { - const read = ($lists: ReadonlyMap) => { + network = (pubkey: string): Projection => + projectFrom(this.ctx.use(FollowLists).index, $lists => { const pubkeys = new Set(listPubkeys($lists.get(pubkey))) const network = new Set() @@ -125,39 +115,20 @@ export class Wot { } return Array.from(network) - } + }) - return projection(derived(this.ctx.use(FollowLists).index.$, read), () => - read(this.ctx.use(FollowLists).index.get()), - ) - } + followers = (pubkey: string): Projection => + projectFrom(this.followersByPubkey, $followers => Array.from($followers.get(pubkey) || [])) - followers = (pubkey: string): Projection => { - const read = ($followers: ReadonlyMap>) => - Array.from($followers.get(pubkey) || []) + muters = (pubkey: string): Projection => + projectFrom(this.mutersByPubkey, $muters => Array.from($muters.get(pubkey) || [])) - return projection(derived(this.followersByPubkey.$, read), () => - read(this.followersByPubkey.get()), - ) - } - - muters = (pubkey: string): Projection => { - const read = ($muters: ReadonlyMap>) => - Array.from($muters.get(pubkey) || []) - - return projection(derived(this.mutersByPubkey.$, read), () => read(this.mutersByPubkey.get())) - } - - followsWhoFollow = (pubkey: string, target: string): Projection => { - const read = ($lists: ReadonlyMap) => + followsWhoFollow = (pubkey: string, target: string): Projection => + projectFrom(this.ctx.use(FollowLists).index, $lists => listPubkeys($lists.get(pubkey)).filter(other => listPubkeys($lists.get(other)).includes(target), - ) - - return projection(derived(this.ctx.use(FollowLists).index.$, read), () => - read(this.ctx.use(FollowLists).index.get()), + ), ) - } followsWhoMute = (pubkey: string, target: string): Projection => { const read = ($follows: ReadonlyMap, $mutes: ReadonlyMap) =>