MOBILE-3833 core: Implement cancellable promise
parent
1b6e578c41
commit
894e4a7b62
|
@ -0,0 +1,56 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CorePromise } from '@classes/promise';
|
||||
|
||||
/**
|
||||
* Promise whose execution can be cancelled.
|
||||
*/
|
||||
export class CoreCancellablePromise<T = unknown> extends CorePromise<T> {
|
||||
|
||||
/**
|
||||
* Create a new resolved promise.
|
||||
*
|
||||
* @returns Resolved promise.
|
||||
*/
|
||||
static resolve(): CoreCancellablePromise<void>;
|
||||
static resolve<T>(result: T): CoreCancellablePromise<T>;
|
||||
static resolve<T>(result?: T): CoreCancellablePromise<T> {
|
||||
return new this(resolve => result ? resolve(result) : (resolve as () => void)(), () => {
|
||||
// Nothing to do here.
|
||||
});
|
||||
}
|
||||
|
||||
protected cancelPromise: () => void;
|
||||
|
||||
constructor(
|
||||
executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: Error) => void) => void,
|
||||
cancelPromise: () => void,
|
||||
) {
|
||||
super(new Promise(executor));
|
||||
|
||||
this.cancelPromise = cancelPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel promise.
|
||||
*
|
||||
* After this method is called, the promise will remain unresolved forever. Make sure that after calling
|
||||
* this method there aren't any references to this object, or it could cause memory leaks.
|
||||
*/
|
||||
cancel(): void {
|
||||
this.cancelPromise();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* Base class to use for implementing custom Promises.
|
||||
*/
|
||||
export abstract class CorePromise<T = unknown> implements Promise<T> {
|
||||
|
||||
protected nativePromise: Promise<T>;
|
||||
|
||||
constructor(nativePromise: Promise<T>) {
|
||||
this.nativePromise = nativePromise;
|
||||
}
|
||||
|
||||
[Symbol.toStringTag]: string;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
||||
onRejected?: ((reason: Error) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.nativePromise.then(onFulfilled, onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
catch<TResult = never>(
|
||||
onRejected?: ((reason: Error) => TResult | PromiseLike<TResult>) | undefined | null,
|
||||
): Promise<T | TResult> {
|
||||
return this.nativePromise.catch(onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.nativePromise.finally(onFinally);
|
||||
}
|
||||
|
||||
}
|
|
@ -12,10 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CorePromise } from '@classes/promise';
|
||||
|
||||
/**
|
||||
* Promise wrapper to expose result synchronously.
|
||||
*/
|
||||
export class CorePromisedValue<T = unknown> implements Promise<T> {
|
||||
export class CorePromisedValue<T = unknown> extends CorePromise<T> {
|
||||
|
||||
/**
|
||||
* Wrap an existing promise.
|
||||
|
@ -33,20 +35,28 @@ export class CorePromisedValue<T = unknown> implements Promise<T> {
|
|||
return promisedValue;
|
||||
}
|
||||
|
||||
private _resolvedValue?: T;
|
||||
private _rejectedReason?: Error;
|
||||
declare private promise: Promise<T>;
|
||||
declare private _resolve: (result: T) => void;
|
||||
declare private _reject: (error?: Error) => void;
|
||||
protected resolvedValue?: T;
|
||||
protected rejectedReason?: Error;
|
||||
protected resolvePromise!: (result: T) => void;
|
||||
protected rejectPromise!: (error?: Error) => void;
|
||||
|
||||
constructor() {
|
||||
this.initPromise();
|
||||
let resolvePromise!: (result: T) => void;
|
||||
let rejectPromise!: (error?: Error) => void;
|
||||
|
||||
const nativePromise = new Promise<T>((resolve, reject) => {
|
||||
resolvePromise = resolve;
|
||||
rejectPromise = reject;
|
||||
});
|
||||
|
||||
super(nativePromise);
|
||||
|
||||
this.resolvePromise = resolvePromise;
|
||||
this.rejectPromise = rejectPromise;
|
||||
}
|
||||
|
||||
[Symbol.toStringTag]: string;
|
||||
|
||||
get value(): T | null {
|
||||
return this._resolvedValue ?? null;
|
||||
return this.resolvedValue ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +65,7 @@ export class CorePromisedValue<T = unknown> implements Promise<T> {
|
|||
* @return Whether the promise resolved successfuly.
|
||||
*/
|
||||
isResolved(): this is { value: T } {
|
||||
return '_resolvedValue' in this;
|
||||
return 'resolvedValue' in this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,7 +74,7 @@ export class CorePromisedValue<T = unknown> implements Promise<T> {
|
|||
* @return Whether the promise was rejected.
|
||||
*/
|
||||
isRejected(): boolean {
|
||||
return '_rejectedReason' in this;
|
||||
return 'rejectedReason' in this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,32 +86,6 @@ export class CorePromisedValue<T = unknown> implements Promise<T> {
|
|||
return this.isResolved() || this.isRejected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
||||
onRejected?: ((reason: Error) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.promise.then(onFulfilled, onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
catch<TResult = never>(
|
||||
onRejected?: ((reason: Error) => TResult | PromiseLike<TResult>) | undefined | null,
|
||||
): Promise<T | TResult> {
|
||||
return this.promise.catch(onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.promise.finally(onFinally);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the promise.
|
||||
*
|
||||
|
@ -109,13 +93,13 @@ export class CorePromisedValue<T = unknown> implements Promise<T> {
|
|||
*/
|
||||
resolve(value: T): void {
|
||||
if (this.isSettled()) {
|
||||
delete this._rejectedReason;
|
||||
delete this.rejectedReason;
|
||||
|
||||
this.initPromise();
|
||||
this.resetNativePromise();
|
||||
}
|
||||
|
||||
this._resolvedValue = value;
|
||||
this._resolve(value);
|
||||
this.resolvedValue = value;
|
||||
this.resolvePromise(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,32 +109,32 @@ export class CorePromisedValue<T = unknown> implements Promise<T> {
|
|||
*/
|
||||
reject(reason?: Error): void {
|
||||
if (this.isSettled()) {
|
||||
delete this._resolvedValue;
|
||||
delete this.resolvedValue;
|
||||
|
||||
this.initPromise();
|
||||
this.resetNativePromise();
|
||||
}
|
||||
|
||||
this._rejectedReason = reason;
|
||||
this._reject(reason);
|
||||
this.rejectedReason = reason;
|
||||
this.rejectPromise(reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset status and value.
|
||||
*/
|
||||
reset(): void {
|
||||
delete this._resolvedValue;
|
||||
delete this._rejectedReason;
|
||||
delete this.resolvedValue;
|
||||
delete this.rejectedReason;
|
||||
|
||||
this.initPromise();
|
||||
this.resetNativePromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the promise and the callbacks.
|
||||
* Reset native promise and callbacks.
|
||||
*/
|
||||
private initPromise(): void {
|
||||
this.promise = new Promise((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
protected resetNativePromise(): void {
|
||||
this.nativePromise = new Promise((resolve, reject) => {
|
||||
this.resolvePromise = resolve;
|
||||
this.rejectPromise = reject;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,10 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
import { CoreMath } from '@singletons/math';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreFormatTextDirective } from './format-text';
|
||||
import { CoreEventObserver, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
|
||||
/**
|
||||
* Directive to make an element fixed at the bottom collapsible when scrolling.
|
||||
|
@ -48,7 +49,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
protected contentScrollListener?: EventListener;
|
||||
protected endContentScrollListener?: EventListener;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
protected domListener?: CoreSingleTimeEventObserver;
|
||||
protected domPromise?: CoreCancellablePromise<void>;
|
||||
|
||||
constructor(el: ElementRef, protected ionContent: IonContent) {
|
||||
this.element = el.nativeElement;
|
||||
|
@ -61,10 +62,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
async ngOnInit(): Promise<void> {
|
||||
// Only if not present or explicitly falsy it will be false.
|
||||
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
||||
this.domPromise = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
|
||||
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
await this.domListener.promise;
|
||||
|
||||
await this.domPromise;
|
||||
await this.waitLoadingsDone();
|
||||
await this.waitFormatTextsRendered(this.element);
|
||||
|
||||
|
@ -234,7 +234,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
this.resizeListener?.off();
|
||||
this.domListener?.off();
|
||||
this.domPromise?.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,12 +13,13 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreEventObserver, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreFormatTextDirective } from './format-text';
|
||||
|
||||
const defaultMaxHeight = 80;
|
||||
|
@ -49,7 +50,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
|||
protected maxHeight = defaultMaxHeight;
|
||||
protected expandedHeight = 0;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
protected domListener?: CoreSingleTimeEventObserver;
|
||||
protected domPromise?: CoreCancellablePromise<void>;
|
||||
|
||||
constructor(el: ElementRef<HTMLElement>) {
|
||||
this.element = el.nativeElement;
|
||||
|
@ -96,8 +97,9 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
|||
* @return Promise resolved when loadings are done.
|
||||
*/
|
||||
protected async waitLoadingsDone(): Promise<void> {
|
||||
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
await this.domListener.promise;
|
||||
this.domPromise = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
|
||||
await this.domPromise;
|
||||
|
||||
const page = this.element.closest('.ion-page');
|
||||
|
||||
|
@ -242,7 +244,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
|||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.resizeListener?.off();
|
||||
this.domListener?.off();
|
||||
this.domPromise?.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import { CoreFilterHelper } from '@features/filter/services/filter-helper';
|
|||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreCollapsibleItemDirective } from './collapsible-item';
|
||||
import { CoreSingleTimeEventObserver } from '@singletons/events';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
|
||||
/**
|
||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||
|
@ -90,7 +90,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy {
|
|||
protected element: HTMLElement;
|
||||
protected emptyText = '';
|
||||
protected contentSpan: HTMLElement;
|
||||
protected domListener?: CoreSingleTimeEventObserver;
|
||||
protected domPromise?: CoreCancellablePromise<void>;
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
|
@ -133,7 +133,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy {
|
|||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.domListener?.off();
|
||||
this.domPromise?.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -556,8 +556,9 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy {
|
|||
* @return The width of the element in pixels.
|
||||
*/
|
||||
protected async getElementWidth(): Promise<number> {
|
||||
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
await this.domListener.promise;
|
||||
this.domPromise = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
|
||||
await this.domPromise;
|
||||
|
||||
let width = this.element.getBoundingClientRect().width;
|
||||
if (!width) {
|
||||
|
@ -711,7 +712,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy {
|
|||
let width: string | number;
|
||||
let height: string | number;
|
||||
|
||||
await CoreDomUtils.waitToBeInDOM(iframe, 5000).promise;
|
||||
await CoreDomUtils.waitToBeInDOM(iframe, 5000);
|
||||
|
||||
if (iframe.width) {
|
||||
width = iframe.width;
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Directive, ElementRef, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreSingleTimeEventObserver } from '@singletons/events';
|
||||
|
||||
/**
|
||||
* Directive to listen when an element becomes visible.
|
||||
|
@ -27,7 +27,7 @@ export class CoreOnAppearDirective implements OnInit, OnDestroy {
|
|||
@Output() onAppear = new EventEmitter();
|
||||
|
||||
private element: HTMLElement;
|
||||
protected domListener?: CoreSingleTimeEventObserver;
|
||||
protected domPromise?: CoreCancellablePromise<void>;
|
||||
|
||||
constructor(element: ElementRef) {
|
||||
this.element = element.nativeElement;
|
||||
|
@ -37,8 +37,9 @@ export class CoreOnAppearDirective implements OnInit, OnDestroy {
|
|||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
await this.domListener.promise;
|
||||
this.domPromise = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
|
||||
await this.domPromise;
|
||||
|
||||
this.onAppear.emit();
|
||||
}
|
||||
|
@ -47,7 +48,7 @@ export class CoreOnAppearDirective implements OnInit, OnDestroy {
|
|||
* @inheritdoc
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.domListener?.off();
|
||||
this.domPromise?.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,11 +36,12 @@ import { CoreDomUtils } from '@services/utils/dom';
|
|||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Platform, Translate } from '@singletons';
|
||||
import { CoreEventFormActionData, CoreEventObserver, CoreEvents, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||
import { CoreEventFormActionData, CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreEditorOffline } from '../../services/editor-offline';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||
import { CoreScreen } from '@services/screen';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
|
||||
/**
|
||||
* Component to display a rich text editor if enabled.
|
||||
|
@ -105,7 +106,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
|||
protected selectionChangeFunction?: () => void;
|
||||
protected languageChangedSubscription?: Subscription;
|
||||
protected resizeListener?: CoreEventObserver;
|
||||
protected domListener?: CoreSingleTimeEventObserver;
|
||||
protected domPromise?: CoreCancellablePromise<void>;
|
||||
|
||||
rteEnabled = false;
|
||||
isPhone = false;
|
||||
|
@ -288,8 +289,9 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
|||
* @return Promise resolved when loadings are done.
|
||||
*/
|
||||
protected async waitLoadingsDone(): Promise<void> {
|
||||
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
await this.domListener.promise;
|
||||
this.domPromise = CoreDomUtils.waitToBeInDOM(this.element);
|
||||
|
||||
await this.domPromise;
|
||||
|
||||
const page = this.element.closest('.ion-page');
|
||||
|
||||
|
@ -829,7 +831,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
|||
|
||||
const length = await this.toolbarSlides.length();
|
||||
|
||||
await CoreDomUtils.waitToBeInDOM(this.toolbar.nativeElement, 5000).promise;
|
||||
await CoreDomUtils.waitToBeInDOM(this.toolbar.nativeElement, 5000);
|
||||
|
||||
const width = this.toolbar.nativeElement.getBoundingClientRect().width;
|
||||
|
||||
|
@ -1089,7 +1091,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
|||
this.keyboardObserver?.off();
|
||||
this.labelObserver?.disconnect();
|
||||
this.resizeListener?.off();
|
||||
this.domListener?.off();
|
||||
this.domPromise?.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,7 +53,8 @@ import { NavigationStart } from '@angular/router';
|
|||
import { filter } from 'rxjs/operators';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreEventObserver, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
|
||||
/*
|
||||
* "Utils" service with helper functions for UI, DOM elements and HTML code.
|
||||
|
@ -97,51 +98,46 @@ export class CoreDomUtilsProvider {
|
|||
*
|
||||
* @param element Element to wait.
|
||||
* @param timeout If defined, timeout to wait before rejecting the promise.
|
||||
* @return Promise CoreSingleTimeEventObserver with a promise.
|
||||
* @return Cancellable promise.
|
||||
*/
|
||||
waitToBeInDOM(
|
||||
element: Element,
|
||||
timeout?: number,
|
||||
): CoreSingleTimeEventObserver {
|
||||
let root = element.getRootNode({ composed: true });
|
||||
waitToBeInDOM(element: Element, timeout?: number): CoreCancellablePromise<void> {
|
||||
const root = element.getRootNode({ composed: true });
|
||||
|
||||
if (root === document) {
|
||||
// Already in DOM.
|
||||
return {
|
||||
off: (): void => {
|
||||
// Nothing to do here.
|
||||
},
|
||||
promise: Promise.resolve(),
|
||||
};
|
||||
return CoreCancellablePromise.resolve();
|
||||
}
|
||||
|
||||
let observer: MutationObserver | undefined;
|
||||
let observer: MutationObserver;
|
||||
let observerTimeout: number | undefined;
|
||||
if (timeout) {
|
||||
observerTimeout = window.setTimeout(() => {
|
||||
observer?.disconnect();
|
||||
throw new Error('Waiting for DOM timeout reached');
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
return {
|
||||
off: (): void => {
|
||||
observer?.disconnect();
|
||||
clearTimeout(observerTimeout);
|
||||
},
|
||||
promise: new Promise((resolve) => {
|
||||
return new CoreCancellablePromise<void>(
|
||||
(resolve, reject) => {
|
||||
observer = new MutationObserver(() => {
|
||||
root = element.getRootNode({ composed: true });
|
||||
const root = element.getRootNode({ composed: true });
|
||||
|
||||
if (root === document) {
|
||||
observer?.disconnect();
|
||||
clearTimeout(observerTimeout);
|
||||
observerTimeout && clearTimeout(observerTimeout);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
if (timeout) {
|
||||
observerTimeout = window.setTimeout(() => {
|
||||
observer?.disconnect();
|
||||
|
||||
reject(new Error('Waiting for DOM timeout reached'));
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
observer.observe(document.body, { subtree: true, childList: true });
|
||||
}),
|
||||
};
|
||||
},
|
||||
() => {
|
||||
observer?.disconnect();
|
||||
observerTimeout && clearTimeout(observerTimeout);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,21 +31,6 @@ export interface CoreEventObserver {
|
|||
off: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer instance to stop listening to an observer.
|
||||
*/
|
||||
export interface CoreSingleTimeEventObserver {
|
||||
/**
|
||||
* Stop the observer.
|
||||
*/
|
||||
off: () => void;
|
||||
|
||||
/**
|
||||
* Promise Resolved when event is done (first time).
|
||||
*/
|
||||
promise: Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event payloads.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue