From d3ec3ad3e204768c96058be550b88339b013424d Mon Sep 17 00:00:00 2001 From: buttercat1791 Date: Sat, 31 May 2025 20:50:38 -0500 Subject: [PATCH] Add observability to node resolution --- src/lib/data_structures/publication_tree.ts | 32 ++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/lib/data_structures/publication_tree.ts b/src/lib/data_structures/publication_tree.ts index d616740..3184b29 100644 --- a/src/lib/data_structures/publication_tree.ts +++ b/src/lib/data_structures/publication_tree.ts @@ -1,7 +1,7 @@ -import type NDK from "@nostr-dev-kit/ndk"; -import type { NDKEvent } from "@nostr-dev-kit/ndk"; -import { Lazy } from "./lazy.ts"; -import { findIndexAsync as _findIndexAsync } from '../utils.ts'; +import type NDK from '@nostr-dev-kit/ndk'; +import type { NDKEvent } from '@nostr-dev-kit/ndk'; +import { Lazy } from './lazy.ts'; +import { SvelteSet } from "svelte/reactivity"; enum PublicationTreeNodeType { Branch, @@ -22,6 +22,14 @@ interface PublicationTreeNode { } export class PublicationTree implements AsyncIterable { + // TODO: Abstract this into a `SveltePublicationTree` wrapper class. + /** + * A reactive set of addresses of the events that have been resolved (loaded) into the tree. + * Svelte components can use this set in reactive code blocks to trigger updates when new nodes + * are added to the tree. + */ + resolvedAddresses: SvelteSet = new SvelteSet(); + /** * The root node of the tree. */ @@ -52,6 +60,8 @@ export class PublicationTree implements AsyncIterable { */ #ndk: NDK; + #onNodeResolvedCallbacks: Array<(address: string) => void> = []; + constructor(rootEvent: NDKEvent, ndk: NDK) { const rootAddress = rootEvent.tagAddress(); this.#root = { @@ -185,6 +195,16 @@ export class PublicationTree implements AsyncIterable { this.#cursor.tryMoveTo(address); } + /** + * Registers an observer function that is invoked whenever a new node is resolved. Nodes are + * added lazily. + * + * @param observer The observer function. + */ + onNodeResolved(observer: (address: string) => void) { + this.#onNodeResolvedCallbacks.push(observer); + } + // #region Iteration Cursor #cursor = new class { @@ -504,6 +524,7 @@ export class PublicationTree implements AsyncIterable { } this.#events.set(address, event); + this.resolvedAddresses.add(address); const childAddresses = event.tags.filter(tag => tag[0] === 'a').map(tag => tag[1]); @@ -519,6 +540,9 @@ export class PublicationTree implements AsyncIterable { this.addEventByAddress(address, event); } + // TODO: We may need to move this to `#addNode`, so the observer is notified more eagerly. + this.#onNodeResolvedCallbacks.forEach(observer => observer(address)); + return node; }