diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index 4b892bb714..0626e1a7bf 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -1,5 +1,6 @@ import { Utils } from "../Utils" import { Readable, Subscriber, Unsubscriber, Updater, Writable } from "svelte/store" + /** * Various static utils */ @@ -104,6 +105,24 @@ export class Stores { }) return newStore } + + public static fromArray(sources: ReadonlyArray>): UIEventSource { + const src = new UIEventSource(sources.map(s => s.data)) + + for (let i = 0; i < sources.length; i++) { + sources[i].addCallback(content => { + src.data[i] = content + src.ping() + }) + } + src.addCallbackD(contents => { + for (let i = 0; i < contents.length; i++) { + sources[i].setData(contents[i]) + } + }) + + return src + } } export abstract class Store implements Readable { @@ -129,16 +148,16 @@ export abstract class Store implements Readable { } abstract map(f: (t: T) => J): Store - abstract map(f: (t: T) => J, extraStoresToWatch: Store[]): Store + abstract map(f: (t: T) => J, extraStoresToWatch: Store[]): Store abstract map( f: (t: T) => J, - extraStoresToWatch: Store[], + extraStoresToWatch: Store[], callbackDestroyFunction: (f: () => void) => void ): Store public mapD( f: (t: Exclude) => J, - extraStoresToWatch?: Store[], + extraStoresToWatch?: Store[], callbackDestroyFunction?: (f: () => void) => void ): Store { return this.map( @@ -241,7 +260,7 @@ export abstract class Store implements Readable { * src.setData(0) * lastValue // => "def" */ - public bind(f: (t: T) => Store, extraSources: Store[] = []): Store { + public bind(f: (t: T) => Store, extraSources: Store[] = []): Store { const mapped = this.map(f, extraSources) const sink = new UIEventSource(undefined) const seenEventSources = new Set>() @@ -275,7 +294,7 @@ export abstract class Store implements Readable { public bindD( f: (t: Exclude) => Store, - extraSources: Store[] = [] + extraSources: Store[] = [] ): Store { return this.bind((t) => { if (t === null) { @@ -295,10 +314,9 @@ export abstract class Store implements Readable { const newSource = new UIEventSource(this.data) - const self = this this.addCallback((latestData) => { window.setTimeout(() => { - if (self.data == latestData) { + if (this.data == latestData) { // compare by reference. // Note that 'latestData' and 'self.data' are both from the same UIEVentSource, but both are dereferenced at a different time newSource.setData(latestData) @@ -316,14 +334,13 @@ export abstract class Store implements Readable { * @constructor */ public AsPromise(condition?: (t: T) => boolean): Promise { - const self = this condition = condition ?? ((t) => t !== undefined) return new Promise((resolve) => { - const data = self.data + const data = this.data if (condition(data)) { resolve(data) } else { - self.addCallbackD((data) => { + this.addCallbackD((data) => { if (condition(data)) { resolve(data) return true // return true to unregister as we only need to be called once @@ -338,7 +355,7 @@ export abstract class Store implements Readable { /** * Same as 'addCallbackAndRun', added to be compatible with Svelte */ - public subscribe(run: Subscriber & ((value: T) => void), _?): Unsubscriber { + public subscribe(run: Subscriber & ((value: T) => void)): Unsubscriber { // We don't need to do anything with 'invalidate', see // https://github.com/sveltejs/svelte/issues/3859 @@ -451,7 +468,7 @@ class ListenerTracker { public ping(data: T): number { this.pingCount++ let toDelete = undefined - let startTime = new Date().getTime() / 1000 + const startTime = new Date().getTime() / 1000 for (const callback of this._callbacks) { try { if (callback(data) === true) { @@ -467,7 +484,7 @@ class ListenerTracker { console.error("Got an error while running a callback:", e) } } - let endTime = new Date().getTime() / 1000 + const endTime = new Date().getTime() / 1000 if (endTime - startTime > 500) { console.trace( "Warning: a ping took more then 500ms; this is probably a performance issue" @@ -495,13 +512,13 @@ class ListenerTracker { * The mapped store is a helper type which does the mapping of a function. */ class MappedStore extends Store { - private static readonly pass: () => {} + private static readonly pass: () => void private readonly _upstream: Store private readonly _upstreamCallbackHandler: ListenerTracker | undefined private _upstreamPingCount: number = -1 private _unregisterFromUpstream: () => void private readonly _f: (t: TIn) => T - private readonly _extraStores: Store[] | undefined + private readonly _extraStores: Store[] | undefined private _unregisterFromExtraStores: (() => void)[] | undefined private _callbacks: ListenerTracker = new ListenerTracker() private _callbacksAreRegistered = false @@ -509,7 +526,7 @@ class MappedStore extends Store { constructor( upstream: Store, f: (t: TIn) => T, - extraStores: Store[], + extraStores: Store[], upstreamListenerHandler: ListenerTracker | undefined, initialState: T, onDestroy?: (f: () => void) => void @@ -551,10 +568,10 @@ class MappedStore extends Store { map( f: (t: T) => J, - extraStores: Store[] = undefined, + extraStores: Store[] = undefined, ondestroyCallback?: (f: () => void) => void ): Store { - let stores: Store[] = undefined + let stores: Store[] = undefined if (extraStores?.length > 0 || this._extraStores?.length > 0) { stores = [] } @@ -578,7 +595,7 @@ class MappedStore extends Store { ) } - addCallback(callback: (data: T) => any | boolean | void): () => void { + addCallback(callback: (data: T) => unknown | boolean | void): () => void { if (!this._callbacksAreRegistered) { // This is the first callback that is added // We register this 'map' to the upstream object and all the streams @@ -593,7 +610,7 @@ class MappedStore extends Store { } } - addCallbackAndRun(callback: (data: T) => any | boolean | void): () => void { + addCallbackAndRun(callback: (data: T) => unknown | boolean | void): () => void { const unregister = this.addCallback(callback) const doRemove = callback(this.data) if (doRemove === true) { @@ -603,7 +620,7 @@ class MappedStore extends Store { return unregister } - addCallbackAndRunD(callback: (data: T) => any | boolean | void): () => void { + addCallbackAndRunD(callback: (data: T) => unknown | boolean | void): () => void { return this.addCallbackAndRun((data) => { if (data !== undefined) { return callback(data) @@ -611,7 +628,7 @@ class MappedStore extends Store { }) } - addCallbackD(callback: (data: T) => any | boolean | void): () => void { + addCallbackD(callback: (data: T) => unknown | boolean | void): () => void { return this.addCallback((data) => { if (data !== undefined) { return callback(data) @@ -626,9 +643,9 @@ class MappedStore extends Store { } private registerCallbacksToUpstream() { - this._unregisterFromUpstream = this._upstream.addCallback((_) => this.update()) + this._unregisterFromUpstream = this._upstream.addCallback(() => this.update()) this._unregisterFromExtraStores = this._extraStores?.map((store) => - store?.addCallback((_) => this.update()) + store?.addCallback(() => this.update()) ) this._callbacksAreRegistered = true } @@ -827,11 +844,11 @@ export class UIEventSource extends Store implements Writable { * If the result of the callback is 'true', the callback is considered finished and will be removed again * @param callback */ - public addCallback(callback: (latestData: T) => boolean | void | any): () => void { + public addCallback(callback: (latestData: T) => boolean | void | unknown): () => void { return this._callbacks.addCallback(callback) } - public addCallbackAndRun(callback: (latestData: T) => boolean | void | any): () => void { + public addCallbackAndRun(callback: (latestData: T) => boolean | void | unknown): () => void { const doDeleteCallback = callback(this.data) if (doDeleteCallback !== true) { return this.addCallback(callback) @@ -896,7 +913,7 @@ export class UIEventSource extends Store implements Writable { */ public map( f: (t: T) => J, - extraSources: Store[] = [], + extraSources: Store[] = [], onDestroy?: (f: () => void) => void ): Store { return new MappedStore(this, f, extraSources, this._callbacks, f(this.data), onDestroy) @@ -908,7 +925,7 @@ export class UIEventSource extends Store implements Writable { */ public mapD( f: (t: Exclude) => J, - extraSources: Store[] = [], + extraSources: Store[] = [], callbackDestroyFunction?: (f: () => void) => void ): Store { return new MappedStore( @@ -926,7 +943,7 @@ export class UIEventSource extends Store implements Writable { this._callbacks, this.data === undefined || this.data === null ? this.data - : f(this.data), + : f(>this.data), callbackDestroyFunction ) } @@ -945,7 +962,7 @@ export class UIEventSource extends Store implements Writable { */ public sync( f: (t: T) => J, - extraSources: Store[], + extraSources: Store[], g: (j: J, t: T) => T, allowUnregister = false ): UIEventSource {