/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-11-23 08:06:30 * @modify date 2021-11-14 09:03:39 * @desc [description] */ import { BehaviorSubject } from "rxjs"; import { NopeEventEmitter } from "../eventEmitter/nopeEventEmitter"; import { TSubjectOptions } from "../helpers/getSubject"; import { generateId } from "../helpers/idMethods"; import { getNopeLogger } from "../logger/getLogger"; import { IEventAdditionalData, IEventCallback, INopeObservable, INopeObserver, INopePartialObserver, INopeSubscriptionOptions, IObservableType, } from "../types/nope/index"; const logger = getNopeLogger("obervable"); /** * RsJX based Observable. * * Contains additional Functionalities like: * - property with the current value * - function to publish values. (wrapper for next) * - enables performing a subscription with synced call or a immediate call. */ export class NopeObservable extends NopeEventEmitter implements INopeObservable { protected _emitter: BehaviorSubject>; public get observable(): BehaviorSubject> { return this._emitter; } public readonly id: string = generateId(); public _value: T; public options: any = { generateTimeStamp: true, }; /** * Function to specify a Setter */ public setter: | (( value: S | null, sender: string | null, timeStamp: number | null, ...data ) => { data: T | null; valid: boolean; }) | null = null; /** * Function to specify a Getter */ public getter: ((value: T | null) => G | null) | null = null; public setContent( value: S | null, options: IEventAdditionalData = {} ): boolean { return this._emit(value, options); } /** * Function to set the content of the Observable * @param value * @param sender * @param timeStamp * @param data */ protected _emit( value: S | null, options: IEventAdditionalData = {} ): boolean { // Change the Value. if (this.setter !== null) { const adapted = this.setter( value as S, options.sender, options.timestamp ); if (!adapted.valid) { return false; } this._value = adapted.data; } else { // Adapt the Value if required. this._value = value as any as T; } const valueToPublish = this.getContent(); /** Publish the Data */ if ( !this.disablePublishing && (options.forced || this._emitter.value?.value !== valueToPublish) ) { return this._publish(valueToPublish, options); } return false; } /** * Internal Function to Publish content * * @author M.Karkowski * @protected * @param {G} value The value to use. * @param {IEventAdditionalData} [options={}] * @return {*} {boolean} * @memberof NopeObservable */ protected _publish(value: G, options: IEventAdditionalData = {}): boolean { // Only Proceed if Publishing is required. if (options.forced || this.disablePublishing === false) { options = this._updateSenderAndTimestamp(options); // Define the value. this._emitter.next({ value, ...options }); return this.hasSubscriptions; } return false; } /** * Function to Force an Update * * @author M.Karkowski * @param {IEventAdditionalData} options Options which might be relevant * @return {*} {boolean} * @memberof NopeObservable */ public forcePublish(options: IEventAdditionalData = {}): boolean { options.forced = true; return this._publish(this.getContent(), options); } /** * Function to extract the Content. * If a Getter is provided, the Getter will be used * to Transform the item. */ public getContent(): G | null { if (this.getter !== null) return this.getter(this._value); return this._value as any as G; } protected _lastDataUpdate: number; /** * A Function to subscribe to updates of the Observable. * @param observer The Observer. Could be a Function or a Partial Observer. * @param mode The Mode of the Subscription * @param options Additional Options. */ public subscribe( observer: INopePartialObserver | IEventCallback, options: INopeSubscriptionOptions = { type: "sync", mode: ["direct", "sub", "super"], skipCurrent: false, } ): INopeObserver { return this._subscribe(observer, options); } constructor(protected _options: TSubjectOptions = {}) { super(Object.assign(_options, { showCurrent: true })); } }