forked from EVOgeek/Vmeda.Online
		
	MOBILE-3833 core: Implement cancellable promise
This commit is contained in:
		
							parent
							
								
									1b6e578c41
								
							
						
					
					
						commit
						894e4a7b62
					
				
							
								
								
									
										56
									
								
								src/core/classes/cancellable-promise.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/core/classes/cancellable-promise.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/core/classes/promise.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/core/classes/promise.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user