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