mute feature
This commit is contained in:
@@ -290,7 +290,7 @@ describe('UmbElementMixin', () => {
|
||||
const check2: CheckType<A, string> = value as string;
|
||||
const check3: CheckType<A, null> = value as null;
|
||||
const check4: CheckType<A, undefined> = value as undefined;
|
||||
expect(check).to.be.equal('hello');
|
||||
expect(check).to.be.equal(null);
|
||||
expect(check2 === check3 && check2 === check4).to.be.true; // Just to use the const for something.
|
||||
});
|
||||
// Because the source is potentially undefined, the controller could be undefined and the value of the callback method could be undefined [NL]
|
||||
|
||||
@@ -35,6 +35,7 @@ export class UmbArrayState<T> extends UmbDeepState<T[]> {
|
||||
*/
|
||||
sortBy(sortMethod?: (a: T, b: T) => number) {
|
||||
this.#sortMethod = sortMethod;
|
||||
super.setValue(this.getValue().sort(this.#sortMethod));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,19 @@ import { UmbBasicState } from './basic-state.js';
|
||||
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
|
||||
*/
|
||||
export class UmbDeepState<T> extends UmbBasicState<T> {
|
||||
#mute?: boolean;
|
||||
#value: T;
|
||||
|
||||
constructor(initialData: T) {
|
||||
super(deepFreeze(initialData));
|
||||
this.#value = this._subject.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @method createObservablePart
|
||||
* @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return.
|
||||
* @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different.
|
||||
* @returns {Observable<R>}
|
||||
* @description - Creates an Observable from this State.
|
||||
*/
|
||||
asObservablePart<ReturnType>(
|
||||
@@ -39,9 +43,63 @@ export class UmbDeepState<T> extends UmbBasicState<T> {
|
||||
setValue(data: T): void {
|
||||
if (!this._subject) return;
|
||||
const frozenData = deepFreeze(data);
|
||||
// Only update data if its different than current data.
|
||||
if (!jsonStringComparison(frozenData, this._subject.getValue())) {
|
||||
this.#value = frozenData;
|
||||
// Only update data if its not muted and is different than current data. [NL]
|
||||
if (!this.#mute && !jsonStringComparison(frozenData, this._subject.getValue())) {
|
||||
this._subject.next(frozenData);
|
||||
}
|
||||
}
|
||||
|
||||
getValue(): T {
|
||||
return this.#value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method mute
|
||||
* @description - Set mute for this state.
|
||||
*/
|
||||
mute() {
|
||||
if (this.#mute) return;
|
||||
this.#mute = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method unmute
|
||||
* @description - Unset the mute of this state.
|
||||
*/
|
||||
unmute() {
|
||||
if (!this.#mute) return;
|
||||
this.#mute = false;
|
||||
// Only update data if it is different than current data. [NL]
|
||||
if (!jsonStringComparison(this.#value, this._subject.getValue())) {
|
||||
this._subject?.next(this.#value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method isMuted
|
||||
* @description - Check if the state is muted.
|
||||
* @returns {boolean} - Returns true if the state is muted.
|
||||
*/
|
||||
isMuted() {
|
||||
return this.#mute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method getMutePromise
|
||||
* @description - Get a promise which resolves when the mute is unset.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
getMutePromise() {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (!this.#mute) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const subscription = this._subject.subscribe(() => {
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,50 @@ describe('UmbObjectState', () => {
|
||||
subject.update({ key: 'change_this_first_should_not_trigger_update' });
|
||||
subject.update({ another: 'myNewValue' });
|
||||
});
|
||||
|
||||
it('replays the latests value when unmuted.', (done) => {
|
||||
let amountOfCallbacks = 0;
|
||||
|
||||
const observer = subject.asObservable();
|
||||
observer.subscribe((value) => {
|
||||
amountOfCallbacks++;
|
||||
if (amountOfCallbacks === 1) {
|
||||
// First callback gives us the initialized value.
|
||||
expect(value.key).to.be.equal('some');
|
||||
}
|
||||
if (amountOfCallbacks === 2) {
|
||||
// Second callback gives us the first change.
|
||||
expect(value.key).to.be.equal('firstChange');
|
||||
}
|
||||
if (amountOfCallbacks === 3) {
|
||||
// Third callback gives us the last change before unmuted.
|
||||
expect(value.key).to.be.equal('thirdChange');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
subject.update({ key: 'firstChange' });
|
||||
subject.mute();
|
||||
subject.update({ key: 'secondChange' });
|
||||
subject.update({ key: 'thirdChange' });
|
||||
subject.unmute();
|
||||
});
|
||||
|
||||
/*
|
||||
it('replays latests unmuted value when muted.', (done) => {
|
||||
const observer = subject.asObservable();
|
||||
observer.subscribe((value) => {
|
||||
expect(value).to.be.equal(initialData);
|
||||
});
|
||||
|
||||
subject.mute();
|
||||
subject.update({ key: 'firstChange' });
|
||||
|
||||
observer.subscribe((value) => {
|
||||
expect(value).to.be.equal(initialData);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
@@ -10,9 +10,6 @@ import { UmbDeepState } from './deep-state.js';
|
||||
* The UmbObjectState provides methods to append data when the data is an Object.
|
||||
*/
|
||||
export class UmbObjectState<T> extends UmbDeepState<T> {
|
||||
#partialUpdateData?: Partial<T>;
|
||||
#partialUpdateDebounce?: boolean;
|
||||
|
||||
/**
|
||||
* @method update
|
||||
* @param {Partial<T>} partialData - A object containing some of the data to update in this Subject.
|
||||
@@ -23,39 +20,8 @@ export class UmbObjectState<T> extends UmbDeepState<T> {
|
||||
* const myState = new UmbObjectState(data);
|
||||
* myState.update({value: 'myNewValue'});
|
||||
*/
|
||||
|
||||
update(partialData: Partial<T>) {
|
||||
this.setValue({ ...this._subject.getValue(), ...partialData });
|
||||
this.setValue({ ...this.getValue(), ...partialData });
|
||||
return this;
|
||||
}
|
||||
/*
|
||||
update(partialData: Partial<T>) {
|
||||
this.#partialUpdateData = { ...this.#partialUpdateData, ...partialData };
|
||||
this.#performUpdate();
|
||||
return this;
|
||||
}
|
||||
|
||||
async #performUpdate() {
|
||||
if (this.#partialUpdateDebounce) return;
|
||||
this.#partialUpdateDebounce = true;
|
||||
await Promise.resolve();
|
||||
if (this.#partialUpdateData) {
|
||||
this.setValue({ ...this._subject.getValue(), ...this.#partialUpdateData });
|
||||
this.#partialUpdateData = undefined;
|
||||
}
|
||||
this.#partialUpdateDebounce = false;
|
||||
}
|
||||
|
||||
//getValue? — should this also include the partial update? but be aware that getValue is used for setValue comparison....!! [NL]
|
||||
|
||||
setValue(data: T): void {
|
||||
if (this.#partialUpdateData) {
|
||||
console.error('SetValue was called while in debouncing mode.');
|
||||
super.setValue({ ...data, ...this.#partialUpdateData });
|
||||
//this.#partialUpdateData = undefined; // maybe not, cause keeping this enables that to be merged in despite a another change coming from above.
|
||||
} else {
|
||||
super.setValue(data);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user