nope/lib/observables/nopeObservable.ts
2021-12-04 08:25:26 +01:00

191 lines
4.6 KiB
TypeScript

/**
* @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<T, S = T, G = T>
extends NopeEventEmitter<T, S, G>
implements INopeObservable<T, S, G>
{
protected _emitter: BehaviorSubject<IObservableType<G>>;
public get observable(): BehaviorSubject<IObservableType<G>> {
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<G> | IEventCallback<G>,
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 }));
}
}