From 5c061cad4816b0f5c33a41d68c59bad0f4bb86fb Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Tue, 25 Jul 2023 14:02:21 -0500 Subject: [PATCH] rebuild event requester --- .changeset/soft-walls-wink.md | 5 + .changeset/tender-lions-sort.md | 5 + src/classes/cached-pubkey-event-requester.ts | 42 --- src/classes/pubkey-event-requester.ts | 172 ------------ .../debug-modals/user-debug-modal.tsx | 5 +- src/components/layout/index.tsx | 10 +- src/services/db/index.ts | 27 +- src/services/db/schema.ts | 16 ++ src/services/replaceable-event-requester.ts | 254 ++++++++++++++++++ src/services/settings/user-app-settings.ts | 27 +- src/services/user-contacts.ts | 37 +-- src/services/user-metadata.ts | 33 ++- src/services/user-relays.ts | 23 +- 13 files changed, 347 insertions(+), 309 deletions(-) create mode 100644 .changeset/soft-walls-wink.md create mode 100644 .changeset/tender-lions-sort.md delete mode 100644 src/classes/cached-pubkey-event-requester.ts delete mode 100644 src/classes/pubkey-event-requester.ts create mode 100644 src/services/replaceable-event-requester.ts diff --git a/.changeset/soft-walls-wink.md b/.changeset/soft-walls-wink.md new file mode 100644 index 000000000..6377b3869 --- /dev/null +++ b/.changeset/soft-walls-wink.md @@ -0,0 +1,5 @@ +--- +"nostrudel": minor +--- + +Add expiration to cached metadata events diff --git a/.changeset/tender-lions-sort.md b/.changeset/tender-lions-sort.md new file mode 100644 index 000000000..1714d2425 --- /dev/null +++ b/.changeset/tender-lions-sort.md @@ -0,0 +1,5 @@ +--- +"nostrudel": minor +--- + +Rebuild underlying event requester classes diff --git a/src/classes/cached-pubkey-event-requester.ts b/src/classes/cached-pubkey-event-requester.ts deleted file mode 100644 index ccd9cc429..000000000 --- a/src/classes/cached-pubkey-event-requester.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NostrEvent } from "../types/nostr-event"; -import { PubkeyEventRequester } from "./pubkey-event-requester"; - -export class CachedPubkeyEventRequester extends PubkeyEventRequester { - private readCacheDedupe = new Map>(); - async readCache(pubkey: string): Promise { - return undefined; - } - async writeCache(pubkey: string, event: NostrEvent): Promise {} - - handleEvent(event: NostrEvent) { - const sub = this.getSubject(event.pubkey); - if (!sub.value || event.created_at > sub.value.created_at) { - this.writeCache(event.pubkey, event); - } - super.handleEvent(event); - } - - requestEvent(pubkey: string, relays: string[], alwaysRequest = false) { - const sub = this.getSubject(pubkey); - - if (!sub.value) { - // only call this.readCache once per pubkey - if (!this.readCacheDedupe.has(pubkey)) { - const promise = this.readCacheDedupe.get(pubkey) || this.readCache(pubkey); - this.readCacheDedupe.set(pubkey, promise); - - promise.then((cached) => { - this.readCacheDedupe.delete(pubkey); - - if (cached) this.handleEvent(cached); - - if (!sub.value || alwaysRequest) super.requestEvent(pubkey, relays); - }); - } - } else if (alwaysRequest) { - super.requestEvent(pubkey, relays); - } - - return sub; - } -} diff --git a/src/classes/pubkey-event-requester.ts b/src/classes/pubkey-event-requester.ts deleted file mode 100644 index 6bc7f2f00..000000000 --- a/src/classes/pubkey-event-requester.ts +++ /dev/null @@ -1,172 +0,0 @@ -import dayjs from "dayjs"; -import debug, { Debugger } from "debug"; -import { NostrSubscription } from "./nostr-subscription"; -import { SuperMap } from "./super-map"; -import { NostrEvent } from "../types/nostr-event"; -import Subject from "./subject"; -import { NostrQuery } from "../types/nostr-query"; -import { nameOrPubkey } from "../helpers/debug"; - -type pubkey = string; -type relay = string; - -class PubkeyEventRequestSubscription { - private subscription: NostrSubscription; - private kind: number; - private dTag?: string; - - private subjects = new SuperMap>(() => new Subject()); - - private requestNext = new Set(); - - private requestedPubkeys = new Map(); - - log: Debugger; - - constructor(relay: string, kind: number, name?: string, dTag?: string, log?: Debugger) { - this.kind = kind; - this.dTag = dTag; - this.subscription = new NostrSubscription(relay, undefined, name); - - this.subscription.onEvent.subscribe(this.handleEvent.bind(this)); - this.subscription.onEOSE.subscribe(this.handleEOSE.bind(this)); - - this.log = log || debug("misc"); - } - - private handleEvent(event: NostrEvent) { - // reject the event if its the wrong kind - if (event.kind !== this.kind) return; - // reject the event if has the wrong d tag or is missing one - if (this.dTag && !event.tags.some((t) => t[0] === "d" && t[1] === this.dTag)) return; - - // remove the pubkey from the waiting list - this.requestedPubkeys.delete(event.pubkey); - - const sub = this.subjects.get(event.pubkey); - - const current = sub.value; - if (!current || event.created_at > current.created_at) { - this.log(`Found newer event for ${nameOrPubkey(event.pubkey)}`); - sub.next(event); - } - } - private handleEOSE() { - // relays says it has nothing left - this.requestedPubkeys.clear(); - } - - getSubject(pubkey: string) { - return this.subjects.get(pubkey); - } - - requestEvent(pubkey: string) { - const sub = this.subjects.get(pubkey); - - if (!sub.value) { - this.log(`Adding ${nameOrPubkey(pubkey)} to queue`); - this.requestNext.add(pubkey); - } - - return sub; - } - - update() { - let needsUpdate = false; - for (const pubkey of this.requestNext) { - if (!this.requestedPubkeys.has(pubkey)) { - this.requestedPubkeys.set(pubkey, new Date()); - needsUpdate = true; - } - } - this.requestNext.clear(); - - // prune pubkeys - const timeout = dayjs().subtract(1, "minute"); - for (const [pubkey, date] of this.requestedPubkeys) { - if (dayjs(date).isBefore(timeout)) { - this.requestedPubkeys.delete(pubkey); - needsUpdate = true; - this.log(`Request for ${nameOrPubkey(pubkey)} expired`); - } - } - - // update the subscription - if (needsUpdate) { - if (this.requestedPubkeys.size > 0) { - const query: NostrQuery = { authors: Array.from(this.requestedPubkeys.keys()), kinds: [this.kind] }; - if (this.dTag) query["#d"] = [this.dTag]; - - this.log(`Updating query with ${query.authors?.length} pubkeys`); - this.subscription.setQuery(query); - - if (this.subscription.state !== NostrSubscription.OPEN) { - this.subscription.open(); - } - } else if (this.subscription.state === NostrSubscription.OPEN) { - this.subscription.close(); - } - } - } -} - -export class PubkeyEventRequester { - private kind: number; - private name?: string; - private dTag?: string; - private subjects = new SuperMap>(() => new Subject()); - - private subscriptions = new SuperMap( - (relay) => new PubkeyEventRequestSubscription(relay, this.kind, this.name, this.dTag, this.log.extend(relay)) - ); - - log: Debugger; - - constructor(kind: number, name?: string, dTag?: string, log?: Debugger) { - this.kind = kind; - this.name = name; - this.dTag = dTag; - - this.log = log || debug("misc"); - } - - getSubject(pubkey: string) { - return this.subjects.get(pubkey); - } - - handleEvent(event: NostrEvent) { - if (event.kind !== this.kind) return; - - const sub = this.subjects.get(event.pubkey); - const current = sub.value; - if (!current || event.created_at > current.created_at) { - this.log(`New event for ${nameOrPubkey(event.pubkey)}`); - sub.next(event); - } - } - - requestEvent(pubkey: string, relays: string[]) { - this.log(`Requesting event for ${nameOrPubkey(pubkey)}`); - const sub = this.subjects.get(pubkey); - - for (const relay of relays) { - const relaySub = this.subscriptions.get(relay).requestEvent(pubkey); - - sub.connectWithHandler(relaySub, (event, next, current) => { - if (event.kind !== this.kind) return; - if (!current || event.created_at > current.created_at) { - this.log(`Event for ${nameOrPubkey(event.pubkey)} from connection`); - next(event); - } - }); - } - - return sub; - } - - update() { - for (const [relay, subscription] of this.subscriptions) { - subscription.update(); - } - } -} diff --git a/src/components/debug-modals/user-debug-modal.tsx b/src/components/debug-modals/user-debug-modal.tsx index 27c33ffb7..073850d86 100644 --- a/src/components/debug-modals/user-debug-modal.tsx +++ b/src/components/debug-modals/user-debug-modal.tsx @@ -6,14 +6,15 @@ import { useUserMetadata } from "../../hooks/use-user-metadata"; import RawValue from "./raw-value"; import RawJson from "./raw-json"; import { useSharableProfileId } from "../../hooks/use-shareable-profile-id"; -import userRelaysService from "../../services/user-relays"; import useUserLNURLMetadata from "../../hooks/use-user-lnurl-metadata"; +import replaceableEventLoaderService from "../../services/replaceable-event-requester"; +import { Kind } from "nostr-tools"; export default function UserDebugModal({ pubkey, ...props }: { pubkey: string } & Omit) { const npub = useMemo(() => normalizeToBech32(pubkey, Bech32Prefix.Pubkey), [pubkey]); const metadata = useUserMetadata(pubkey); const nprofile = useSharableProfileId(pubkey); - const relays = userRelaysService.requester.getSubject(pubkey).value; + const relays = replaceableEventLoaderService.getEvent(Kind.RelayList, pubkey).value; const tipMetadata = useUserLNURLMetadata(pubkey); return ( diff --git a/src/components/layout/index.tsx b/src/components/layout/index.tsx index aa8c1e471..fa9db03de 100644 --- a/src/components/layout/index.tsx +++ b/src/components/layout/index.tsx @@ -15,7 +15,15 @@ export default function Layout({ children }: { children: React.ReactNode }) { {!isMobile && } - + {children} {isMobile && ( diff --git a/src/services/db/index.ts b/src/services/db/index.ts index bb20cd2c0..f69cecf10 100644 --- a/src/services/db/index.ts +++ b/src/services/db/index.ts @@ -1,11 +1,11 @@ import { openDB, deleteDB } from "idb"; import { IDBPDatabase } from "idb"; -import { SchemaV1, SchemaV2 } from "./schema"; +import { SchemaV1, SchemaV2, SchemaV3 } from "./schema"; const dbName = "storage"; -const version = 2; -const db = await openDB(dbName, version, { +const version = 3; +const db = await openDB(dbName, version, { upgrade(db, oldVersion, newVersion, transaction, event) { if (oldVersion < 1) { const v0 = db as unknown as IDBPDatabase; @@ -56,14 +56,29 @@ const db = await openDB(dbName, version, { }); settings.createIndex("created_at", "created_at"); } + + if (oldVersion < 3) { + const v2 = db as unknown as IDBPDatabase; + const v3 = db as unknown as IDBPDatabase; + + // rename the old event caches + v3.deleteObjectStore("userMetadata"); + v3.deleteObjectStore("userContacts"); + v3.deleteObjectStore("userRelays"); + v3.deleteObjectStore("settings"); + + // create new replaceable event object store + const settings = v3.createObjectStore("replaceableEvents", { + keyPath: "addr", + }); + settings.createIndex("created", "created"); + } }, }); export async function clearCacheData() { - await db.clear("userMetadata"); - await db.clear("userContacts"); + await db.clear("replaceableEvents"); await db.clear("userFollows"); - await db.clear("userRelays"); await db.clear("relayInfo"); await db.clear("dnsIdentifiers"); await db.clear("relayScoreboardStats"); diff --git a/src/services/db/schema.ts b/src/services/db/schema.ts index eeb999381..aabc91b95 100644 --- a/src/services/db/schema.ts +++ b/src/services/db/schema.ts @@ -61,3 +61,19 @@ export interface SchemaV2 extends SchemaV1 { value: any; }; } + +export interface SchemaV3 { + replaceableEvents: { + key: string; + value: { + addr: string; + created: number; + event: NostrEvent; + }; + }; + userFollows: SchemaV2["userFollows"]; + dnsIdentifiers: SchemaV2["dnsIdentifiers"]; + relayInfo: SchemaV2["relayInfo"]; + relayScoreboardStats: SchemaV2["relayScoreboardStats"]; + misc: SchemaV2["misc"]; +} diff --git a/src/services/replaceable-event-requester.ts b/src/services/replaceable-event-requester.ts new file mode 100644 index 000000000..70e1baffb --- /dev/null +++ b/src/services/replaceable-event-requester.ts @@ -0,0 +1,254 @@ +import dayjs from "dayjs"; +import debug, { Debugger } from "debug"; +import { NostrSubscription } from "../classes/nostr-subscription"; +import { SuperMap } from "../classes/super-map"; +import { NostrEvent } from "../types/nostr-event"; +import Subject from "../classes/subject"; +import { NostrQuery } from "../types/nostr-query"; +import { logger, nameOrPubkey } from "../helpers/debug"; +import db from "./db"; + +type Pubkey = string; +type Relay = string; + +export function getReadableAddr(kind: number, pubkey: string, d?: string) { + return `${kind}:${nameOrPubkey(pubkey)}${d ? ":" + d : ""}`; +} +export function getAddr(kind: number, pubkey: string, d?: string) { + return `${kind}:${pubkey}${d ? ":" + d : ""}`; +} + +class ReplaceableEventRelayLoader { + private subscription: NostrSubscription; + private events = new SuperMap>(() => new Subject()); + + private requestNext = new Set(); + private requested = new Map(); + + log: Debugger; + + constructor(relay: string, log?: Debugger) { + this.subscription = new NostrSubscription(relay, undefined, `replaceable-event-loader`); + + this.subscription.onEvent.subscribe(this.handleEvent.bind(this)); + this.subscription.onEOSE.subscribe(this.handleEOSE.bind(this)); + + this.log = log || debug("misc"); + } + + private handleEvent(event: NostrEvent) { + const d = event.tags.find((t) => t[0] === "d" && t[1])?.[1]; + const addr = getAddr(event.kind, event.pubkey, d); + + // remove the pubkey from the waiting list + this.requested.delete(addr); + + const sub = this.events.get(addr); + + const current = sub.value; + if (!current || event.created_at > current.created_at) { + sub.next(event); + } + } + private handleEOSE() { + // relays says it has nothing left + this.requested.clear(); + } + + getEvent(kind: number, pubkey: string, d?: string) { + return this.events.get(getAddr(kind, pubkey, d)); + } + + requestEvent(kind: number, pubkey: string, d?: string) { + const addr = getAddr(kind, pubkey, d); + const event = this.events.get(addr); + + if (!event.value) { + this.requestNext.add(addr); + } + + return event; + } + + update() { + let needsUpdate = false; + for (const addr of this.requestNext) { + if (!this.requested.has(addr)) { + this.requested.set(addr, new Date()); + needsUpdate = true; + } + } + this.requestNext.clear(); + + // prune requests + const timeout = dayjs().subtract(1, "minute"); + for (const [addr, date] of this.requested) { + if (dayjs(date).isBefore(timeout)) { + this.requested.delete(addr); + needsUpdate = true; + } + } + + // update the subscription + if (needsUpdate) { + if (this.requested.size > 0) { + const filters: Record = {}; + + for (const [addr] of this.requested) { + const [kindStr, pubkey, d] = addr.split(":") as [string, string] | [string, string, string]; + const kind = parseInt(kindStr); + filters[kind] = filters[kind] || { kinds: [kind] }; + + const arr = (filters[kind].authors = filters[kind].authors || []); + arr.push(pubkey); + + if (d) { + const arr = (filters[kind]["#d"] = filters[kind]["#d"] || []); + arr.push(d); + } + } + + const query = Array.from(Object.values(filters)); + + this.log( + `Updating query`, + Array.from(Object.keys(filters)) + .map((kind: string) => `kind ${kind}: ${filters[parseInt(kind)].authors?.length}`) + .join(", ") + ); + this.subscription.setQuery(query); + + if (this.subscription.state !== NostrSubscription.OPEN) { + this.subscription.open(); + } + } else if (this.subscription.state === NostrSubscription.OPEN) { + this.subscription.close(); + } + } + } +} + +class ReplaceableEventLoaderService { + private events = new SuperMap>(() => new Subject()); + + private loaders = new SuperMap( + (relay) => new ReplaceableEventRelayLoader(relay, this.log.extend(relay)) + ); + + log = logger.extend("ReplaceableEventLoader"); + + handleEvent(event: NostrEvent) { + const d = event.tags.find((t) => t[0] === "d" && t[1])?.[1]; + const addr = getAddr(event.kind, event.pubkey, d); + + const sub = this.events.get(addr); + const current = sub.value; + if (!current || event.created_at > current.created_at) { + sub.next(event); + this.saveToCache(addr, event); + } + } + + getEvent(kind: number, pubkey: string, d?: string) { + return this.events.get(getAddr(kind, pubkey, d)); + } + + private loadCacheDedupe = new Map>(); + private loadFromCache(addr: string) { + const dedupe = this.loadCacheDedupe.get(addr); + if (dedupe) return dedupe; + + const promise = db.get("replaceableEvents", addr).then((cached) => { + this.loadCacheDedupe.delete(addr); + if (cached?.event) { + this.handleEvent(cached.event); + return true; + } + return false; + }); + + this.loadCacheDedupe.set(addr, promise); + + return promise; + } + private async saveToCache(addr: string, event: NostrEvent) { + await db.put("replaceableEvents", { addr, event, created: dayjs().unix() }); + } + + async pruneCache() { + const keys = await db.getAllKeysFromIndex( + "replaceableEvents", + "created", + IDBKeyRange.upperBound(dayjs().subtract(1, "day").unix()) + ); + + this.log(`Pruning ${keys.length} events`); + + const transaction = db.transaction("replaceableEvents", "readwrite"); + for (const key of keys) { + transaction.store.delete(key); + } + await transaction.commit(); + } + + private requestEventFromRelays(relays: string[], kind: number, pubkey: string, d?: string) { + const addr = getAddr(kind, pubkey, d); + const sub = this.events.get(addr); + + for (const relay of relays) { + const request = this.loaders.get(relay).requestEvent(kind, pubkey, d); + + sub.connectWithHandler(request, (event, next, current) => { + if (!current || event.created_at > current.created_at) { + next(event); + this.saveToCache(addr, event); + } + }); + } + + return sub; + } + + requestEvent(relays: string[], kind: number, pubkey: string, d?: string, alwaysRequest = false) { + const addr = getAddr(kind, pubkey, d); + const sub = this.events.get(addr); + + if (!sub.value) { + this.loadFromCache(addr).then((loaded) => { + if (!loaded) { + this.requestEventFromRelays(relays, kind, pubkey, d); + } + }); + } + + if (alwaysRequest) { + this.requestEventFromRelays(relays, kind, pubkey, d); + } + + return sub; + } + + update() { + for (const [relay, loader] of this.loaders) { + loader.update(); + } + } +} + +const replaceableEventLoaderService = new ReplaceableEventLoaderService(); + +replaceableEventLoaderService.pruneCache(); + +setInterval(() => { + replaceableEventLoaderService.update(); +}, 1000 * 2); +setInterval(() => { + replaceableEventLoaderService.pruneCache(); +}, 1000 * 60 * 60); + +if (import.meta.env.DEV) { + //@ts-ignore + window.replaceableEventLoaderService = replaceableEventLoaderService; +} + +export default replaceableEventLoaderService; diff --git a/src/services/settings/user-app-settings.ts b/src/services/settings/user-app-settings.ts index 3bc22c702..6e6d1f012 100644 --- a/src/services/settings/user-app-settings.ts +++ b/src/services/settings/user-app-settings.ts @@ -1,45 +1,30 @@ import dayjs from "dayjs"; import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event"; -import db from "../db"; -import { logger } from "../../helpers/debug"; import { SuperMap } from "../../classes/super-map"; import { PersistentSubject } from "../../classes/subject"; -import { CachedPubkeyEventRequester } from "../../classes/cached-pubkey-event-requester"; import { AppSettings, defaultSettings, parseAppSettings } from "./migrations"; +import replaceableEventLoaderService from "../replaceable-event-requester"; const DTAG = "nostrudel-settings"; class UserAppSettings { - requester: CachedPubkeyEventRequester; - log = logger.extend("UserAppSettings"); - - constructor() { - this.requester = new CachedPubkeyEventRequester(30078, "user-app-data", DTAG, this.log.extend("requester")); - this.requester.readCache = (pubkey) => db.get("settings", pubkey); - this.requester.writeCache = (pubkey, event) => db.put("settings", event); - } - private parsedSubjects = new SuperMap>( - (pubkey) => new PersistentSubject(defaultSettings) + () => new PersistentSubject(defaultSettings) ); getSubject(pubkey: string) { return this.parsedSubjects.get(pubkey); } requestAppSettings(pubkey: string, relays: string[], alwaysRequest = false) { const sub = this.parsedSubjects.get(pubkey); - const requestSub = this.requester.requestEvent(pubkey, relays, alwaysRequest); + const requestSub = replaceableEventLoaderService.requestEvent(relays, 30078, pubkey, DTAG, alwaysRequest); sub.connectWithHandler(requestSub, (event, next) => next(parseAppSettings(event))); return sub; } receiveEvent(event: NostrEvent) { - this.requester.handleEvent(event); - } - - update() { - this.requester.update(); + replaceableEventLoaderService.handleEvent(event); } buildAppSettingsEvent(settings: AppSettings): DraftNostrEvent { @@ -54,10 +39,6 @@ class UserAppSettings { const userAppSettings = new UserAppSettings(); -setInterval(() => { - userAppSettings.update(); -}, 1000 * 2); - if (import.meta.env.DEV) { // @ts-ignore window.userAppSettings = userAppSettings; diff --git a/src/services/user-contacts.ts b/src/services/user-contacts.ts index 48ac69771..d6dfcd2c1 100644 --- a/src/services/user-contacts.ts +++ b/src/services/user-contacts.ts @@ -1,11 +1,11 @@ import { isPTag, NostrEvent } from "../types/nostr-event"; import { safeJson } from "../helpers/parse"; -import db from "./db"; -import { CachedPubkeyEventRequester } from "../classes/cached-pubkey-event-requester"; import { SuperMap } from "../classes/super-map"; import Subject from "../classes/subject"; import { RelayConfig, RelayMode } from "../classes/relay"; import { normalizeRelayConfigs } from "../helpers/relay"; +import replaceableEventLoaderService from "./replaceable-event-requester"; +import { Kind } from "nostr-tools"; export type UserContacts = { pubkey: string; @@ -48,21 +48,6 @@ function parseContacts(event: NostrEvent): UserContacts { } class UserContactsService { - requester: CachedPubkeyEventRequester; - - constructor() { - this.requester = new CachedPubkeyEventRequester(3, "user-contacts"); - this.requester.readCache = this.readCache; - this.requester.writeCache = this.writeCache; - } - - readCache(pubkey: string) { - return db.get("userContacts", pubkey); - } - writeCache(pubkey: string, event: NostrEvent) { - return db.put("userContacts", event); - } - private subjects = new SuperMap>(() => new Subject()); getSubject(pubkey: string) { return this.subjects.get(pubkey); @@ -70,7 +55,13 @@ class UserContactsService { requestContacts(pubkey: string, relays: string[], alwaysRequest = false) { const sub = this.subjects.get(pubkey); - const requestSub = this.requester.requestEvent(pubkey, relays, alwaysRequest); + const requestSub = replaceableEventLoaderService.requestEvent( + relays, + Kind.Contacts, + pubkey, + undefined, + alwaysRequest + ); sub.connectWithHandler(requestSub, (event, next) => next(parseContacts(event))); @@ -78,20 +69,12 @@ class UserContactsService { } receiveEvent(event: NostrEvent) { - this.requester.handleEvent(event); - } - - update() { - this.requester.update(); + replaceableEventLoaderService.handleEvent(event); } } const userContactsService = new UserContactsService(); -setInterval(() => { - userContactsService.update(); -}, 1000 * 2); - if (import.meta.env.DEV) { // @ts-ignore window.userContactsService = userContactsService; diff --git a/src/services/user-metadata.ts b/src/services/user-metadata.ts index e7c7caf1c..49bef6a92 100644 --- a/src/services/user-metadata.ts +++ b/src/services/user-metadata.ts @@ -1,17 +1,18 @@ import db from "./db"; -import { CachedPubkeyEventRequester } from "../classes/cached-pubkey-event-requester"; import { NostrEvent } from "../types/nostr-event"; import { Kind0ParsedContent, parseKind0Event } from "../helpers/user-metadata"; import { SuperMap } from "../classes/super-map"; import Subject from "../classes/subject"; +import replaceableEventLoaderService from "./replaceable-event-requester"; +import { Kind } from "nostr-tools"; class UserMetadataService { - requester: CachedPubkeyEventRequester; - constructor() { - this.requester = new CachedPubkeyEventRequester(0, "user-metadata"); - this.requester.readCache = this.readCache; - this.requester.writeCache = this.writeCache; - } + // requester: CachedPubkeyEventRequester; + // constructor() { + // this.requester = new CachedPubkeyEventRequester(0, "user-metadata"); + // this.requester.readCache = this.readCache; + // this.requester.writeCache = this.writeCache; + // } readCache(pubkey: string) { return db.get("userMetadata", pubkey); @@ -26,26 +27,24 @@ class UserMetadataService { } requestMetadata(pubkey: string, relays: string[], alwaysRequest = false) { const sub = this.parsedSubjects.get(pubkey); - const requestSub = this.requester.requestEvent(pubkey, relays, alwaysRequest); + const requestSub = replaceableEventLoaderService.requestEvent( + relays, + Kind.Metadata, + pubkey, + undefined, + alwaysRequest + ); sub.connectWithHandler(requestSub, (event, next) => next(parseKind0Event(event))); return sub; } receiveEvent(event: NostrEvent) { - this.requester.handleEvent(event); - } - - update() { - this.requester.update(); + replaceableEventLoaderService.handleEvent(event); } } const userMetadataService = new UserMetadataService(); -setInterval(() => { - userMetadataService.update(); -}, 1000 * 2); - if (import.meta.env.DEV) { // @ts-ignore window.userMetadataService = userMetadataService; diff --git a/src/services/user-relays.ts b/src/services/user-relays.ts index d8f172ee2..53f0d02ec 100644 --- a/src/services/user-relays.ts +++ b/src/services/user-relays.ts @@ -1,12 +1,12 @@ -import db from "./db"; import { isRTag, NostrEvent } from "../types/nostr-event"; import { RelayConfig } from "../classes/relay"; import { parseRTag } from "../helpers/nostr-event"; -import { CachedPubkeyEventRequester } from "../classes/cached-pubkey-event-requester"; import { SuperMap } from "../classes/super-map"; import Subject from "../classes/subject"; import { normalizeRelayConfigs } from "../helpers/relay"; import userContactsService from "./user-contacts"; +import replaceableEventLoaderService from "./replaceable-event-requester"; +import { Kind } from "nostr-tools"; export type ParsedUserRelays = { pubkey: string; @@ -23,20 +23,13 @@ function parseRelaysEvent(event: NostrEvent): ParsedUserRelays { } class UserRelaysService { - requester: CachedPubkeyEventRequester; - constructor() { - this.requester = new CachedPubkeyEventRequester(10002, "user-relays"); - this.requester.readCache = (pubkey) => db.get("userRelays", pubkey); - this.requester.writeCache = (pubkey, event) => db.put("userRelays", event); - } - private subjects = new SuperMap>(() => new Subject()); getRelays(pubkey: string) { return this.subjects.get(pubkey); } requestRelays(pubkey: string, relays: string[], alwaysRequest = false) { const sub = this.subjects.get(pubkey); - const requestSub = this.requester.requestEvent(pubkey, relays, alwaysRequest); + const requestSub = replaceableEventLoaderService.requestEvent(relays, Kind.RelayList, pubkey); sub.connectWithHandler(requestSub, (event, next) => next(parseRelaysEvent(event))); // also fetch the relays from the users contacts @@ -51,20 +44,12 @@ class UserRelaysService { } receiveEvent(event: NostrEvent) { - this.requester.handleEvent(event); - } - - update() { - this.requester.update(); + replaceableEventLoaderService.handleEvent(event); } } const userRelaysService = new UserRelaysService(); -setInterval(() => { - userRelaysService.update(); -}, 1000 * 2); - if (import.meta.env.DEV) { // @ts-ignore window.userRelaysService = userRelaysService;