UNPKG

@river-build/sdk

Version:

For more details, visit the following resources:

111 lines 4.54 kB
import EventEmitter from 'events'; import { StreamStateView } from './streamStateView'; import { isLocalEvent } from './types'; export class Stream extends EventEmitter { clientEmitter; logEmitFromStream; userId; _view; get view() { return this._view; } stopped = false; constructor(userId, streamId, clientEmitter, logEmitFromStream) { super(); this.clientEmitter = clientEmitter; this.logEmitFromStream = logEmitFromStream; this.userId = userId; this._view = new StreamStateView(userId, streamId); } get streamId() { return this._view.streamId; } get syncCookie() { return this.view.syncCookie; } /** * NOTE: Separating initial rollup from the constructor allows consumer to subscribe to events * on the new stream event and still access this object through Client.streams. */ initialize(nextSyncCookie, minipoolEvents, snapshot, miniblocks, prependedMiniblocks, prevSnapshotMiniblockNum, cleartexts) { // grab any local events from the previous view that haven't been processed const localEvents = this._view.timeline .filter(isLocalEvent) .filter((e) => e.hashStr.startsWith('~')); this._view = new StreamStateView(this.userId, this.streamId); this._view.initialize(nextSyncCookie, minipoolEvents, snapshot, miniblocks, prependedMiniblocks, prevSnapshotMiniblockNum, cleartexts, localEvents, this); } stop() { this.removeAllListeners(); this.stopped = true; } async appendEvents(events, nextSyncCookie, cleartexts) { this._view.appendEvents(events, nextSyncCookie, cleartexts, this); } prependEvents(miniblocks, cleartexts, terminus) { this._view.prependEvents(miniblocks, cleartexts, terminus, this, this); } appendLocalEvent(channelMessage, status) { return this._view.appendLocalEvent(channelMessage, status, this); } updateDecryptedContent(eventId, content) { return this._view.updateDecryptedContent(eventId, content, this); } updateDecryptedContentError(eventId, content) { return this._view.updateDecryptedContentError(eventId, content, this); } updateLocalEvent(localId, parsedEventHash, status) { return this._view.updateLocalEvent(localId, parsedEventHash, status, this); } emit(event, ...args) { if (this.stopped) { return false; } this.logEmitFromStream(event, ...args); this.clientEmitter.emit(event, ...args); return super.emit(event, ...args); } /** * Memberships are processed on block boundaries, so we need to wait for the next block to be processed * passing an undefined userId will wait for the membership to be updated for the current user */ async waitForMembership(membership, inUserId) { // check to see if we're already in that state const userId = inUserId ?? this.userId; // wait for a membership updated event, event, check again await this.waitFor('streamMembershipUpdated', () => this._view.getMembers().isMember(membership, userId)); } /** * Wait for a stream event to be emitted * optionally pass a condition function to check the event args */ async waitFor(event, condition, opts = { timeoutMs: 20000 }) { if (condition()) { return; } this.logEmitFromStream('waitFor', this.streamId, event); return new Promise((resolve, reject) => { // Set up the event listener const handler = () => { if (condition()) { this.logEmitFromStream('waitFor success', this.streamId, event); this.off(event, handler); this.off('streamInitialized', handler); clearTimeout(timeout); resolve(); } }; const timeoutError = new Error(`waitFor timeout waiting for ${event}`); // Set up the timeout const timeout = setTimeout(() => { this.logEmitFromStream('waitFor timeout', this.streamId, event); this.off(event, handler); this.off('streamInitialized', handler); reject(timeoutError); }, opts.timeoutMs); this.on(event, handler); this.on('streamInitialized', handler); }); } } //# sourceMappingURL=stream.js.map