diff --git a/src/classes/batch-kind-loader.ts b/src/classes/batch-kind-loader.ts new file mode 100644 index 000000000..799562cfd --- /dev/null +++ b/src/classes/batch-kind-loader.ts @@ -0,0 +1,118 @@ +import dayjs from "dayjs"; +import { Filter, NostrEvent } from "nostr-tools"; +import _throttle from "lodash.throttle"; +import debug, { Debugger } from "debug"; + +import NostrSubscription from "./nostr-subscription"; +import EventStore from "./event-store"; +import { getEventCoordinate } from "../helpers/nostr/events"; + +export function createCoordinate(kind: number, pubkey: string, d?: string) { + return `${kind}:${pubkey}${d ? ":" + d : ""}`; +} + +const RELAY_REQUEST_BATCH_TIME = 500; + +/** This class is ued to batch requests by kind to a single relay */ +export default class BatchKindLoader { + private subscription: NostrSubscription; + events = new EventStore(); + + 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("RelayBatchLoader"); + } + + private handleEvent(event: NostrEvent) { + const key = getEventCoordinate(event); + + // remove the key from the waiting list + this.requested.delete(key); + + const current = this.events.getEvent(key); + if (!current || event.created_at > current.created_at) { + this.events.addEvent(event); + } + } + private handleEOSE() { + // relays says it has nothing left + this.requested.clear(); + } + + requestEvent(kind: number, pubkey: string, d?: string) { + const key = createCoordinate(kind, pubkey, d); + const event = this.events.getEvent(key); + if (!event) { + this.requestNext.add(key); + this.updateThrottle(); + } + return event; + } + + updateThrottle = _throttle(this.update, RELAY_REQUEST_BATCH_TIME); + update() { + let needsUpdate = false; + for (const key of this.requestNext) { + if (!this.requested.has(key)) { + this.requested.set(key, new Date()); + needsUpdate = true; + } + } + this.requestNext.clear(); + + // prune requests + const timeout = dayjs().subtract(1, "minute"); + for (const [key, date] of this.requested) { + if (dayjs(date).isBefore(timeout)) { + this.requested.delete(key); + needsUpdate = true; + } + } + + // update the subscription + if (needsUpdate) { + if (this.requested.size > 0) { + const filters: Record = {}; + + for (const [cord] of this.requested) { + const [kindStr, pubkey, d] = cord.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.setFilters(query); + + if (this.subscription.state !== NostrSubscription.OPEN) { + this.subscription.open(); + } + } else if (this.subscription.state === NostrSubscription.OPEN) { + this.subscription.close(); + } + } + } +} diff --git a/src/classes/event-store.ts b/src/classes/event-store.ts index e53302094..ea0a17b92 100644 --- a/src/classes/event-store.ts +++ b/src/classes/event-store.ts @@ -1,4 +1,6 @@ import { NostrEvent } from "nostr-tools"; +import { nanoid } from "nanoid"; + import { getEventUID, sortByDate } from "../helpers/nostr/events"; import ControlledObservable from "./controlled-observable"; import SuperMap from "./super-map"; @@ -6,7 +8,9 @@ import deleteEventService from "../services/delete-events"; export type EventFilter = (event: NostrEvent, store: EventStore) => boolean; +/** a class used to store and sort events */ export default class EventStore { + id = nanoid(8); name?: string; events = new Map(); @@ -34,16 +38,15 @@ export default class EventStore { onClear = new ControlledObservable(); private handleEvent(event: NostrEvent) { - const id = getEventUID(event); - const existing = this.events.get(id); + const uid = getEventUID(event); + const existing = this.events.get(uid); if (!existing || event.created_at > existing.created_at) { - this.events.set(id, event); + this.events.set(uid, event); this.onEvent.next(event); } } addEvent(event: NostrEvent) { - const id = getEventUID(event); this.handleEvent(event); } getEvent(id: string) { diff --git a/src/classes/nostr-multi-subscription.ts b/src/classes/nostr-multi-subscription.ts index 1da4555bf..5ee74d304 100644 --- a/src/classes/nostr-multi-subscription.ts +++ b/src/classes/nostr-multi-subscription.ts @@ -1,8 +1,8 @@ import { nanoid } from "nanoid"; import { NostrEvent } from "../types/nostr-event"; -import { NostrOutgoingRequest, NostrRequestFilter, RelayQueryMap } from "../types/nostr-query"; -import Relay, { IncomingEvent } from "./relay"; +import { NostrRequestFilter, RelayQueryMap } from "../types/nostr-relay"; +import Relay, { IncomingEvent, OutgoingRequest } from "./relay"; import relayPoolService from "../services/relay-pool"; import { isFilterEqual, isQueryMapEqual } from "../helpers/nostr/filter"; import ControlledObservable from "./controlled-observable"; @@ -26,14 +26,14 @@ export default class NostrMultiSubscription { this.id = nanoid(); this.name = name; } - private handleEvent(incomingEvent: IncomingEvent) { + private handleMessage(incomingEvent: IncomingEvent) { if ( this.state === NostrMultiSubscription.OPEN && - incomingEvent.subId === this.id && - !this.seenEvents.has(incomingEvent.body.id) + incomingEvent[1] === this.id && + !this.seenEvents.has(incomingEvent[2].id) ) { - this.onEvent.next(incomingEvent.body); - this.seenEvents.add(incomingEvent.body.id); + this.onEvent.next(incomingEvent[2]); + this.seenEvents.add(incomingEvent[2].id); } } @@ -41,7 +41,7 @@ export default class NostrMultiSubscription { /** listen for event and open events from relays */ private connectToRelay(relay: Relay) { const subs = this.relaySubs.get(relay); - subs.push(relay.onEvent.subscribe(this.handleEvent.bind(this))); + subs.push(relay.onEvent.subscribe(this.handleMessage.bind(this))); subs.push(relay.onOpen.subscribe(this.handleRelayConnect.bind(this))); subs.push(relay.onClose.subscribe(this.handleRelayDisconnect.bind(this))); relayPoolService.addClaim(relay.url, this); @@ -93,9 +93,7 @@ export default class NostrMultiSubscription { for (const relay of this.relays) { const filter = this.queryMap[relay.url]; - const message: NostrOutgoingRequest = Array.isArray(filter) - ? ["REQ", this.id, ...filter] - : ["REQ", this.id, filter]; + const message: OutgoingRequest = Array.isArray(filter) ? ["REQ", this.id, ...filter] : ["REQ", this.id, filter]; const currentFilter = this.relayQueries.get(relay); if (!currentFilter || !isFilterEqual(currentFilter, filter)) { diff --git a/src/classes/nostr-publish-action.ts b/src/classes/nostr-publish-action.ts index dbc098622..fdcff4db2 100644 --- a/src/classes/nostr-publish-action.ts +++ b/src/classes/nostr-publish-action.ts @@ -14,9 +14,10 @@ export default class NostrPublishAction { relays: string[]; event: NostrEvent; - results = new PersistentSubject([]); - onResult = new ControlledObservable(); - onComplete = createDefer(); + results = new PersistentSubject<{ relay: Relay; result: IncomingCommandResult }[]>([]); + + onResult = new ControlledObservable<{ relay: Relay; result: IncomingCommandResult }>(); + onComplete = createDefer<{ relay: Relay; result: IncomingCommandResult }[]>(); private remaining = new Set(); private relayResultSubs = new SuperMap(() => []); @@ -29,38 +30,31 @@ export default class NostrPublishAction { for (const url of relays) { const relay = relayPoolService.requestRelay(url); this.remaining.add(relay); - this.relayResultSubs.get(relay).push(relay.onCommandResult.subscribe(this.handleResult.bind(this))); + this.relayResultSubs.get(relay).push( + relay.onCommandResult.subscribe((result) => { + if (result[1] === this.event.id) this.handleResult(result, relay); + }), + ); - // send event relay.send(["EVENT", event]); } setTimeout(this.handleTimeout.bind(this), timeout); } - private handleResult(result: IncomingCommandResult) { - if (result.eventId === this.event.id) { - const relay = result.relay; - this.results.next([...this.results.value, result]); + private handleResult(result: IncomingCommandResult, relay: Relay) { + this.results.next([...this.results.value, { relay, result }]); + this.onResult.next({ relay, result }); - this.onResult.next(result); - - this.relayResultSubs.get(relay).forEach((s) => s.unsubscribe()); - this.relayResultSubs.delete(relay); - this.remaining.delete(relay); - if (this.remaining.size === 0) this.onComplete.resolve(this.results.value); - } + this.relayResultSubs.get(relay).forEach((s) => s.unsubscribe()); + this.relayResultSubs.delete(relay); + this.remaining.delete(relay); + if (this.remaining.size === 0) this.onComplete.resolve(this.results.value); } private handleTimeout() { for (const relay of this.remaining) { - this.handleResult({ - message: "Timeout", - eventId: this.event.id, - status: false, - type: "OK", - relay, - }); + this.handleResult(["OK", this.event.id, false, "Timeout"], relay); } } } diff --git a/src/classes/nostr-request.ts b/src/classes/nostr-request.ts index 22b3a9c82..838148a61 100644 --- a/src/classes/nostr-request.ts +++ b/src/classes/nostr-request.ts @@ -1,8 +1,9 @@ import { nanoid } from "nanoid"; -import { CountResponse, NostrEvent } from "../types/nostr-event"; -import { NostrRequestFilter } from "../types/nostr-query"; + +import { NostrEvent } from "../types/nostr-event"; +import { NostrRequestFilter } from "../types/nostr-relay"; import relayPoolService from "../services/relay-pool"; -import Relay, { IncomingCount, IncomingEOSE, IncomingEvent } from "./relay"; +import Relay, { CountResponse, IncomingCount, IncomingEOSE, IncomingEvent } from "./relay"; import createDefer from "./deferred"; import ControlledObservable from "./controlled-observable"; import SuperMap from "./super-map"; @@ -19,7 +20,6 @@ export default class NostrRequest { state = NostrRequest.IDLE; onEvent = new ControlledObservable(); onCount = new ControlledObservable(); - /** @deprecated */ onComplete = createDefer(); seenEvents = new Set(); @@ -30,7 +30,7 @@ export default class NostrRequest { for (const relay of this.relays) { const subs = this.relaySubs.get(relay); - subs.push(relay.onEOSE.subscribe(this.handleEOSE.bind(this))); + subs.push(relay.onEOSE.subscribe((m) => this.handleEOSE(m, relay))); subs.push(relay.onEvent.subscribe(this.handleEvent.bind(this))); subs.push(relay.onCount.subscribe(this.handleCount.bind(this))); } @@ -38,9 +38,8 @@ export default class NostrRequest { this.timeout = timeout ?? REQUEST_DEFAULT_TIMEOUT; } - handleEOSE(eose: IncomingEOSE) { - if (eose.subId === this.id) { - const relay = eose.relay; + handleEOSE(message: IncomingEOSE, relay: Relay) { + if (message[1] === this.id) { this.relays.delete(relay); relay.send(["CLOSE", this.id]); @@ -53,19 +52,15 @@ export default class NostrRequest { } } } - handleEvent(incomingEvent: IncomingEvent) { - if ( - this.state === NostrRequest.RUNNING && - incomingEvent.subId === this.id && - !this.seenEvents.has(incomingEvent.body.id) - ) { - this.onEvent.next(incomingEvent.body); - this.seenEvents.add(incomingEvent.body.id); + handleEvent(message: IncomingEvent) { + if (this.state === NostrRequest.RUNNING && message[1] === this.id && !this.seenEvents.has(message[2].id)) { + this.onEvent.next(message[2]); + this.seenEvents.add(message[2].id); } } handleCount(incomingCount: IncomingCount) { - if (incomingCount.subId === this.id) { - this.onCount.next({ count: incomingCount.count, approximate: incomingCount.approximate }); + if (incomingCount[1] === this.id) { + this.onCount.next(incomingCount[2]); } } diff --git a/src/classes/nostr-subscription.ts b/src/classes/nostr-subscription.ts index d3211dcb7..237a3a90c 100644 --- a/src/classes/nostr-subscription.ts +++ b/src/classes/nostr-subscription.ts @@ -1,8 +1,7 @@ import { nanoid } from "nanoid"; +import { Filter, NostrEvent } from "nostr-tools"; -import { NostrEvent } from "../types/nostr-event"; -import { NostrOutgoingMessage, NostrRequestFilter } from "../types/nostr-query"; -import Relay, { IncomingEOSE } from "./relay"; +import Relay, { IncomingEOSE, OutgoingMessage } from "./relay"; import relayPoolService from "../services/relay-pool"; import ControlledObservable from "./controlled-observable"; @@ -13,61 +12,58 @@ export default class NostrSubscription { id: string; name?: string; - query?: NostrRequestFilter; + filters?: Filter[]; relay: Relay; state = NostrSubscription.INIT; + onEvent = new ControlledObservable(); onEOSE = new ControlledObservable(); private subs: ZenObservable.Subscription[] = []; - constructor(relayUrl: string | URL, query?: NostrRequestFilter, name?: string) { + constructor(relayUrl: string | URL, filters?: Filter[], name?: string) { this.id = nanoid(); - this.query = query; + this.filters = filters; this.name = name; this.relay = relayPoolService.requestRelay(relayUrl); this.subs.push( this.relay.onEvent.subscribe((message) => { - if (this.state === NostrSubscription.OPEN && message.subId === this.id) { - this.onEvent.next(message.body); + if (this.state === NostrSubscription.OPEN && message[1] === this.id) { + this.onEvent.next(message[2]); } }), ); this.subs.push( this.relay.onEOSE.subscribe((eose) => { - if (this.state === NostrSubscription.OPEN && eose.subId === this.id) this.onEOSE.next(eose); + if (this.state === NostrSubscription.OPEN && eose[1] === this.id) this.onEOSE.next(eose); }), ); } - send(message: NostrOutgoingMessage) { + send(message: OutgoingMessage) { this.relay.send(message); } + setFilters(filters: Filter[]) { + this.filters = filters; + if (this.state === NostrSubscription.OPEN) { + this.send(["REQ", this.id, ...this.filters]); + } + return this; + } open() { - if (!this.query) throw new Error("cant open without a query"); + if (!this.filters) throw new Error("cant open without a query"); if (this.state === NostrSubscription.OPEN) return this; this.state = NostrSubscription.OPEN; - if (Array.isArray(this.query)) { - this.send(["REQ", this.id, ...this.query]); - } else this.send(["REQ", this.id, this.query]); + this.send(["REQ", this.id, ...this.filters]); relayPoolService.addClaim(this.relay.url, this); return this; } - setQuery(query: NostrRequestFilter) { - this.query = query; - if (this.state === NostrSubscription.OPEN) { - if (Array.isArray(this.query)) { - this.send(["REQ", this.id, ...this.query]); - } else this.send(["REQ", this.id, this.query]); - } - return this; - } close() { if (this.state !== NostrSubscription.OPEN) return this; diff --git a/src/classes/relay-pool.ts b/src/classes/relay-pool.ts new file mode 100644 index 000000000..0673ea171 --- /dev/null +++ b/src/classes/relay-pool.ts @@ -0,0 +1,90 @@ +import { logger } from "../helpers/debug"; +import { validateRelayURL } from "../helpers/relay"; +import { offlineMode } from "../services/offline-mode"; +import Relay from "./relay"; +import Subject from "./subject"; + +export default class RelayPool { + relays = new Map(); + onRelayCreated = new Subject(); + + relayClaims = new Map>(); + + log = logger.extend("RelayPool"); + + getRelays() { + return Array.from(this.relays.values()); + } + getRelayClaims(url: string | URL) { + url = validateRelayURL(url); + const key = url.toString(); + if (!this.relayClaims.has(key)) { + this.relayClaims.set(key, new Set()); + } + return this.relayClaims.get(key) as Set; + } + + requestRelay(url: string | URL, connect = true) { + url = validateRelayURL(url); + const key = url.toString(); + if (!this.relays.has(key)) { + const newRelay = new Relay(key); + this.relays.set(key, newRelay); + this.onRelayCreated.next(newRelay); + } + + const relay = this.relays.get(key) as Relay; + if (connect && !relay.okay) { + try { + relay.open(); + } catch (e) { + this.log(`Failed to connect to ${relay.url}`); + this.log(e); + } + } + return relay; + } + + pruneRelays() { + for (const [url, relay] of this.relays.entries()) { + const claims = this.getRelayClaims(url).size; + if (claims === 0) { + relay.close(); + } + } + } + reconnectRelays() { + if (offlineMode.value) return; + + for (const [url, relay] of this.relays.entries()) { + const claims = this.getRelayClaims(url).size; + if (!relay.okay && claims > 0) { + try { + relay.open(); + } catch (e) { + this.log(`Failed to connect to ${relay.url}`); + this.log(e); + } + } + } + } + + addClaim(url: string | URL, id: any) { + url = validateRelayURL(url); + const key = url.toString(); + this.getRelayClaims(key).add(id); + } + removeClaim(url: string | URL, id: any) { + url = validateRelayURL(url); + const key = url.toString(); + this.getRelayClaims(key).delete(id); + } + + get connectedCount() { + let count = 0; + for (const [url, relay] of this.relays.entries()) { + if (relay.connected) count++; + } + return count; + } +} diff --git a/src/classes/relay.ts b/src/classes/relay.ts index a2e39718d..b2ce6d015 100644 --- a/src/classes/relay.ts +++ b/src/classes/relay.ts @@ -1,40 +1,28 @@ +import { Filter, NostrEvent } from "nostr-tools"; import { offlineMode } from "../services/offline-mode"; import relayScoreboardService from "../services/relay-scoreboard"; -import { RawIncomingNostrEvent, NostrEvent, CountResponse } from "../types/nostr-event"; -import { NostrOutgoingMessage } from "../types/nostr-query"; import ControlledObservable from "./controlled-observable"; import createDefer, { Deferred } from "./deferred"; import { PersistentSubject } from "./subject"; -export type IncomingEvent = { - type: "EVENT"; - subId: string; - body: NostrEvent; - relay: Relay; -}; -export type IncomingNotice = { - type: "NOTICE"; - message: string; - relay: Relay; -}; -export type IncomingCount = { - type: "COUNT"; - subId: string; - relay: Relay; -} & CountResponse; -export type IncomingEOSE = { - type: "EOSE"; - subId: string; - relay: Relay; -}; -export type IncomingCommandResult = { - type: "OK"; - eventId: string; - status: boolean; - message?: string; - relay: Relay; +export type CountResponse = { + count: number; + approximate?: boolean; }; +export type IncomingEvent = ["EVENT", string, NostrEvent]; +export type IncomingNotice = ["NOTICE", string]; +export type IncomingCount = ["COUNT", string, CountResponse]; +export type IncomingEOSE = ["EOSE", string]; +export type IncomingCommandResult = ["OK", string, boolean] | ["OK", string, boolean, string]; +export type IncomingMessage = IncomingEvent | IncomingNotice | IncomingCount | IncomingEOSE | IncomingCommandResult; + +export type OutgoingEvent = ["EVENT", NostrEvent]; +export type OutgoingRequest = ["REQ", string, ...Filter[]]; +export type OutgoingCount = ["COUNT", string, ...Filter[]]; +export type OutgoingClose = ["CLOSE", string]; +export type OutgoingMessage = OutgoingEvent | OutgoingRequest | OutgoingClose | OutgoingCount; + export enum RelayMode { NONE = 0, READ = 1, @@ -46,15 +34,16 @@ const CONNECTION_TIMEOUT = 1000 * 30; export default class Relay { url: string; + ws?: WebSocket; status = new PersistentSubject(WebSocket.CLOSED); onOpen = new ControlledObservable(); onClose = new ControlledObservable(); + onEvent = new ControlledObservable(); onNotice = new ControlledObservable(); onCount = new ControlledObservable(); onEOSE = new ControlledObservable(); onCommandResult = new ControlledObservable(); - ws?: WebSocket; private connectionPromises: Deferred[] = []; @@ -62,7 +51,7 @@ export default class Relay { private ejectTimer?: () => void; private intentionalClose = false; private subscriptionResTimer = new Map void>(); - private queue: NostrOutgoingMessage[] = []; + private queue: OutgoingMessage[] = []; constructor(url: string) { this.url = url; @@ -123,7 +112,7 @@ export default class Relay { }; this.ws.onmessage = this.handleMessage.bind(this); } - send(json: NostrOutgoingMessage) { + send(json: OutgoingMessage) { if (this.connected) { this.ws?.send(JSON.stringify(json)); @@ -185,35 +174,38 @@ export default class Relay { return this.ws?.readyState; } - handleMessage(event: MessageEvent) { - if (!event.data) return; + handleMessage(message: MessageEvent) { + if (!message.data) return; try { - const data: RawIncomingNostrEvent = JSON.parse(event.data); + const data: IncomingMessage = JSON.parse(message.data); const type = data[0]; + // all messages must have an argument + if (!data[1]) return; + switch (type) { case "EVENT": - this.onEvent.next({ relay: this, type, subId: data[1], body: data[2] }); + this.onEvent.next(data); this.endSubResTimer(data[1]); break; case "NOTICE": - this.onNotice.next({ relay: this, type, message: data[1] }); + this.onNotice.next(data); break; case "COUNT": - this.onCount.next({ relay: this, type, subId: data[1], ...data[2] }); + this.onCount.next(data); break; case "EOSE": - this.onEOSE.next({ relay: this, type, subId: data[1] }); + this.onEOSE.next(data); this.endSubResTimer(data[1]); break; case "OK": - this.onCommandResult.next({ relay: this, type, eventId: data[1], status: data[2], message: data[3] }); + this.onCommandResult.next(data); break; } } catch (e) { - console.log(`Relay: Failed to parse event from ${this.url}`); - console.log(event.data, e); + console.log(`Relay: Failed to parse massage from ${this.url}`); + console.log(message.data, e); } } } diff --git a/src/classes/timeline-loader.ts b/src/classes/timeline-loader.ts index a715aae53..464e70f58 100644 --- a/src/classes/timeline-loader.ts +++ b/src/classes/timeline-loader.ts @@ -4,14 +4,14 @@ import { Filter, matchFilters } from "nostr-tools"; import _throttle from "lodash.throttle"; import { NostrEvent, isATag, isETag } from "../types/nostr-event"; -import { NostrRequestFilter, RelayQueryMap } from "../types/nostr-query"; +import { NostrRequestFilter, RelayQueryMap } from "../types/nostr-relay"; import NostrRequest from "./nostr-request"; import NostrMultiSubscription from "./nostr-multi-subscription"; import Subject, { PersistentSubject } from "./subject"; import { logger } from "../helpers/debug"; import EventStore from "./event-store"; import { isReplaceable } from "../helpers/nostr/events"; -import replaceableEventLoaderService from "../services/replaceable-event-requester"; +import replaceableEventsService from "../services/replaceable-events"; import deleteEventService from "../services/delete-events"; import { addQueryToFilter, @@ -127,7 +127,7 @@ export default class TimelineLoader { this.name = name; this.log = logger.extend("TimelineLoader:" + name); this.events = new EventStore(name); - this.events.connect(replaceableEventLoaderService.events, false); + this.events.connect(replaceableEventsService.events, false); this.subscription = new NostrMultiSubscription(name); this.subscription.onEvent.subscribe(this.handleEvent.bind(this)); @@ -147,7 +147,7 @@ export default class TimelineLoader { } private handleEvent(event: NostrEvent, cache = true) { // if this is a replaceable event, mirror it over to the replaceable event service - if (isReplaceable(event.kind)) replaceableEventLoaderService.handleEvent(event); + if (isReplaceable(event.kind)) replaceableEventsService.handleEvent(event); this.events.addEvent(event); if (cache) localRelay.publish(event); diff --git a/src/components/debug-modal/user-debug-modal.tsx b/src/components/debug-modal/user-debug-modal.tsx index e6ec9dbc2..d45827a56 100644 --- a/src/components/debug-modal/user-debug-modal.tsx +++ b/src/components/debug-modal/user-debug-modal.tsx @@ -7,13 +7,13 @@ import RawValue from "./raw-value"; import RawJson from "./raw-json"; import { useSharableProfileId } from "../../hooks/use-shareable-profile-id"; import useUserLNURLMetadata from "../../hooks/use-user-lnurl-metadata"; -import replaceableEventLoaderService from "../../services/replaceable-event-requester"; +import replaceableEventsService from "../../services/replaceable-events"; export default function UserDebugModal({ pubkey, ...props }: { pubkey: string } & Omit) { const npub = nip19.npubEncode(pubkey); const metadata = useUserMetadata(pubkey); const nprofile = useSharableProfileId(pubkey); - const relays = replaceableEventLoaderService.getEvent(kinds.RelayList, pubkey).value; + const relays = replaceableEventsService.getEvent(kinds.RelayList, pubkey).value; const tipMetadata = useUserLNURLMetadata(pubkey); return ( diff --git a/src/components/embed-event/event-types/embedded-list.tsx b/src/components/embed-event/event-types/embedded-list.tsx index 981a87f3e..712c9af3d 100644 --- a/src/components/embed-event/event-types/embedded-list.tsx +++ b/src/components/embed-event/event-types/embedded-list.tsx @@ -3,7 +3,7 @@ import { Link as RouterLink } from "react-router-dom"; import { NostrEvent } from "../../../types/nostr-event"; import { getListDescription, getListName, isSpecialListKind } from "../../../helpers/nostr/lists"; -import { createCoordinate } from "../../../services/replaceable-event-requester"; +import { createCoordinate } from "../../../services/replaceable-events"; import { getSharableEventAddress } from "../../../helpers/nip19"; import UserAvatarLink from "../../user-avatar-link"; import UserLink from "../../user-link"; diff --git a/src/components/loading-nostr-link.tsx b/src/components/loading-nostr-link.tsx index aed17f3e5..1c9639d0f 100644 --- a/src/components/loading-nostr-link.tsx +++ b/src/components/loading-nostr-link.tsx @@ -30,7 +30,7 @@ import { isValidRelayURL } from "../helpers/relay"; import relayScoreboardService from "../services/relay-scoreboard"; import { RelayFavicon } from "./relay-favicon"; import singleEventService from "../services/single-event"; -import replaceableEventLoaderService from "../services/replaceable-event-requester"; +import replaceableEventsService from "../services/replaceable-events"; function SearchOnRelaysModal({ isOpen, @@ -53,7 +53,7 @@ function SearchOnRelaysModal({ setLoading(true); switch (decode.type) { case "naddr": - replaceableEventLoaderService.requestEvent( + replaceableEventsService.requestEvent( Array.from(relays), decode.data.kind, decode.data.pubkey, diff --git a/src/components/note/components/repost-modal.tsx b/src/components/note/components/repost-modal.tsx index 63afa3ba6..0ca97d5ec 100644 --- a/src/components/note/components/repost-modal.tsx +++ b/src/components/note/components/repost-modal.tsx @@ -21,7 +21,7 @@ import { EmbedEvent } from "../../embed-event"; import { ChevronDownIcon, ChevronUpIcon, ExternalLinkIcon } from "../../icons"; import useUserCommunitiesList from "../../../hooks/use-user-communities-list"; import useCurrentAccount from "../../../hooks/use-current-account"; -import { createCoordinate } from "../../../services/replaceable-event-requester"; +import { createCoordinate } from "../../../services/replaceable-events"; import relayHintService from "../../../services/event-relay-hint"; import { usePublishEvent } from "../../../providers/global/publish-provider"; diff --git a/src/components/publish-details.tsx b/src/components/publish-details.tsx index f5b03c204..abd22220b 100644 --- a/src/components/publish-details.tsx +++ b/src/components/publish-details.tsx @@ -17,17 +17,17 @@ export function PublishDetails({ pub }: PostResultsProps & Omit - {results.map((result) => ( - + {results.map(({ result, relay }) => ( + - - {result.relay.url} + + {relay.url} - + - {result.message && {result.message}} + {result[3] && {result[3]}} ))} diff --git a/src/components/publish-log.tsx b/src/components/publish-log.tsx index 6033c46c8..bc1417780 100644 --- a/src/components/publish-log.tsx +++ b/src/components/publish-log.tsx @@ -25,8 +25,8 @@ import { PublishContext } from "../providers/global/publish-provider"; export function PublishActionStatusTag({ pub, ...props }: { pub: NostrPublishAction } & Omit) { const results = useSubject(pub.results); - const successful = results.filter((result) => result.status); - const failedWithMessage = results.filter((result) => !result.status && result.message); + const successful = results.filter(({ result }) => result[2]); + const failedWithMessage = results.filter(({ result }) => !result[2] && result[3]); let statusIcon = ; let statusColor: TagProps["colorScheme"] = "blue"; diff --git a/src/helpers/nostr/events.ts b/src/helpers/nostr/events.ts index 466a7fa2b..c9decd7be 100644 --- a/src/helpers/nostr/events.ts +++ b/src/helpers/nostr/events.ts @@ -1,14 +1,14 @@ import { EventTemplate, kinds, validateEvent } from "nostr-tools"; +import { getEventUID } from "nostr-idb"; +import dayjs from "dayjs"; +import { nanoid } from "nanoid"; import { ATag, DraftNostrEvent, ETag, isATag, isDTag, isETag, isPTag, NostrEvent, Tag } from "../../types/nostr-event"; import { getMatchNostrLink } from "../regexp"; import { AddressPointer, EventPointer } from "nostr-tools/lib/types/nip19"; import { safeJson } from "../parse"; import { safeDecode } from "../nip19"; -import { getEventUID } from "nostr-idb"; import { safeRelayUrl, safeRelayUrls } from "../relay"; -import dayjs from "dayjs"; -import { nanoid } from "nanoid"; import userMailboxesService from "../../services/user-mailboxes"; import RelaySet from "../../classes/relay-set"; diff --git a/src/helpers/nostr/filter.ts b/src/helpers/nostr/filter.ts index 97277b99a..87c33133e 100644 --- a/src/helpers/nostr/filter.ts +++ b/src/helpers/nostr/filter.ts @@ -1,5 +1,5 @@ import stringify from "json-stringify-deterministic"; -import { NostrRequestFilter, RelayQueryMap } from "../../types/nostr-query"; +import { NostrRequestFilter, RelayQueryMap } from "../../types/nostr-relay"; import { Filter } from "nostr-tools"; import { safeRelayUrls } from "../relay"; diff --git a/src/helpers/nostr/stream.ts b/src/helpers/nostr/stream.ts index 3e0fe91be..66ecc7965 100644 --- a/src/helpers/nostr/stream.ts +++ b/src/helpers/nostr/stream.ts @@ -2,7 +2,7 @@ import dayjs from "dayjs"; import { DraftNostrEvent, NostrEvent, isPTag } from "../../types/nostr-event"; import { unique } from "../array"; import { ensureNotifyContentMentions } from "./post"; -import { createCoordinate } from "../../services/replaceable-event-requester"; +import { createCoordinate } from "../../services/replaceable-events"; export const STREAM_KIND = 30311; export const STREAM_CHAT_MESSAGE_KIND = 1311; diff --git a/src/helpers/relay.ts b/src/helpers/relay.ts index 43d70af21..31a334d2e 100644 --- a/src/helpers/relay.ts +++ b/src/helpers/relay.ts @@ -1,7 +1,7 @@ import { SimpleRelay, SubscriptionOptions } from "nostr-idb"; import { Filter } from "nostr-tools"; -import { NostrQuery, NostrRequestFilter } from "../types/nostr-query"; +import { NostrQuery, NostrRequestFilter } from "../types/nostr-relay"; import { NostrEvent } from "../types/nostr-event"; // NOTE: only use this for equality checks and querying diff --git a/src/hooks/use-channel-metadata.ts b/src/hooks/use-channel-metadata.ts index 0c1285f37..9ac304c5e 100644 --- a/src/hooks/use-channel-metadata.ts +++ b/src/hooks/use-channel-metadata.ts @@ -1,6 +1,6 @@ import { useMemo } from "react"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import useSubject from "./use-subject"; import channelMetadataService from "../services/channel-metadata"; import { ChannelMetadata, safeParseChannelMetadata } from "../helpers/nostr/channel"; diff --git a/src/hooks/use-event-count.ts b/src/hooks/use-event-count.ts index 8916f9e3c..d7a7e0d01 100644 --- a/src/hooks/use-event-count.ts +++ b/src/hooks/use-event-count.ts @@ -1,6 +1,6 @@ import { useMemo } from "react"; import eventCountService from "../services/event-count"; -import { NostrRequestFilter } from "../types/nostr-query"; +import { NostrRequestFilter } from "../types/nostr-relay"; import useSubject from "./use-subject"; export default function useEventCount(filter?: NostrRequestFilter, alwaysRequest = false) { diff --git a/src/hooks/use-event-exists.ts b/src/hooks/use-event-exists.ts index 871d4a3e3..b1e5f0462 100644 --- a/src/hooks/use-event-exists.ts +++ b/src/hooks/use-event-exists.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import stringify from "json-stringify-deterministic"; import eventExistsService from "../services/event-exists"; -import { NostrRequestFilter } from "../types/nostr-query"; +import { NostrRequestFilter } from "../types/nostr-relay"; import useSubject from "./use-subject"; export default function useEventExists(filter?: NostrRequestFilter, relays: string[] = [], fallback = true) { diff --git a/src/hooks/use-favorite-emoji-packs.ts b/src/hooks/use-favorite-emoji-packs.ts index efdf8be7d..fde9a56ca 100644 --- a/src/hooks/use-favorite-emoji-packs.ts +++ b/src/hooks/use-favorite-emoji-packs.ts @@ -1,7 +1,7 @@ import useReplaceableEvent from "./use-replaceable-event"; import useCurrentAccount from "./use-current-account"; import { USER_EMOJI_LIST_KIND } from "../helpers/nostr/emoji-packs"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; export const FAVORITE_LISTS_IDENTIFIER = "nostrudel-favorite-lists"; diff --git a/src/hooks/use-replaceable-event.ts b/src/hooks/use-replaceable-event.ts index bdb8b8a47..67816fa91 100644 --- a/src/hooks/use-replaceable-event.ts +++ b/src/hooks/use-replaceable-event.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { useReadRelays } from "./use-client-relays"; -import replaceableEventLoaderService, { RequestOptions } from "../services/replaceable-event-requester"; +import replaceableEventsService, { RequestOptions } from "../services/replaceable-events"; import { CustomAddressPointer, parseCoordinate } from "../helpers/nostr/events"; import useSubject from "./use-subject"; @@ -14,7 +14,7 @@ export default function useReplaceableEvent( const sub = useMemo(() => { const parsed = typeof cord === "string" ? parseCoordinate(cord) : cord; if (!parsed) return; - return replaceableEventLoaderService.requestEvent( + return replaceableEventsService.requestEvent( parsed.relays ? [...readRelays, ...parsed.relays] : readRelays, parsed.kind, parsed.pubkey, diff --git a/src/hooks/use-replaceable-events.ts b/src/hooks/use-replaceable-events.ts index fac48a454..4c01b5d34 100644 --- a/src/hooks/use-replaceable-events.ts +++ b/src/hooks/use-replaceable-events.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { useReadRelays } from "./use-client-relays"; -import replaceableEventLoaderService, { RequestOptions } from "../services/replaceable-event-requester"; +import replaceableEventsService, { RequestOptions } from "../services/replaceable-events"; import { CustomAddressPointer, parseCoordinate } from "../helpers/nostr/events"; import Subject from "../classes/subject"; import { NostrEvent } from "../types/nostr-event"; @@ -20,7 +20,7 @@ export default function useReplaceableEvents( const parsed = typeof cord === "string" ? parseCoordinate(cord) : cord; if (!parsed) return; subs.push( - replaceableEventLoaderService.requestEvent( + replaceableEventsService.requestEvent( parsed.relays ? [...readRelays, ...parsed.relays] : readRelays, parsed.kind, parsed.pubkey, diff --git a/src/hooks/use-timeline-loader.ts b/src/hooks/use-timeline-loader.ts index 19bc5d9ba..8dcc593f6 100644 --- a/src/hooks/use-timeline-loader.ts +++ b/src/hooks/use-timeline-loader.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo } from "react"; import { useUnmount } from "react-use"; -import { NostrRequestFilter } from "../types/nostr-query"; +import { NostrRequestFilter } from "../types/nostr-relay"; import timelineCacheService from "../services/timeline-cache"; import { EventFilter } from "../classes/timeline-loader"; import { NostrEvent } from "../types/nostr-event"; diff --git a/src/hooks/use-user-bookmarks-list.ts b/src/hooks/use-user-bookmarks-list.ts index 2ce429890..0893aef8d 100644 --- a/src/hooks/use-user-bookmarks-list.ts +++ b/src/hooks/use-user-bookmarks-list.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { BOOKMARK_LIST_KIND, getAddressPointersFromList, getEventPointersFromList } from "../helpers/nostr/lists"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import useCurrentAccount from "./use-current-account"; import useReplaceableEvent from "./use-replaceable-event"; diff --git a/src/hooks/use-user-channels-list.ts b/src/hooks/use-user-channels-list.ts index 651cdcc15..0fc0ace92 100644 --- a/src/hooks/use-user-channels-list.ts +++ b/src/hooks/use-user-channels-list.ts @@ -1,5 +1,5 @@ import { CHANNELS_LIST_KIND, getEventPointersFromList } from "../helpers/nostr/lists"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import useCurrentAccount from "./use-current-account"; import useReplaceableEvent from "./use-replaceable-event"; diff --git a/src/hooks/use-user-communities-list.ts b/src/hooks/use-user-communities-list.ts index 3abe13e45..565b3336d 100644 --- a/src/hooks/use-user-communities-list.ts +++ b/src/hooks/use-user-communities-list.ts @@ -1,6 +1,6 @@ import { COMMUNITY_DEFINITION_KIND, SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER } from "../helpers/nostr/communities"; import { COMMUNITIES_LIST_KIND, NOTE_LIST_KIND, getAddressPointersFromList } from "../helpers/nostr/lists"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import useCurrentAccount from "./use-current-account"; import useReplaceableEvent from "./use-replaceable-event"; diff --git a/src/hooks/use-user-contact-list.ts b/src/hooks/use-user-contact-list.ts index 33d487cf5..1c9104985 100644 --- a/src/hooks/use-user-contact-list.ts +++ b/src/hooks/use-user-contact-list.ts @@ -1,6 +1,6 @@ import { kinds } from "nostr-tools"; import useReplaceableEvent from "./use-replaceable-event"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; export default function useUserContactList( pubkey?: string, diff --git a/src/hooks/use-user-contact-relays.ts b/src/hooks/use-user-contact-relays.ts index a3d731139..4d6a953b2 100644 --- a/src/hooks/use-user-contact-relays.ts +++ b/src/hooks/use-user-contact-relays.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import RelaySet from "../classes/relay-set"; import useUserContactList from "./use-user-contact-list"; import { RelayMode } from "../classes/relay"; diff --git a/src/hooks/use-user-mailboxes.ts b/src/hooks/use-user-mailboxes.ts index 02bd13b22..a657bf603 100644 --- a/src/hooks/use-user-mailboxes.ts +++ b/src/hooks/use-user-mailboxes.ts @@ -1,5 +1,5 @@ import RelaySet from "../classes/relay-set"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import userMailboxesService from "../services/user-mailboxes"; import { useReadRelays } from "./use-client-relays"; import useSubject from "./use-subject"; diff --git a/src/hooks/use-user-metadata.ts b/src/hooks/use-user-metadata.ts index 9d7ac7a22..d5bd2a730 100644 --- a/src/hooks/use-user-metadata.ts +++ b/src/hooks/use-user-metadata.ts @@ -2,7 +2,7 @@ import { useMemo } from "react"; import userMetadataService from "../services/user-metadata"; import { useReadRelays } from "./use-client-relays"; import useSubject from "./use-subject"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import { COMMON_CONTACT_RELAY } from "../const"; export function useUserMetadata(pubkey?: string, additionalRelays: Iterable = [], opts: RequestOptions = {}) { diff --git a/src/hooks/use-user-mute-filter.ts b/src/hooks/use-user-mute-filter.ts index 96b07dc14..ec9287ba8 100644 --- a/src/hooks/use-user-mute-filter.ts +++ b/src/hooks/use-user-mute-filter.ts @@ -5,7 +5,7 @@ import useUserMuteList from "./use-user-mute-list"; import { getPubkeysFromList } from "../helpers/nostr/lists"; import { NostrEvent } from "../types/nostr-event"; import { STREAM_KIND, getStreamHost } from "../helpers/nostr/stream"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; export default function useUserMuteFilter(pubkey?: string, additionalRelays?: string[], opts?: RequestOptions) { const account = useCurrentAccount(); diff --git a/src/hooks/use-user-mute-list.ts b/src/hooks/use-user-mute-list.ts index 590bd81c5..aaa72f079 100644 --- a/src/hooks/use-user-mute-list.ts +++ b/src/hooks/use-user-mute-list.ts @@ -1,6 +1,6 @@ import useReplaceableEvent from "./use-replaceable-event"; import { MUTE_LIST_KIND } from "../helpers/nostr/lists"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; export default function useUserMuteList( pubkey?: string, diff --git a/src/hooks/use-user-mute-lists.ts b/src/hooks/use-user-mute-lists.ts index a135eab69..49b30b239 100644 --- a/src/hooks/use-user-mute-lists.ts +++ b/src/hooks/use-user-mute-lists.ts @@ -3,7 +3,7 @@ import { useMemo } from "react"; import useReplaceableEvent from "./use-replaceable-event"; import { PEOPLE_LIST_KIND, getPubkeysFromList } from "../helpers/nostr/lists"; import useUserMuteList from "./use-user-mute-list"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; export default function useUserMuteLists( pubkey?: string, diff --git a/src/hooks/use-user-network.ts b/src/hooks/use-user-network.ts index c7b59ca9d..5e32704ea 100644 --- a/src/hooks/use-user-network.ts +++ b/src/hooks/use-user-network.ts @@ -3,7 +3,7 @@ import { kinds } from "nostr-tools"; import { getPubkeysFromList } from "../helpers/nostr/lists"; import useUserContactList from "./use-user-contact-list"; -import replaceableEventLoaderService from "../services/replaceable-event-requester"; +import replaceableEventsService from "../services/replaceable-events"; import { useReadRelays } from "./use-client-relays"; import useSubjects from "./use-subjects"; import userMetadataService from "../services/user-metadata"; @@ -34,7 +34,7 @@ export default function useUserNetwork(pubkey: string, additionalRelays?: Iterab const subjects = useMemo(() => { return contactsPubkeys.map((person) => - replaceableEventLoaderService.requestEvent(readRelays, kinds.Contacts, person.pubkey), + replaceableEventsService.requestEvent(readRelays, kinds.Contacts, person.pubkey), ); }, [contactsPubkeys, readRelays.urls.join("|")]); diff --git a/src/hooks/use-user-pin-list.ts b/src/hooks/use-user-pin-list.ts index df08f0c14..683402ae4 100644 --- a/src/hooks/use-user-pin-list.ts +++ b/src/hooks/use-user-pin-list.ts @@ -1,5 +1,5 @@ import { PIN_LIST_KIND, getEventPointersFromList } from "../helpers/nostr/lists"; -import { RequestOptions } from "../services/replaceable-event-requester"; +import { RequestOptions } from "../services/replaceable-events"; import useCurrentAccount from "./use-current-account"; import useReplaceableEvent from "./use-replaceable-event"; diff --git a/src/providers/global/publish-provider.tsx b/src/providers/global/publish-provider.tsx index 4117e0986..8768877d6 100644 --- a/src/providers/global/publish-provider.tsx +++ b/src/providers/global/publish-provider.tsx @@ -8,7 +8,7 @@ import NostrPublishAction from "../../classes/nostr-publish-action"; import clientRelaysService from "../../services/client-relays"; import RelaySet from "../../classes/relay-set"; import { addPubkeyRelayHints, getAllRelayHints, isReplaceable } from "../../helpers/nostr/events"; -import replaceableEventLoaderService from "../../services/replaceable-event-requester"; +import replaceableEventsService from "../../services/replaceable-events"; import eventExistsService from "../../services/event-exists"; import eventReactionsService from "../../services/event-reactions"; import { localRelay } from "../../services/local-relay"; @@ -66,8 +66,8 @@ export default function PublishProvider({ children }: PropsWithChildren) { const pub = new NostrPublishAction(label, relays, signed); setLog((arr) => arr.concat(pub)); - pub.onResult.subscribe((result) => { - if (result.status) handleEventFromRelay(result.relay, signed); + pub.onResult.subscribe(({ relay, result }) => { + if (result[2]) handleEventFromRelay(relay, signed); }); // send it to the local relay @@ -75,7 +75,7 @@ export default function PublishProvider({ children }: PropsWithChildren) { // pass it to other services eventExistsService.handleEvent(signed); - if (isReplaceable(signed.kind)) replaceableEventLoaderService.handleEvent(signed); + if (isReplaceable(signed.kind)) replaceableEventsService.handleEvent(signed); if (signed.kind === kinds.Reaction) eventReactionsService.handleEvent(signed); if (signed.kind === kinds.EventDeletion) deleteEventService.handleEvent(signed); return pub; diff --git a/src/providers/local/people-list-provider.tsx b/src/providers/local/people-list-provider.tsx index a5e4e3356..0901a76af 100644 --- a/src/providers/local/people-list-provider.tsx +++ b/src/providers/local/people-list-provider.tsx @@ -5,7 +5,7 @@ import useCurrentAccount from "../../hooks/use-current-account"; import { getPubkeysFromList } from "../../helpers/nostr/lists"; import useReplaceableEvent from "../../hooks/use-replaceable-event"; import { NostrEvent } from "../../types/nostr-event"; -import { NostrQuery } from "../../types/nostr-query"; +import { NostrQuery } from "../../types/nostr-relay"; import useRouteSearchValue from "../../hooks/use-route-search-value"; export type ListId = "following" | "global" | string; diff --git a/src/services/channel-metadata.ts b/src/services/channel-metadata.ts index c8d10a1a3..4fd1f7d52 100644 --- a/src/services/channel-metadata.ts +++ b/src/services/channel-metadata.ts @@ -7,7 +7,7 @@ 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 { NostrQuery } from "../types/nostr-relay"; import { logger } from "../helpers/debug"; import db from "./db"; import createDefer, { Deferred } from "../classes/deferred"; @@ -109,7 +109,7 @@ class ChannelMetadataRelayLoader { }; if (query["#e"] && query["#e"].length > 0) this.log(`Updating query`, query["#e"].length); - this.subscription.setQuery(query); + this.subscription.setFilters([query]); if (this.subscription.state !== NostrSubscription.OPEN) { this.subscription.open(); diff --git a/src/services/event-count.ts b/src/services/event-count.ts index 87c8f5cda..39f73161c 100644 --- a/src/services/event-count.ts +++ b/src/services/event-count.ts @@ -2,7 +2,7 @@ import stringify from "json-stringify-deterministic"; import Subject from "../classes/subject"; import SuperMap from "../classes/super-map"; -import { NostrRequestFilter } from "../types/nostr-query"; +import { NostrRequestFilter } from "../types/nostr-relay"; import NostrRequest from "../classes/nostr-request"; import relayPoolService from "./relay-pool"; diff --git a/src/services/event-exists.ts b/src/services/event-exists.ts index 3d5393ff9..9032520c6 100644 --- a/src/services/event-exists.ts +++ b/src/services/event-exists.ts @@ -1,7 +1,7 @@ import stringify from "json-stringify-deterministic"; import Subject from "../classes/subject"; -import { NostrRequestFilter } from "../types/nostr-query"; +import { NostrRequestFilter } from "../types/nostr-relay"; import SuperMap from "../classes/super-map"; import NostrRequest from "../classes/nostr-request"; import relayScoreboardService from "./relay-scoreboard"; diff --git a/src/services/event-relay-hint.ts b/src/services/event-relay-hint.ts index dde8407b2..2f5aa8e72 100644 --- a/src/services/event-relay-hint.ts +++ b/src/services/event-relay-hint.ts @@ -2,7 +2,7 @@ import { NostrEvent } from "../types/nostr-event"; import { getEventRelays } from "./event-relays"; import relayScoreboardService from "./relay-scoreboard"; import type { AddressPointer, EventPointer } from "nostr-tools/lib/types/nip19"; -import { createCoordinate } from "./replaceable-event-requester"; +import { createCoordinate } from "./replaceable-events"; function pickBestRelays(relays: string[]) { // ignore local relays diff --git a/src/services/event-relays.ts b/src/services/event-relays.ts index 8fbb56910..6c9ad0f1e 100644 --- a/src/services/event-relays.ts +++ b/src/services/event-relays.ts @@ -31,8 +31,8 @@ export function handleEventFromRelay(relay: Relay, event: NostrEvent) { } relayPoolService.onRelayCreated.subscribe((relay) => { - relay.onEvent.subscribe(({ body: event }) => { - handleEventFromRelay(relay, event); + relay.onEvent.subscribe((message) => { + handleEventFromRelay(relay, message[2]); }); }); diff --git a/src/services/relay-pool.ts b/src/services/relay-pool.ts index 78b4005a4..07ad1beeb 100644 --- a/src/services/relay-pool.ts +++ b/src/services/relay-pool.ts @@ -1,95 +1,7 @@ -import Relay from "../classes/relay"; -import Subject from "../classes/subject"; -import { logger } from "../helpers/debug"; -import { safeRelayUrl, validateRelayURL } from "../helpers/relay"; +import RelayPool from "../classes/relay-pool"; import { offlineMode } from "./offline-mode"; -export class RelayPoolService { - relays = new Map(); - relayClaims = new Map>(); - onRelayCreated = new Subject(); - - log = logger.extend("RelayPool"); - - getRelays() { - return Array.from(this.relays.values()); - } - getRelayClaims(url: string | URL) { - url = validateRelayURL(url); - const key = url.toString(); - if (!this.relayClaims.has(key)) { - this.relayClaims.set(key, new Set()); - } - return this.relayClaims.get(key) as Set; - } - - requestRelay(url: string | URL, connect = true) { - url = validateRelayURL(url); - const key = url.toString(); - if (!this.relays.has(key)) { - const newRelay = new Relay(key); - this.relays.set(key, newRelay); - this.onRelayCreated.next(newRelay); - } - - const relay = this.relays.get(key) as Relay; - if (connect && !relay.okay) { - try { - relay.open(); - } catch (e) { - this.log(`Failed to connect to ${relay.url}`); - this.log(e); - } - } - return relay; - } - - pruneRelays() { - for (const [url, relay] of this.relays.entries()) { - const claims = this.getRelayClaims(url).size; - if (claims === 0) { - relay.close(); - } - } - } - reconnectRelays() { - if (offlineMode.value) return; - - for (const [url, relay] of this.relays.entries()) { - const claims = this.getRelayClaims(url).size; - if (!relay.okay && claims > 0) { - try { - relay.open(); - } catch (e) { - this.log(`Failed to connect to ${relay.url}`); - this.log(e); - } - } - } - } - - // id can be anything - addClaim(url: string | URL, id: any) { - url = validateRelayURL(url); - const key = url.toString(); - this.getRelayClaims(key).add(id); - } - removeClaim(url: string | URL, id: any) { - url = validateRelayURL(url); - const key = url.toString(); - this.getRelayClaims(key).delete(id); - } - - get connectedCount() { - let count = 0; - for (const [url, relay] of this.relays.entries()) { - if (relay.connected) count++; - } - return count; - } -} - -const relayPoolService = new RelayPoolService(); +const relayPoolService = new RelayPool(); setInterval(() => { if (document.visibilityState === "visible") { diff --git a/src/services/replaceable-event-requester.ts b/src/services/replaceable-events.ts similarity index 54% rename from src/services/replaceable-event-requester.ts rename to src/services/replaceable-events.ts index f2f5a2ccd..f724ba697 100644 --- a/src/services/replaceable-event-requester.ts +++ b/src/services/replaceable-events.ts @@ -1,12 +1,8 @@ -import dayjs from "dayjs"; -import debug, { Debugger } from "debug"; import _throttle from "lodash/throttle"; import { Filter } from "nostr-tools"; -import NostrSubscription from "../classes/nostr-subscription"; import SuperMap from "../classes/super-map"; import { NostrEvent } from "../types/nostr-event"; -import { NostrQuery } from "../types/nostr-query"; import { logger } from "../helpers/debug"; import { nameOrPubkey } from "./user-metadata"; import { getEventCoordinate } from "../helpers/nostr/events"; @@ -15,9 +11,7 @@ import { localRelay } from "./local-relay"; import { relayRequest } from "../helpers/relay"; import EventStore from "../classes/event-store"; import Subject from "../classes/subject"; - -type Pubkey = string; -type Relay = string; +import BatchKindLoader, { createCoordinate } from "../classes/batch-kind-loader"; export type RequestOptions = { /** Always request the event from the relays */ @@ -31,123 +25,14 @@ export type RequestOptions = { export function getHumanReadableCoordinate(kind: number, pubkey: string, d?: string) { return `${kind}:${nameOrPubkey(pubkey)}${d ? ":" + d : ""}`; } -export function createCoordinate(kind: number, pubkey: string, d?: string) { - return `${kind}:${pubkey}${d ? ":" + d : ""}`; -} - -const RELAY_REQUEST_BATCH_TIME = 500; - -/** This class is ued to batch requests to a single relay */ -class ReplaceableEventRelayLoader { - private subscription: NostrSubscription; - events = new EventStore(); - - 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 cord = getEventCoordinate(event); - - // remove the cord from the waiting list - this.requested.delete(cord); - - const current = this.events.getEvent(cord); - if (!current || event.created_at > current.created_at) { - this.events.addEvent(event); - } - } - private handleEOSE() { - // relays says it has nothing left - this.requested.clear(); - } - - requestEvent(kind: number, pubkey: string, d?: string) { - const cord = createCoordinate(kind, pubkey, d); - const event = this.events.getEvent(cord); - if (!event) { - this.requestNext.add(cord); - this.updateThrottle(); - } - return event; - } - - updateThrottle = _throttle(this.update, RELAY_REQUEST_BATCH_TIME); - update() { - let needsUpdate = false; - for (const cord of this.requestNext) { - if (!this.requested.has(cord)) { - this.requested.set(cord, new Date()); - needsUpdate = true; - } - } - this.requestNext.clear(); - - // prune requests - const timeout = dayjs().subtract(1, "minute"); - for (const [cord, date] of this.requested) { - if (dayjs(date).isBefore(timeout)) { - this.requested.delete(cord); - needsUpdate = true; - } - } - - // update the subscription - if (needsUpdate) { - if (this.requested.size > 0) { - const filters: Record = {}; - - for (const [cord] of this.requested) { - const [kindStr, pubkey, d] = cord.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(); - } - } - } -} const READ_CACHE_BATCH_TIME = 250; const WRITE_CACHE_BATCH_TIME = 250; -class ReplaceableEventLoaderService { - private subjects = new SuperMap>(() => new Subject()); - private loaders = new SuperMap((relay) => { - const loader = new ReplaceableEventRelayLoader(relay, this.log.extend(relay)); +class ReplaceableEventsService { + private subjects = new SuperMap>(() => new Subject()); + private loaders = new SuperMap((relay) => { + const loader = new BatchKindLoader(relay, this.log.extend(relay)); loader.events.onEvent.subscribe((e) => this.handleEvent(e)); return loader; }); @@ -251,11 +136,11 @@ class ReplaceableEventLoaderService { } requestEvent(relays: Iterable, kind: number, pubkey: string, d?: string, opts: RequestOptions = {}) { - const cord = createCoordinate(kind, pubkey, d); - const sub = this.subjects.get(cord); + const key = createCoordinate(kind, pubkey, d); + const sub = this.subjects.get(key); if (!sub.value) { - this.loadFromCache(cord).then((loaded) => { + this.loadFromCache(key).then((loaded) => { if (!loaded && !sub.value) this.requestEventFromRelays(relays, kind, pubkey, d); }); } @@ -268,11 +153,11 @@ class ReplaceableEventLoaderService { } } -const replaceableEventLoaderService = new ReplaceableEventLoaderService(); +const replaceableEventsService = new ReplaceableEventsService(); if (import.meta.env.DEV) { //@ts-ignore - window.replaceableEventLoaderService = replaceableEventLoaderService; + window.replaceableEventsService = replaceableEventsService; } -export default replaceableEventLoaderService; +export default replaceableEventsService; diff --git a/src/services/settings/user-app-settings.ts b/src/services/settings/user-app-settings.ts index 321b86122..0a594b9af 100644 --- a/src/services/settings/user-app-settings.ts +++ b/src/services/settings/user-app-settings.ts @@ -5,7 +5,7 @@ import { DraftNostrEvent } from "../../types/nostr-event"; import SuperMap from "../../classes/super-map"; import { PersistentSubject } from "../../classes/subject"; import { AppSettings, defaultSettings, parseAppSettings } from "./migrations"; -import replaceableEventLoaderService, { RequestOptions } from "../replaceable-event-requester"; +import replaceableEventsService, { RequestOptions } from "../replaceable-events"; export const APP_SETTINGS_KIND = 30078; export const SETTING_EVENT_IDENTIFIER = "nostrudel-settings"; @@ -19,7 +19,7 @@ class UserAppSettings { } requestAppSettings(pubkey: string, relays: Iterable, opts?: RequestOptions) { const sub = this.parsedSubjects.get(pubkey); - const requestSub = replaceableEventLoaderService.requestEvent( + const requestSub = replaceableEventsService.requestEvent( relays, APP_SETTINGS_KIND, pubkey, diff --git a/src/services/user-event-sync.ts b/src/services/user-event-sync.ts index 3debdfa36..0ba674edc 100644 --- a/src/services/user-event-sync.ts +++ b/src/services/user-event-sync.ts @@ -4,7 +4,7 @@ import { logger } from "../helpers/debug"; import accountService from "./account"; import clientRelaysService from "./client-relays"; import { offlineMode } from "./offline-mode"; -import replaceableEventLoaderService from "./replaceable-event-requester"; +import replaceableEventsService from "./replaceable-events"; import userAppSettings from "./settings/user-app-settings"; import userMailboxesService from "./user-mailboxes"; import userMetadataService from "./user-metadata"; @@ -15,7 +15,7 @@ function loadContactsList() { const account = accountService.current.value!; log("Loading contacts list"); - replaceableEventLoaderService.requestEvent( + replaceableEventsService.requestEvent( [...clientRelaysService.readRelays.value, COMMON_CONTACT_RELAY], kinds.Contacts, account.pubkey, diff --git a/src/services/user-mailboxes.ts b/src/services/user-mailboxes.ts index a2fe92757..94c234c11 100644 --- a/src/services/user-mailboxes.ts +++ b/src/services/user-mailboxes.ts @@ -3,7 +3,7 @@ import { kinds } from "nostr-tools"; import { NostrEvent } from "../types/nostr-event"; import SuperMap from "../classes/super-map"; import Subject from "../classes/subject"; -import replaceableEventLoaderService, { createCoordinate, RequestOptions } from "./replaceable-event-requester"; +import replaceableEventsService, { createCoordinate, RequestOptions } from "./replaceable-events"; import RelaySet from "../classes/relay-set"; import { RelayMode } from "../classes/relay"; import { relaysFromContactsEvent } from "../helpers/nostr/contacts"; @@ -30,17 +30,17 @@ function nip65ToUserMailboxes(event: NostrEvent): UserMailboxes { class UserMailboxesService { private subjects = new SuperMap>((pubkey) => - replaceableEventLoaderService.getEvent(kinds.RelayList, pubkey).map(nip65ToUserMailboxes), + replaceableEventsService.getEvent(kinds.RelayList, pubkey).map(nip65ToUserMailboxes), ); getMailboxes(pubkey: string) { return this.subjects.get(pubkey); } requestMailboxes(pubkey: string, relays: Iterable, opts: RequestOptions = {}) { const sub = this.subjects.get(pubkey); - replaceableEventLoaderService.requestEvent(relays, kinds.RelayList, pubkey, undefined, opts); + replaceableEventsService.requestEvent(relays, kinds.RelayList, pubkey, undefined, opts); // also fetch the relays from the users contacts - const contactsSub = replaceableEventLoaderService.requestEvent(relays, kinds.Contacts, pubkey, undefined, opts); + const contactsSub = replaceableEventsService.requestEvent(relays, kinds.Contacts, pubkey, undefined, opts); sub.connectWithMapper(contactsSub, (event, next, value) => { // NOTE: only use relays from contact list if the user dose not have a NIP-65 relay list const relays = relaysFromContactsEvent(event); @@ -61,12 +61,12 @@ class UserMailboxesService { async loadFromCache(pubkey: string) { const sub = this.subjects.get(pubkey); - await replaceableEventLoaderService.loadFromCache(createCoordinate(kinds.RelayList, pubkey)); + await replaceableEventsService.loadFromCache(createCoordinate(kinds.RelayList, pubkey)); return sub; } receiveEvent(event: NostrEvent) { - replaceableEventLoaderService.handleEvent(event); + replaceableEventsService.handleEvent(event); } } diff --git a/src/services/user-metadata.ts b/src/services/user-metadata.ts index 8de1379e7..88a168345 100644 --- a/src/services/user-metadata.ts +++ b/src/services/user-metadata.ts @@ -4,18 +4,18 @@ import _throttle from "lodash.throttle"; import { Kind0ParsedContent, parseKind0Event } from "../helpers/user-metadata"; import SuperMap from "../classes/super-map"; import Subject from "../classes/subject"; -import replaceableEventLoaderService, { RequestOptions } from "./replaceable-event-requester"; +import replaceableEventsService, { RequestOptions } from "./replaceable-events"; class UserMetadataService { private metadata = new SuperMap>((pubkey) => { - return replaceableEventLoaderService.getEvent(0, pubkey).map(parseKind0Event); + return replaceableEventsService.getEvent(0, pubkey).map(parseKind0Event); }); getSubject(pubkey: string) { return this.metadata.get(pubkey); } requestMetadata(pubkey: string, relays: Iterable, opts: RequestOptions = {}) { const subject = this.metadata.get(pubkey); - replaceableEventLoaderService.requestEvent(relays, kinds.Metadata, pubkey, undefined, opts); + replaceableEventsService.requestEvent(relays, kinds.Metadata, pubkey, undefined, opts); return subject; } } diff --git a/src/services/username-search.ts b/src/services/username-search.ts index b09019234..d96e8ee96 100644 --- a/src/services/username-search.ts +++ b/src/services/username-search.ts @@ -1,7 +1,7 @@ import _throttle from "lodash.throttle"; import { getSearchNames } from "../helpers/user-metadata"; import db from "./db"; -import replaceableEventLoaderService from "./replaceable-event-requester"; +import replaceableEventsService from "./replaceable-events"; import userMetadataService from "./user-metadata"; const WRITE_USER_SEARCH_BATCH_TIME = 500; @@ -25,7 +25,7 @@ const writeSearchData = _throttle(async () => { await transaction.done; }, WRITE_USER_SEARCH_BATCH_TIME); -replaceableEventLoaderService.events.onEvent.subscribe((event) => { +replaceableEventsService.events.onEvent.subscribe((event) => { if (event.kind === 0) { writeSearchQueue.add(event.pubkey); writeSearchData(); diff --git a/src/types/nostr-event.ts b/src/types/nostr-event.ts index 2d77d9563..307f0acb6 100644 --- a/src/types/nostr-event.ts +++ b/src/types/nostr-event.ts @@ -12,25 +12,9 @@ export type Tag = string[] | ETag | PTag | RTag | DTag | ATag | ExpirationTag; export type NostrEvent = Omit & { tags: Tag[]; }; -export type CountResponse = { - count: number; - approximate?: boolean; -}; export type DraftNostrEvent = Omit & { pubkey?: string; id?: string }; -export type RawIncomingEvent = ["EVENT", string, NostrEvent]; -export type RawIncomingNotice = ["NOTICE", string]; -export type RawIncomingCount = ["COUNT", string, CountResponse]; -export type RawIncomingEOSE = ["EOSE", string]; -export type RawIncomingCommandResult = ["OK", string, boolean, string]; -export type RawIncomingNostrEvent = - | RawIncomingEvent - | RawIncomingNotice - | RawIncomingCount - | RawIncomingEOSE - | RawIncomingCommandResult; - export function isETag(tag: Tag): tag is ETag { return tag[0] === "e" && tag[1] !== undefined; } diff --git a/src/types/nostr-query.ts b/src/types/nostr-query.ts deleted file mode 100644 index 11755efaf..000000000 --- a/src/types/nostr-query.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Filter } from "nostr-tools"; -import { NostrEvent } from "./nostr-event"; - -export type NostrOutgoingEvent = ["EVENT", NostrEvent]; -export type NostrOutgoingRequest = ["REQ", string, ...Filter[]]; -export type NostrOutgoingCount = ["COUNT", string, ...Filter[]]; -export type NostrOutgoingClose = ["CLOSE", string]; - -export type NostrOutgoingMessage = NostrOutgoingEvent | NostrOutgoingRequest | NostrOutgoingClose | NostrOutgoingCount; - -/** @deprecated use Filter instead */ -export type NostrQuery = Filter; - -export type NostrRequestFilter = Filter | Filter[]; - -export type RelayQueryMap = Record; diff --git a/src/types/nostr-relay.ts b/src/types/nostr-relay.ts new file mode 100644 index 000000000..f9b5a1e99 --- /dev/null +++ b/src/types/nostr-relay.ts @@ -0,0 +1,8 @@ +import { Filter } from "nostr-tools"; + +/** @deprecated use Filter instead */ +export type NostrQuery = Filter; + +export type NostrRequestFilter = Filter | Filter[]; + +export type RelayQueryMap = Record; diff --git a/src/views/communities/explore.tsx b/src/views/communities/explore.tsx index ca1c7e079..a9b832fab 100644 --- a/src/views/communities/explore.tsx +++ b/src/views/communities/explore.tsx @@ -9,7 +9,7 @@ import { COMMUNITY_DEFINITION_KIND } from "../../helpers/nostr/communities"; import { ErrorBoundary } from "../../components/error-boundary"; import { useReadRelays } from "../../hooks/use-client-relays"; import useSubjects from "../../hooks/use-subjects"; -import replaceableEventLoaderService from "../../services/replaceable-event-requester"; +import replaceableEventsService from "../../services/replaceable-events"; import { COMMUNITIES_LIST_KIND, getCoordinatesFromList } from "../../helpers/nostr/lists"; import { useNavigate } from "react-router-dom"; import { ChevronLeftIcon } from "../../components/icons"; @@ -20,9 +20,7 @@ import { AddressPointer } from "nostr-tools/lib/types/nip19"; export function useUsersJoinedCommunitiesLists(pubkeys: string[], additionalRelays?: Iterable) { const readRelays = useReadRelays(additionalRelays); const communityListsSubjects = useMemo(() => { - return pubkeys.map((pubkey) => - replaceableEventLoaderService.requestEvent(readRelays, COMMUNITIES_LIST_KIND, pubkey), - ); + return pubkeys.map((pubkey) => replaceableEventsService.requestEvent(readRelays, COMMUNITIES_LIST_KIND, pubkey)); }, [pubkeys]); return useSubjects(communityListsSubjects); } diff --git a/src/views/communities/index.tsx b/src/views/communities/index.tsx index 093473bf3..17b5ea3f7 100644 --- a/src/views/communities/index.tsx +++ b/src/views/communities/index.tsx @@ -35,7 +35,7 @@ import { getCommunityMods, getCommunityName, } from "../../helpers/nostr/communities"; -import { createCoordinate } from "../../services/replaceable-event-requester"; +import { createCoordinate } from "../../services/replaceable-events"; import { getImageSize } from "../../helpers/image"; import { useReadRelays } from "../../hooks/use-client-relays"; import useTimelineLoader from "../../hooks/use-timeline-loader"; diff --git a/src/views/lists/components/list-card.tsx b/src/views/lists/components/list-card.tsx index 58b153626..d58d14af3 100644 --- a/src/views/lists/components/list-card.tsx +++ b/src/views/lists/components/list-card.tsx @@ -29,7 +29,7 @@ import { import { getSharableEventAddress } from "../../../helpers/nip19"; import { NostrEvent } from "../../../types/nostr-event"; import useReplaceableEvent from "../../../hooks/use-replaceable-event"; -import { createCoordinate } from "../../../services/replaceable-event-requester"; +import { createCoordinate } from "../../../services/replaceable-events"; import { useRegisterIntersectionEntity } from "../../../providers/local/intersection-observer"; import ListFavoriteButton from "./list-favorite-button"; import { getEventUID } from "../../../helpers/nostr/events"; diff --git a/src/views/relays/relay/relay-details.tsx b/src/views/relays/relay/relay-details.tsx index 701b53bc1..012f92354 100644 --- a/src/views/relays/relay/relay-details.tsx +++ b/src/views/relays/relay/relay-details.tsx @@ -39,7 +39,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import EventStore from "../../../classes/event-store"; import NostrRequest from "../../../classes/nostr-request"; import { sortByDate } from "../../../helpers/nostr/events"; -import { NostrQuery } from "../../../types/nostr-query"; +import { NostrQuery } from "../../../types/nostr-relay"; ChartJS.register( ArcElement, diff --git a/src/views/streams/components/streamer-cards.tsx b/src/views/streams/components/streamer-cards.tsx index 89e960447..50deec90f 100644 --- a/src/views/streams/components/streamer-cards.tsx +++ b/src/views/streams/components/streamer-cards.tsx @@ -2,7 +2,7 @@ import { useMemo } from "react"; import { Card, CardBody, CardHeader, CardProps, Heading, Image, LinkBox, LinkOverlay } from "@chakra-ui/react"; import { useReadRelays } from "../../../hooks/use-client-relays"; -import replaceableEventLoaderService from "../../../services/replaceable-event-requester"; +import replaceableEventsService from "../../../services/replaceable-events"; import useSubject from "../../../hooks/use-subject"; import { NoteContents } from "../../../components/note/text-note-contents"; import { isATag } from "../../../types/nostr-event"; @@ -15,7 +15,7 @@ export const STREAMER_CARD_TYPE = 37777; function useStreamerCardsCords(pubkey: string, relays: Iterable) { const sub = useMemo( - () => replaceableEventLoaderService.requestEvent(relays, STREAMER_CARDS_TYPE, pubkey), + () => replaceableEventsService.requestEvent(relays, STREAMER_CARDS_TYPE, pubkey), [pubkey, relays], ); const streamerCards = useSubject(sub); diff --git a/src/views/streams/index.tsx b/src/views/streams/index.tsx index 12d1c8921..57627d7b5 100644 --- a/src/views/streams/index.tsx +++ b/src/views/streams/index.tsx @@ -12,7 +12,7 @@ import PeopleListSelection from "../../components/people-list-selection/people-l import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider"; import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status"; import useParsedStreams from "../../hooks/use-parsed-streams"; -import { NostrRequestFilter } from "../../types/nostr-query"; +import { NostrRequestFilter } from "../../types/nostr-relay"; import { useAppTitle } from "../../hooks/use-app-title"; import { NostrEvent } from "../../types/nostr-event"; import VerticalPageLayout from "../../components/vertical-page-layout"; diff --git a/src/views/streams/stream/index.tsx b/src/views/streams/stream/index.tsx index ba0e75a3f..c6a3f6f20 100644 --- a/src/views/streams/stream/index.tsx +++ b/src/views/streams/stream/index.tsx @@ -31,7 +31,7 @@ import StreamSummaryContent from "../components/stream-summary-content"; import { ChevronLeftIcon, ExternalLinkIcon } from "../../../components/icons"; import useSetColorMode from "../../../hooks/use-set-color-mode"; import { CopyIconButton } from "../../../components/copy-icon-button"; -import replaceableEventLoaderService from "../../../services/replaceable-event-requester"; +import replaceableEventsService from "../../../services/replaceable-events"; import useSubject from "../../../hooks/use-subject"; import StreamerCards from "../components/streamer-cards"; import { useAppTitle } from "../../../hooks/use-app-title"; @@ -251,7 +251,7 @@ export default function StreamView() { if (parsed.data.kind !== STREAM_KIND) throw new Error("Invalid stream kind"); const addrRelays = parsed.data.relays ?? []; - return replaceableEventLoaderService.requestEvent( + return replaceableEventsService.requestEvent( unique([...readRelays, ...streamRelays, ...addrRelays]), parsed.data.kind, parsed.data.pubkey, diff --git a/src/views/streams/stream/stream-chat/use-stream-chat-timeline.ts b/src/views/streams/stream/stream-chat/use-stream-chat-timeline.ts index 80e5ef974..bb50f9451 100644 --- a/src/views/streams/stream/stream-chat/use-stream-chat-timeline.ts +++ b/src/views/streams/stream/stream-chat/use-stream-chat-timeline.ts @@ -6,7 +6,7 @@ import { ParsedStream, STREAM_CHAT_MESSAGE_KIND, getATag } from "../../../../hel import useTimelineLoader from "../../../../hooks/use-timeline-loader"; import { NostrEvent } from "../../../../types/nostr-event"; import useStreamGoal from "../../../../hooks/use-stream-goal"; -import { NostrQuery } from "../../../../types/nostr-query"; +import { NostrQuery } from "../../../../types/nostr-relay"; import useUserMuteFilter from "../../../../hooks/use-user-mute-filter"; import useClientSideMuteFilter from "../../../../hooks/use-client-side-mute-filter"; import { useReadRelays } from "../../../../hooks/use-client-relays"; diff --git a/src/views/tools/network-mute-graph.tsx b/src/views/tools/network-mute-graph.tsx index d4d756c31..4ab0d7c8e 100644 --- a/src/views/tools/network-mute-graph.tsx +++ b/src/views/tools/network-mute-graph.tsx @@ -19,7 +19,7 @@ import { useUsersMetadata } from "../../hooks/use-user-network"; import { MUTE_LIST_KIND, getPubkeysFromList, isPubkeyInList } from "../../helpers/nostr/lists"; import useUserContactList from "../../hooks/use-user-contact-list"; import { useReadRelays } from "../../hooks/use-client-relays"; -import replaceableEventLoaderService from "../../services/replaceable-event-requester"; +import replaceableEventsService from "../../services/replaceable-events"; import useSubjects from "../../hooks/use-subjects"; import { useUserMetadata } from "../../hooks/use-user-metadata"; import { useNavigate } from "react-router-dom"; @@ -28,7 +28,7 @@ import { ChevronLeftIcon } from "../../components/icons"; export function useUsersMuteLists(pubkeys: string[], additionalRelays?: Iterable) { const readRelays = useReadRelays(additionalRelays); const muteListSubjects = useMemo(() => { - return pubkeys.map((pubkey) => replaceableEventLoaderService.requestEvent(readRelays, MUTE_LIST_KIND, pubkey)); + return pubkeys.map((pubkey) => replaceableEventsService.requestEvent(readRelays, MUTE_LIST_KIND, pubkey)); }, [pubkeys]); return useSubjects(muteListSubjects); }