From 6e24f493848cf3252060794f9515d93c01ec8f12 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 9 Oct 2024 09:48:08 -0700 Subject: [PATCH] Add default feed loader to app --- package-lock.json | 2 ++ packages/app/package.json | 2 ++ packages/app/src/feeds.ts | 68 ++++++++++++++++++++++++++++++++++++ packages/app/src/handles.ts | 6 ++-- packages/app/src/index.ts | 1 + packages/app/src/wot.ts | 20 ++++++++++- packages/feeds/src/loader.ts | 6 ++-- packages/lib/package.json | 2 +- packages/lib/src/Tools.ts | 12 +++++++ 9 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 packages/app/src/feeds.ts diff --git a/package-lock.json b/package-lock.json index 7274bf7..9258ca2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3639,6 +3639,8 @@ "version": "0.0.11", "license": "MIT", "dependencies": { + "@welshman/dvm": "~0.0.9", + "@welshman/feeds": "~0.0.19", "@welshman/lib": "~0.0.19", "@welshman/net": "~0.0.24", "@welshman/signer": "~0.0.7", diff --git a/packages/app/package.json b/packages/app/package.json index ca2d9f3..02ddef4 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -32,6 +32,8 @@ }, "dependencies": { "@welshman/lib": "~0.0.19", + "@welshman/feeds": "~0.0.19", + "@welshman/dvm": "~0.0.9", "@welshman/net": "~0.0.24", "@welshman/signer": "~0.0.7", "@welshman/store": "~0.0.9", diff --git a/packages/app/src/feeds.ts b/packages/app/src/feeds.ts new file mode 100644 index 0000000..e12b26d --- /dev/null +++ b/packages/app/src/feeds.ts @@ -0,0 +1,68 @@ +import {ctx, now} from '@welshman/lib' +import {createEvent, getPubkeyTagValues} from '@welshman/util' +import {FeedLoader, Scope} from '@welshman/feeds' +import {makeDvmRequest} from '@welshman/dvm' +import {makeSecret, Nip01Signer} from '@welshman/signer' +import {pubkey, signer} from './session' +import {getFilterSelections} from './router' +import {wotGraph, maxWot, getFollows, getNetwork, getFollowers} from './wot' +import {load} from './core' + +export const feedLoader = new FeedLoader({ + request: async ({filters = [{}], relays = [], onEvent}) => { + if (relays.length > 0) { + await load({onEvent, filters, relays}) + } else { + await Promise.all( + getFilterSelections(filters) + .map(opts => load({onEvent, ...opts})) + ) + } + }, + requestDVM: async ({kind, onEvent, ...request}) => { + const tags = [...request.tags || [], ["expiration", String(now() + 5)]] + const $signer = signer.get() || new Nip01Signer(makeSecret()) + const event = await $signer.sign(createEvent(kind, {tags})) + const relays = + request.relays + ? ctx.app.router.fromRelays(request.relays).getUrls() + : ctx.app.router.FromPubkeys(getPubkeyTagValues(tags)).getUrls() + + const req = makeDvmRequest({event, relays}) + + await new Promise(resolve => { + req.emitter.on("result", (url, event) => { + onEvent(event) + resolve() + }) + }) + }, + getPubkeysForScope: (scope: string) => { + const $pubkey = pubkey.get() + + if (!$pubkey) { + return [] + } + + switch (scope) { + case Scope.Self: return [$pubkey] + case Scope.Follows: return getFollows($pubkey) + case Scope.Network: return getNetwork($pubkey) + case Scope.Followers: return getFollowers($pubkey) + default: return [] + } + }, + getPubkeysForWOTRange: (min, max) => { + const pubkeys = [] + const thresholdMin = maxWot.get() * min + const thresholdMax = maxWot.get() * max + + for (const [tpk, score] of wotGraph.get().entries()) { + if (score >= thresholdMin && score <= thresholdMax) { + pubkeys.push(tpk) + } + } + + return pubkeys + }, +}) diff --git a/packages/app/src/handles.ts b/packages/app/src/handles.ts index dee6087..2e476f9 100644 --- a/packages/app/src/handles.ts +++ b/packages/app/src/handles.ts @@ -108,5 +108,7 @@ export const deriveHandleForPubkey = (pubkey: string, request: Partial - handle.nip05.startsWith("_@") ? last(handle.nip05.split("@")) : handle.nip05 +export const displayNip05 = (nip05: string) => + (nip05?.startsWith("_@") ? last(nip05.split("@")) : nip05) + +export const displayHandle = (handle: Handle) => displayNip05(handle.nip05) diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index d08665e..c296200 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -2,6 +2,7 @@ export * from './context' export * from './core' export * from './collection' export * from './commands' +export * from './feeds' export * from './freshness' export * from './follows' export * from './handles' diff --git a/packages/app/src/wot.ts b/packages/app/src/wot.ts index 84757aa..39cb069 100644 --- a/packages/app/src/wot.ts +++ b/packages/app/src/wot.ts @@ -1,6 +1,6 @@ import {throttle} from 'throttle-debounce' import {derived, writable} from 'svelte/store' -import {addToMapKey, inc, dec} from '@welshman/lib' +import {max, addToMapKey, inc, dec} from '@welshman/lib' import {getListTags, getPubkeyTagValues} from '@welshman/util' import {throttled, withGetter} from '@welshman/store' import {pubkey} from './session' @@ -13,6 +13,22 @@ export const getFollows = (pubkey: string) => export const getMutes = (pubkey: string) => getPubkeyTagValues(getListTags(mutesByPubkey.get().get(pubkey))) +export const getNetwork = (pubkey: string) => { + const pubkeys = new Set(getFollows(pubkey)) + const network = new Set() + + for (const follow of pubkeys) { + for (const tpk of getFollows(follow)) { + if (!pubkeys.has(tpk)) { + network.add(tpk) + } + } + } + + return Array.from(network) +} + + export const followersByPubkey = withGetter( derived( throttled(1000, follows), @@ -61,6 +77,8 @@ export const getFollowsWhoMute = (pubkey: string, target: string) => export const wotGraph = withGetter(writable(new Map())) +export const maxWot = withGetter(derived(wotGraph, $g => max(Array.from($g.values())))) + const buildGraph = throttle(1000, () => { const $pubkey = pubkey.get() const $graph = new Map() diff --git a/packages/feeds/src/loader.ts b/packages/feeds/src/loader.ts index e373c40..826f334 100644 --- a/packages/feeds/src/loader.ts +++ b/packages/feeds/src/loader.ts @@ -1,4 +1,4 @@ -import {inc, max, min, now} from '@welshman/lib' +import {inc, omitVals, max, min, now} from '@welshman/lib' import type {TrustedEvent, Filter} from '@welshman/util' import {EPOCH, trimFilters, guessFilterDelta} from '@welshman/util' import type {Feed, RequestItem, FeedOptions} from './core' @@ -98,7 +98,7 @@ export class FeedLoader { let count = 0 - await this.options.request({ + await this.options.request(omitVals([undefined], { relays, filters: trimFilters(requestFilters), onEvent: (event: TrustedEvent) => { @@ -106,7 +106,7 @@ export class FeedLoader { until = Math.min(until, event.created_at - 1) onEvent?.(event) }, - }) + })) if (useWindowing) { if (since === minSince) { diff --git a/packages/lib/package.json b/packages/lib/package.json index 9b0b9a4..5e4b0ee 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -1,6 +1,6 @@ { "name": "@welshman/lib", - "version": "0.0.19", + "version": "0.0.20", "author": "hodlbod", "license": "MIT", "description": "A collection of utilities.", diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index fe9d066..65f764f 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -47,6 +47,18 @@ export const omit = >(ks: string[], x: T) => { return r } +export const omitVals = >(xs: any[], x: T) => { + const r: Record = {} + + for (const [k, v] of Object.entries(x)) { + if (!xs.includes(v)) { + r[k] = v + } + } + + return r as T +} + export const pick = >(ks: string[], x: T) => { const r: T = {...x}