// (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 { CoreUtils } from '@services/utils/utils'; import { LoadingController } from '@singletons'; /** * Dismiss listener. */ export type CoreIonLoadingElementDismissListener = () => unknown; /** * Class to improve the behaviour of HTMLIonLoadingElement. * * In addition to present/dismiss, this loader can also be paused/resumed in order to allow stacking * modals in top of one another without interfering. Conceptually, a paused loader is still * active but will not be shown in the UI. */ export class CoreIonLoadingElement { protected scheduled = false; protected paused = false; protected listeners: CoreIonLoadingElementDismissListener[] = []; protected asyncLoadingElement?: Promise; constructor(protected text?: string) { } /** * Dismiss the loading element. * * @param data Dismiss data. * @param role Dismiss role. */ async dismiss(data?: unknown, role?: string): Promise { if (this.paused) { this.paused = false; this.listeners.forEach(listener => listener()); return; } if (!this.asyncLoadingElement) { if (this.scheduled) { this.scheduled = false; this.listeners.forEach(listener => listener()); } return; } const asyncLoadingElement = this.asyncLoadingElement; delete this.asyncLoadingElement; const loadingElement = await asyncLoadingElement; await loadingElement.dismiss(data, role); this.listeners.forEach(listener => listener()); } /** * Register dismiss listener. * * @param listener Listener. */ onDismiss(listener: CoreIonLoadingElementDismissListener): void { this.listeners.push(listener); } /** * Hide the loading element. */ async pause(): Promise { if (!this.asyncLoadingElement) { return; } this.paused = true; const asyncLoadingElement = this.asyncLoadingElement; delete this.asyncLoadingElement; const loadingElement = await asyncLoadingElement; loadingElement.dismiss(); } /** * Present the loading element. */ async present(): Promise { if (this.paused || this.scheduled || this.asyncLoadingElement) { return; } // Wait a bit before presenting the modal, to prevent it being displayed if dismiss is called fast. this.scheduled = true; await CoreUtils.wait(40); if (!this.scheduled) { return; } // Present modal. this.scheduled = false; await this.presentLoadingElement(); } /** * Show loading element. */ async resume(): Promise { if (!this.paused) { return; } this.paused = false; await this.presentLoadingElement(); } /** * Update text in the loading element. * * @param text Text. */ async updateText(text: string): Promise { this.text = text; if (!this.asyncLoadingElement) { return; } const loadingElement = await this.asyncLoadingElement; const contentElement = loadingElement.querySelector('.loading-content'); if (contentElement) { contentElement.innerHTML = text; } } /** * Create and present the loading element. */ private async presentLoadingElement(): Promise { let resolveLoadingElement!: ((loadingElement: HTMLIonLoadingElement) => void); this.asyncLoadingElement = new Promise(resolve => resolveLoadingElement = resolve); const loadingElement = await LoadingController.create({ message: this.text }); await loadingElement.present(); resolveLoadingElement(loadingElement); } }