From 635662366402350f6607733efd884be6cbf5c868 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Mon, 30 May 2022 18:13:43 +0200 Subject: [PATCH] MOBILE-3988 core: Remove duplicated promise helper --- .../services/handlers/mathjaxloader.ts | 13 +---- .../pages/discussion/discussion.page.ts | 13 ++--- src/core/classes/delegate-sorted.ts | 9 ++-- src/core/classes/promised-value.ts | 7 +++ src/core/classes/queue-runner.ts | 10 ++-- src/core/classes/site.ts | 11 +++-- .../components/compile-html/compile-html.ts | 5 +- src/core/features/compile/services/compile.ts | 2 + .../services/course-options-delegate.ts | 11 +++-- src/core/features/course/services/course.ts | 13 +++-- .../services/fileuploader-helper.ts | 9 ++-- .../features/login/services/login-helper.ts | 43 +++++++---------- .../services/pushnotifications.ts | 27 +++++------ .../classes/handlers/course-option-handler.ts | 8 ++-- .../classes/handlers/user-handler.ts | 6 +-- .../siteplugins/services/siteplugins.ts | 13 ++--- src/core/services/app.ts | 25 +++++----- .../services/database/local-notifications.ts | 4 +- src/core/services/filepool.ts | 21 ++++---- src/core/services/local-notifications.ts | 6 +-- src/core/services/update-manager.ts | 9 ++-- src/core/services/utils/dom.ts | 33 +++++++------ src/core/services/utils/iframe.ts | 9 ++-- src/core/services/utils/utils.ts | 48 ++++--------------- src/core/services/ws.ts | 8 ++-- 25 files changed, 162 insertions(+), 201 deletions(-) diff --git a/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts b/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts index ad398a11d..ba9010260 100644 --- a/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts +++ b/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts @@ -321,17 +321,8 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan return; } - const deferred = CoreUtils.promiseDefer(); - - setTimeout(async () => { - try { - await this.waitForReady(retries + 1); - } finally { - deferred.resolve(); - } - }, 250); - - return deferred.promise; + await CoreUtils.wait(250); + await CoreUtils.ignoreErrors(this.waitForReady(retries + 1)); } /** diff --git a/src/addons/messages/pages/discussion/discussion.page.ts b/src/addons/messages/pages/discussion/discussion.page.ts index a7be06c6e..4f009de55 100644 --- a/src/addons/messages/pages/discussion/discussion.page.ts +++ b/src/addons/messages/pages/discussion/discussion.page.ts @@ -888,18 +888,13 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView * * @return Resolved when done. */ - protected waitForFetch(): Promise { + protected async waitForFetch(): Promise { if (!this.fetching) { - return Promise.resolve(); + return; } - const deferred = CoreUtils.promiseDefer(); - - setTimeout(() => this.waitForFetch().finally(() => { - deferred.resolve(); - }), 400); - - return deferred.promise; + await CoreUtils.wait(400); + await CoreUtils.ignoreErrors(this.waitForFetch()); } /** diff --git a/src/core/classes/delegate-sorted.ts b/src/core/classes/delegate-sorted.ts index 194a9ff2e..829eb0399 100644 --- a/src/core/classes/delegate-sorted.ts +++ b/src/core/classes/delegate-sorted.ts @@ -15,8 +15,8 @@ import { BehaviorSubject, Subject } from 'rxjs'; import { CoreEvents } from '@singletons/events'; import { CoreDelegate, CoreDelegateDisplayHandler, CoreDelegateToDisplay } from './delegate'; -import { CoreUtils } from '@services/utils/utils'; import { CoreSites } from '@services/sites'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Superclass to help creating sorted delegates. @@ -96,18 +96,17 @@ export class CoreSortedDelegate< return this.sortedHandlers; } - const deferred = CoreUtils.promiseDefer(); - + const promisedHandlers = new CorePromisedValue(); const subscription = this.getHandlersObservable().subscribe((handlers) => { if (this.loaded) { subscription?.unsubscribe(); // Return main handlers. - deferred.resolve(handlers); + promisedHandlers.resolve(handlers); } }); - return deferred.promise; + return promisedHandlers; } /** diff --git a/src/core/classes/promised-value.ts b/src/core/classes/promised-value.ts index c4de39afa..02ff73b25 100644 --- a/src/core/classes/promised-value.ts +++ b/src/core/classes/promised-value.ts @@ -55,6 +55,13 @@ export class CorePromisedValue extends CorePromise { this.rejectPromise = rejectPromise; } + /** + * @deprecated since app 4.1. The instance can be directly used as a promise. + */ + get promise(): Promise { + return this; + } + get value(): T | null { return this.resolvedValue ?? null; } diff --git a/src/core/classes/queue-runner.ts b/src/core/classes/queue-runner.ts index 5d9f97731..f58066d08 100644 --- a/src/core/classes/queue-runner.ts +++ b/src/core/classes/queue-runner.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Function to add to the queue. @@ -38,7 +38,7 @@ export type CoreQueueRunnerItem = { /** * Deferred with a promise resolved/rejected with the result of the function. */ - deferred: PromiseDefer; + deferred: CorePromisedValue; }; /** @@ -122,7 +122,7 @@ export class CoreQueueRunner { if (id in this.queue) { if (!options.allowRepeated) { // Item already in queue, return its promise. - return this.queue[id].deferred.promise; + return this.queue[id].deferred; } id = this.getUniqueId(id); @@ -132,7 +132,7 @@ export class CoreQueueRunner { const item = { id, fn, - deferred: CoreUtils.promiseDefer(), + deferred: new CorePromisedValue(), }; this.queue[id] = item; @@ -141,7 +141,7 @@ export class CoreQueueRunner { // Process next item if we haven't reached the max yet. this.processNextItem(); - return item.deferred.promise; + return item.deferred; } } diff --git a/src/core/classes/site.ts b/src/core/classes/site.ts index 31e0b3005..3aa0626b0 100644 --- a/src/core/classes/site.ts +++ b/src/core/classes/site.ts @@ -32,7 +32,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrlUtils, CoreUrlParams } from '@services/utils/url'; -import { CoreUtils, CoreUtilsOpenInBrowserOptions, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils, CoreUtilsOpenInBrowserOptions } from '@services/utils/utils'; import { CoreConstants } from '@/core/constants'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreError } from '@classes/errors/error'; @@ -46,6 +46,7 @@ import { asyncInstance, AsyncInstance } from '../utils/async-instance'; import { CoreDatabaseTable } from './database/database-table'; import { CoreDatabaseCachingStrategy } from './database/database-table-proxy'; import { CoreSilentError } from './errors/silenterror'; +import { CorePromisedValue } from '@classes/promised-value'; /** * QR Code type enumeration. @@ -779,7 +780,7 @@ export class CoreSite { if (preSets.reusePending) { const request = this.requestQueue.find((request) => request.cacheId == cacheId); if (request) { - return request.deferred.promise; + return request.deferred; } } @@ -789,7 +790,7 @@ export class CoreSite { data, preSets, wsPreSets, - deferred: CoreUtils.promiseDefer(), + deferred: new CorePromisedValue(), }; return this.enqueueRequest(request); @@ -813,7 +814,7 @@ export class CoreSite { ); } - return request.deferred.promise; + return request.deferred; } /** @@ -2291,7 +2292,7 @@ type RequestQueueItem = { data: any; // eslint-disable-line @typescript-eslint/no-explicit-any preSets: CoreSiteWSPreSets; wsPreSets: CoreWSPreSets; - deferred: PromiseDefer; + deferred: CorePromisedValue; }; /** diff --git a/src/core/features/compile/components/compile-html/compile-html.ts b/src/core/features/compile/components/compile-html/compile-html.ts index f548647b1..845825aad 100644 --- a/src/core/features/compile/components/compile-html/compile-html.ts +++ b/src/core/features/compile/components/compile-html/compile-html.ts @@ -33,6 +33,7 @@ import { Type, KeyValueDiffer, } from '@angular/core'; +import { CorePromisedValue } from '@classes/promised-value'; import { CoreCompile } from '@features/compile/services/compile'; import { CoreDomUtils } from '@services/utils/dom'; @@ -278,14 +279,14 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { return this.pendingCalls[name].defer.promise; } - const defer = CoreUtils.promiseDefer(); + const defer = new CorePromisedValue(); this.pendingCalls[name] = { params, defer, }; - return defer.promise; + return defer; } } diff --git a/src/core/features/compile/services/compile.ts b/src/core/features/compile/services/compile.ts index 5f6cd1c4c..550a59994 100644 --- a/src/core/features/compile/services/compile.ts +++ b/src/core/features/compile/services/compile.ts @@ -154,6 +154,7 @@ import { ADDON_PRIVATEFILES_SERVICES } from '@addons/privatefiles/privatefiles.m // Import some addon modules that define components, directives and pipes. Only import the important ones. import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module'; import { AddonModWorkshopComponentsModule } from '@addons/mod/workshop/components/components.module'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Service to provide functionalities regarding compiling dynamic HTML and Javascript. @@ -352,6 +353,7 @@ export class CoreCompileProvider { instance['CoreWindow'] = CoreWindow; instance['CoreCache'] = CoreCache; instance['CoreDelegate'] = CoreDelegate; + instance['CorePromisedValue'] = CorePromisedValue; instance['CoreContentLinksHandlerBase'] = CoreContentLinksHandlerBase; instance['CoreContentLinksModuleGradeHandler'] = CoreContentLinksModuleGradeHandler; instance['CoreContentLinksModuleIndexHandler'] = CoreContentLinksModuleIndexHandler; diff --git a/src/core/features/course/services/course-options-delegate.ts b/src/core/features/course/services/course-options-delegate.ts index 038b5d51a..564f4965b 100644 --- a/src/core/features/course/services/course-options-delegate.ts +++ b/src/core/features/course/services/course-options-delegate.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreDelegate, CoreDelegateHandler, CoreDelegateToDisplay } from '@classes/delegate'; import { CoreEvents } from '@singletons/events'; import { CoreSites } from '@services/sites'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils } from '@services/utils/utils'; import { CoreCourseAnyCourseData, CoreCourseAnyCourseDataWithOptions, @@ -28,6 +28,7 @@ import { CoreCourseProvider } from './course'; import { Params } from '@angular/router'; import { makeSingleton } from '@singletons'; import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Interface that all course options handlers must implement. @@ -224,7 +225,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate; + deferred: CorePromisedValue; enabledHandlers: CoreCourseOptionsHandler[]; enabledMenuHandlers: CoreCourseOptionsMenuHandler[]; }; @@ -331,7 +332,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate(); + await new Promise((resolve, reject) => { + const observer = CoreEvents.on(CoreEvents.SITE_PLUGINS_LOADED, () => { + observer?.off(); - const observer = CoreEvents.on(CoreEvents.SITE_PLUGINS_LOADED, () => { - observer?.off(); - - CoreCourseFormatDelegate.openCourse( course, navOptions) - .then(deferred.resolve).catch(deferred.reject); + CoreCourseFormatDelegate.openCourse( course, navOptions).then(resolve).catch(reject); + }); }); - return deferred.promise; + return; } catch (error) { // The site plugin failed to load. The user needs to restart the app to try loading it again. const message = Translate.instant('core.courses.errorloadplugins'); diff --git a/src/core/features/fileuploader/services/fileuploader-helper.ts b/src/core/features/fileuploader/services/fileuploader-helper.ts index f0b2287e2..2c3580fd3 100644 --- a/src/core/features/fileuploader/services/fileuploader-helper.ts +++ b/src/core/features/fileuploader/services/fileuploader-helper.ts @@ -24,7 +24,7 @@ import { CoreFile, CoreFileProvider, CoreFileProgressEvent } from '@services/fil import { CoreDomUtils } from '@services/utils/dom'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreTextUtils } from '@services/utils/text'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils } from '@services/utils/utils'; import { makeSingleton, Translate, Camera, Chooser, Platform, ActionSheetController } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreCanceledError } from '@classes/errors/cancelederror'; @@ -36,6 +36,7 @@ import { CoreIonLoadingElement } from '@classes/ion-loading'; import { CoreWSUploadFileResult } from '@services/ws'; import { CoreSites } from '@services/sites'; import { CoreText } from '@singletons/text'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Helper service to upload files. @@ -44,7 +45,7 @@ import { CoreText } from '@singletons/text'; export class CoreFileUploaderHelperProvider { protected logger: CoreLogger; - protected filePickerDeferred?: PromiseDefer; + protected filePickerDeferred?: CorePromisedValue; protected actionSheet?: HTMLIonActionSheetElement; constructor() { @@ -349,7 +350,7 @@ export class CoreFileUploaderHelperProvider { }]; const handlers = CoreFileUploaderDelegate.getHandlers(mimetypes); - this.filePickerDeferred = CoreUtils.promiseDefer(); + this.filePickerDeferred = new CorePromisedValue(); // Create a button for each handler. handlers.forEach((handler) => { @@ -431,7 +432,7 @@ export class CoreFileUploaderHelperProvider { }); }, 500); - return this.filePickerDeferred.promise; + return this.filePickerDeferred; } /** diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index 108cff604..04e027594 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -1288,34 +1288,27 @@ export class CoreLoginHelperProvider { * * @return Promise resolved if the user accepts to scan QR. */ - showScanQRInstructions(): Promise { - const deferred = CoreUtils.promiseDefer(); - - // Show some instructions first. - CoreDomUtils.showAlertWithOptions({ - header: Translate.instant('core.login.faqwhereisqrcode'), - message: Translate.instant( - 'core.login.faqwhereisqrcodeanswer', - { $image: CoreLoginHelperProvider.FAQ_QRCODE_IMAGE_HTML }, - ), - buttons: [ - { - text: Translate.instant('core.cancel'), - role: 'cancel', - handler: (): void => { - deferred.reject(new CoreCanceledError()); + async showScanQRInstructions(): Promise { + await new Promise((resolve, reject) => { + CoreDomUtils.showAlertWithOptions({ + header: Translate.instant('core.login.faqwhereisqrcode'), + message: Translate.instant( + 'core.login.faqwhereisqrcodeanswer', + { $image: CoreLoginHelperProvider.FAQ_QRCODE_IMAGE_HTML }, + ), + buttons: [ + { + text: Translate.instant('core.cancel'), + role: 'cancel', + handler: () => reject(new CoreCanceledError()), }, - }, - { - text: Translate.instant('core.next'), - handler: (): void => { - deferred.resolve(); + { + text: Translate.instant('core.next'), + handler: () => resolve(), }, - }, - ], + ], + }); }); - - return deferred.promise; } /** diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts index 2477b1cfc..394f86dc6 100644 --- a/src/core/features/pushnotifications/services/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/pushnotifications.ts @@ -256,14 +256,13 @@ export class CorePushNotificationsProvider { return; } - const deferred = CoreUtils.promiseDefer(); + await new Promise(resolve => { + win.PushNotification.enableAnalytics(resolve, (error) => { + this.logger.error('Error enabling or disabling Firebase analytics', enable, error); - win.PushNotification.enableAnalytics(deferred.resolve, (error) => { - this.logger.error('Error enabling or disabling Firebase analytics', enable, error); - deferred.resolve(); - }, !!enable); - - await deferred.promise; + resolve(); + }, !!enable); + }); } /** @@ -357,14 +356,12 @@ export class CorePushNotificationsProvider { return; } - const deferred = CoreUtils.promiseDefer(); - - win.PushNotification.logEvent(deferred.resolve, (error) => { - this.logger.error('Error logging firebase event', name, error); - deferred.resolve(); - }, name, data, !!filter); - - await deferred.promise; + await new Promise(resolve => { + win.PushNotification.logEvent(resolve, (error) => { + this.logger.error('Error logging firebase event', name, error); + resolve(); + }, name, data, !!filter); + }); } /** diff --git a/src/core/features/siteplugins/classes/handlers/course-option-handler.ts b/src/core/features/siteplugins/classes/handlers/course-option-handler.ts index 4f8d04a28..14d8c35fe 100644 --- a/src/core/features/siteplugins/classes/handlers/course-option-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/course-option-handler.ts @@ -27,8 +27,8 @@ import { CoreSitePluginsCourseOptionHandlerData, CoreSitePluginsPlugin, } from '@features/siteplugins/services/siteplugins'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; import { CoreSitePluginsBaseHandler } from './base-handler'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Handler to display a site plugin in course options. @@ -38,7 +38,7 @@ export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandl priority: number; isMenuHandler: boolean; - protected updatingDefer?: PromiseDefer; + protected updatingDefer?: CorePromisedValue; constructor( name: string, @@ -59,7 +59,7 @@ export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandl async isEnabledForCourse(courseId: number): Promise { // Wait for "init" result to be updated. if (this.updatingDefer) { - await this.updatingDefer.promise; + await this.updatingDefer; } return CoreSitePlugins.isHandlerEnabledForCourse( @@ -132,7 +132,7 @@ export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandl * Mark init being updated. */ updatingInit(): void { - this.updatingDefer = CoreUtils.promiseDefer(); + this.updatingDefer = new CorePromisedValue(); } } diff --git a/src/core/features/siteplugins/classes/handlers/user-handler.ts b/src/core/features/siteplugins/classes/handlers/user-handler.ts index 036cc8152..5ca1dc2f7 100644 --- a/src/core/features/siteplugins/classes/handlers/user-handler.ts +++ b/src/core/features/siteplugins/classes/handlers/user-handler.ts @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CorePromisedValue } from '@classes/promised-value'; import { CoreSitePlugins, CoreSitePluginsContent, @@ -26,7 +27,6 @@ import { CoreUserProfileHandlerData, } from '@features/user/services/user-delegate'; import { CoreNavigator } from '@services/navigator'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; import { Md5 } from 'ts-md5'; import { CoreSitePluginsBaseHandler } from './base-handler'; @@ -38,7 +38,7 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle priority: number; type: string; - protected updatingDefer?: PromiseDefer; + protected updatingDefer?: CorePromisedValue; constructor( name: string, @@ -130,7 +130,7 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle * Mark init being updated. */ updatingInit(): void { - this.updatingDefer = CoreUtils.promiseDefer(); + this.updatingDefer = new CorePromisedValue(); } } diff --git a/src/core/features/siteplugins/services/siteplugins.ts b/src/core/features/siteplugins/services/siteplugins.ts index 29ba79e0a..4d71118ca 100644 --- a/src/core/features/siteplugins/services/siteplugins.ts +++ b/src/core/features/siteplugins/services/siteplugins.ts @@ -23,12 +23,13 @@ import { CoreFilepool } from '@services/filepool'; import { CoreLang } from '@services/lang'; import { CoreSites } from '@services/sites'; import { CoreTextUtils } from '@services/utils/text'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils } from '@services/utils/utils'; import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws'; import { makeSingleton } from '@singletons'; import { CoreEvents } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; import { CoreSitePluginsModuleHandler } from '../classes/handlers/module-handler'; +import { CorePromisedValue } from '@classes/promised-value'; const ROOT_CACHE_KEY = 'CoreSitePlugins:'; @@ -44,7 +45,7 @@ export class CoreSitePluginsProvider { protected logger: CoreLogger; protected sitePlugins: {[name: string]: CoreSitePluginsHandler} = {}; // Site plugins registered. protected sitePluginPromises: {[name: string]: Promise} = {}; // Promises of loading plugins. - protected fetchPluginsDeferred: PromiseDefer; + protected fetchPluginsDeferred: CorePromisedValue; protected moduleHandlerInstances: Record = {}; hasSitePluginsLoaded = false; @@ -59,9 +60,9 @@ export class CoreSitePluginsProvider { }); // Initialize deferred at start and on logout. - this.fetchPluginsDeferred = CoreUtils.promiseDefer(); + this.fetchPluginsDeferred = new CorePromisedValue(); CoreEvents.on(CoreEvents.LOGOUT, () => { - this.fetchPluginsDeferred = CoreUtils.promiseDefer(); + this.fetchPluginsDeferred = new CorePromisedValue(); }); } @@ -659,8 +660,8 @@ export class CoreSitePluginsProvider { * * @return Promise resolved when site plugins have been fetched. */ - waitFetchPlugins(): Promise { - return this.fetchPluginsDeferred.promise; + async waitFetchPlugins(): Promise { + await this.fetchPluginsDeferred; } /** diff --git a/src/core/services/app.ts b/src/core/services/app.ts index cedb147db..40a76b704 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -16,7 +16,6 @@ import { Injectable } from '@angular/core'; import { CoreDB } from '@services/db'; import { CoreEvents } from '@singletons/events'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; import { makeSingleton, Keyboard, Network, StatusBar, Platform, Device } from '@singletons'; @@ -28,6 +27,8 @@ import { CoreRedirectPayload } from './navigator'; import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy'; import { asyncInstance } from '../utils/async-instance'; import { CoreDatabaseTable } from '@classes/database/database-table'; +import { CorePromisedValue } from '@classes/promised-value'; +import { Subscription } from 'rxjs'; /** * Factory to provide some global functionalities, like access to the global app database. @@ -47,7 +48,7 @@ export class CoreAppProvider { protected db?: SQLiteDB; protected logger: CoreLogger; - protected ssoAuthenticationDeferred?: PromiseDefer; + protected ssoAuthenticationDeferred?: CorePromisedValue; protected isKeyboardShown = false; protected keyboardOpening = false; protected keyboardClosing = false; @@ -451,14 +452,14 @@ export class CoreAppProvider { * NOT when the browser is opened. */ startSSOAuthentication(): void { - this.ssoAuthenticationDeferred = CoreUtils.promiseDefer(); + this.ssoAuthenticationDeferred = new CorePromisedValue(); // Resolve it automatically after 10 seconds (it should never take that long). const cancelTimeout = setTimeout(() => this.finishSSOAuthentication(), 10000); // If the promise is resolved because finishSSOAuthentication is called, stop the cancel promise. // eslint-disable-next-line promise/catch-or-return - this.ssoAuthenticationDeferred.promise.then(() => clearTimeout(cancelTimeout)); + this.ssoAuthenticationDeferred.then(() => clearTimeout(cancelTimeout)); } /** @@ -486,9 +487,7 @@ export class CoreAppProvider { * @return Promise resolved once SSO authentication finishes. */ async waitForSSOAuthentication(): Promise { - const promise = this.ssoAuthenticationDeferred?.promise; - - await promise; + await this.ssoAuthenticationDeferred; } /** @@ -497,7 +496,9 @@ export class CoreAppProvider { * @param timeout Maximum time to wait, use null to wait forever. */ async waitForResume(timeout: number | null = null): Promise { - let deferred: PromiseDefer | null = CoreUtils.promiseDefer(); + let deferred: CorePromisedValue | null = new CorePromisedValue(); + let resumeSubscription: Subscription | null = null; + let timeoutId: number | null = null; const stopWaiting = () => { if (!deferred) { @@ -505,16 +506,16 @@ export class CoreAppProvider { } deferred.resolve(); - resumeSubscription.unsubscribe(); + resumeSubscription?.unsubscribe(); timeoutId && clearTimeout(timeoutId); deferred = null; }; - const resumeSubscription = Platform.resume.subscribe(stopWaiting); - const timeoutId = timeout ? setTimeout(stopWaiting, timeout) : false; + resumeSubscription = Platform.resume.subscribe(stopWaiting); + timeoutId = timeout ? window.setTimeout(stopWaiting, timeout) : null; - await deferred.promise; + await deferred; } /** diff --git a/src/core/services/database/local-notifications.ts b/src/core/services/database/local-notifications.ts index 4eb333077..b8c735a93 100644 --- a/src/core/services/database/local-notifications.ts +++ b/src/core/services/database/local-notifications.ts @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CorePromisedValue } from '@classes/promised-value'; import { CoreAppSchema } from '@services/app'; -import { PromiseDefer } from '@services/utils/utils'; /** * Database variables for CoreLocalNotifications service. @@ -76,5 +76,5 @@ export const APP_SCHEMA: CoreAppSchema = { export type CodeRequestsQueueItem = { table: string; id: string; - deferreds: PromiseDefer[]; + deferreds: CorePromisedValue[]; }; diff --git a/src/core/services/filepool.ts b/src/core/services/filepool.ts index 3378566b6..87d88974c 100644 --- a/src/core/services/filepool.ts +++ b/src/core/services/filepool.ts @@ -26,7 +26,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreTextUtils } from '@services/utils/text'; import { CoreTimeUtils } from '@services/utils/time'; import { CoreUrlUtils } from '@services/utils/url'; -import { CoreUtils, CoreUtilsOpenFileOptions, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils, CoreUtilsOpenFileOptions } from '@services/utils/utils'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreError } from '@classes/errors/error'; import { CoreConstants } from '@/core/constants'; @@ -53,6 +53,7 @@ import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/da import { lazyMap, LazyMap } from '../utils/lazy-map'; import { asyncInstance, AsyncInstance } from '../utils/async-instance'; import { CoreText } from '@singletons/text'; +import { CorePromisedValue } from '@classes/promised-value'; /* * Factory for handling downloading files and retrieve downloaded files. @@ -94,7 +95,7 @@ export class CoreFilepoolProvider { ]; // To handle file downloads using the queue. - protected queueDeferreds: { [s: string]: { [s: string]: CoreFilepoolPromiseDefer } } = {}; + protected queueDeferreds: { [s: string]: { [s: string]: CoreFilepoolPromisedValue } } = {}; protected sizeCache: {[fileUrl: string]: number} = {}; // A "cache" to store file sizes. // Variables to prevent downloading packages/files twice at the same time. protected packagesPromises: { [s: string]: { [s: string]: Promise } } = {}; @@ -450,7 +451,7 @@ export class CoreFilepoolProvider { this.logger.debug(`File ${fileId} already in queue and does not require update`); if (queueDeferred) { // If we were able to retrieve the queue deferred before, we use that one. - return queueDeferred.promise; + return queueDeferred; } else { // Create a new deferred and return its promise. return this.getQueuePromise(siteId, fileId, true, onProgress); @@ -1889,7 +1890,7 @@ export class CoreFilepoolProvider { fileId: string, create: boolean = true, onProgress?: CoreFilepoolOnProgressCallback, - ): CoreFilepoolPromiseDefer | undefined { + ): CoreFilepoolPromisedValue | undefined { if (!this.queueDeferreds[siteId]) { if (!create) { return; @@ -1900,7 +1901,7 @@ export class CoreFilepoolProvider { if (!create) { return; } - this.queueDeferreds[siteId][fileId] = CoreUtils.promiseDefer(); + this.queueDeferreds[siteId][fileId] = new CorePromisedValue(); } if (onProgress) { @@ -1938,9 +1939,7 @@ export class CoreFilepoolProvider { create: boolean = true, onProgress?: CoreFilepoolOnProgressCallback, ): Promise | undefined { - const deferred = this.getQueueDeferred(siteId, fileId, create, onProgress); - - return deferred?.promise; + return this.getQueueDeferred(siteId, fileId, create, onProgress); } /** @@ -3032,11 +3031,11 @@ export class CoreFilepoolProvider { * @param error String identifier for error message, if rejected. */ protected treatQueueDeferred(siteId: string, fileId: string, resolve: boolean, error?: string): void { - if (this.queueDeferreds[siteId] && this.queueDeferreds[siteId][fileId]) { + if (siteId in this.queueDeferreds && fileId in this.queueDeferreds[siteId]) { if (resolve) { this.queueDeferreds[siteId][fileId].resolve(); } else { - this.queueDeferreds[siteId][fileId].reject(error); + this.queueDeferreds[siteId][fileId].reject(new Error(error)); } delete this.queueDeferreds[siteId][fileId]; } @@ -3138,7 +3137,7 @@ export type CoreFilepoolOnProgressCallback = (event: T) => void; /** * Deferred promise for file pool. It's similar to the result of $q.defer() in AngularJS. */ -type CoreFilepoolPromiseDefer = PromiseDefer & { +type CoreFilepoolPromisedValue = CorePromisedValue & { onProgress?: CoreFilepoolOnProgressCallback; // On Progress function. }; diff --git a/src/core/services/local-notifications.ts b/src/core/services/local-notifications.ts index f486e79e9..c7abe1bba 100644 --- a/src/core/services/local-notifications.ts +++ b/src/core/services/local-notifications.ts @@ -20,7 +20,6 @@ import { CoreApp } from '@services/app'; import { CoreConfig } from '@services/config'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreTextUtils } from '@services/utils/text'; -import { CoreUtils } from '@services/utils/utils'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreQueueRunner } from '@classes/queue-runner'; import { CoreError } from '@classes/errors/error'; @@ -34,6 +33,7 @@ import { SITES_TABLE_NAME, CodeRequestsQueueItem, } from '@services/database/local-notifications'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Service to handle local notifications. @@ -507,7 +507,7 @@ export class CoreLocalNotificationsProvider { * @return Promise resolved when the code is retrieved. */ protected requestCode(table: string, id: string): Promise { - const deferred = CoreUtils.promiseDefer(); + const deferred = new CorePromisedValue(); const key = table + '#' + id; const isQueueEmpty = Object.keys(this.codeRequestsQueue).length == 0; @@ -527,7 +527,7 @@ export class CoreLocalNotificationsProvider { this.processNextRequest(); } - return deferred.promise; + return deferred; } /** diff --git a/src/core/services/update-manager.ts b/src/core/services/update-manager.ts index 3d1280923..a896c9ea4 100644 --- a/src/core/services/update-manager.ts +++ b/src/core/services/update-manager.ts @@ -21,9 +21,10 @@ import { makeSingleton } from '@singletons'; import { CoreH5P } from '@features/h5p/services/h5p'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreSites } from './sites'; -import { CoreUtils, PromiseDefer } from './utils/utils'; +import { CoreUtils } from './utils/utils'; import { CoreApp } from './app'; import { CoreZoomLevel } from '@features/settings/services/settings-helper'; +import { CorePromisedValue } from '@classes/promised-value'; const VERSION_APPLIED = 'version_applied'; @@ -36,11 +37,11 @@ const VERSION_APPLIED = 'version_applied'; export class CoreUpdateManagerProvider { protected logger: CoreLogger; - protected doneDeferred: PromiseDefer; + protected doneDeferred: CorePromisedValue; constructor() { this.logger = CoreLogger.getInstance('CoreUpdateManagerProvider'); - this.doneDeferred = CoreUtils.promiseDefer(); + this.doneDeferred = new CorePromisedValue(); } /** @@ -49,7 +50,7 @@ export class CoreUpdateManagerProvider { * @return Promise resolved when the load function is done. */ get donePromise(): Promise { - return this.doneDeferred.promise; + return this.doneDeferred; } /** diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index e32d7d869..8a8ea8def 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -678,30 +678,29 @@ export class CoreDomUtilsProvider { * Wait an element to exists using the findFunction. * * @param findFunction The function used to find the element. + * @param retries Number of retries before giving up. + * @param retryAfter Milliseconds to wait before retrying if the element wasn't found. * @return Resolved if found, rejected if too many tries. * @deprecated since app 4.0 Use CoreDom.waitToBeInsideElement instead. */ - waitElementToExist(findFunction: () => HTMLElement | null): Promise { - const promiseInterval = CoreUtils.promiseDefer(); - let tries = 100; + async waitElementToExist( + findFunction: () => HTMLElement | null, + retries: number = 100, + retryAfter: number = 100, + ): Promise { + const element = findFunction(); - const clear = setInterval(() => { - const element: HTMLElement | null = findFunction(); + if (!element && retries === 0) { + throw Error('Element not found'); + } - if (element) { - clearInterval(clear); - promiseInterval.resolve(element); - } else { - tries--; + if (!element) { + await CoreUtils.wait(retryAfter); - if (tries <= 0) { - clearInterval(clear); - promiseInterval.reject(); - } - } - }, 100); + return this.waitElementToExist(findFunction, retries - 1); + } - return promiseInterval.promise; + return element; } /** diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts index 08745cb44..4805a657a 100644 --- a/src/core/services/utils/iframe.ts +++ b/src/core/services/utils/iframe.ts @@ -22,7 +22,7 @@ import { CoreFileHelper } from '@services/file-helper'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUrlUtils } from '@services/utils/url'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; +import { CoreUtils } from '@services/utils/utils'; import { makeSingleton, Network, NgZone, Translate, Diagnostic } from '@singletons'; import { CoreLogger } from '@singletons/logger'; @@ -30,6 +30,7 @@ import { CoreUrl } from '@singletons/url'; import { CoreWindow } from '@singletons/window'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreText } from '@singletons/text'; +import { CorePromisedValue } from '@classes/promised-value'; /** * Possible types of frame elements. @@ -48,7 +49,7 @@ export class CoreIframeUtilsProvider { static readonly FRAME_TAGS = ['iframe', 'frame', 'object', 'embed']; protected logger: CoreLogger; - protected waitAutoLoginDefer?: PromiseDefer; + protected waitAutoLoginDefer?: CorePromisedValue; constructor() { this.logger = CoreLogger.getInstance('CoreIframeUtilsProvider'); @@ -193,14 +194,14 @@ export class CoreIframeUtilsProvider { if (this.waitAutoLoginDefer) { // Another iframe is already using auto-login. Wait for it to finish. - await this.waitAutoLoginDefer.promise; + await this.waitAutoLoginDefer; // Return the original URL, we can't request a new auto-login. return url; } // First iframe requesting auto-login. - this.waitAutoLoginDefer = CoreUtils.promiseDefer(); + this.waitAutoLoginDefer = new CorePromisedValue(); const finalUrl = await currentSite.getAutoLoginUrl(url, false); diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index 0485bb253..44778e743 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -34,6 +34,7 @@ import { CoreFileEntry } from '@services/file-helper'; import { CoreConstants } from '@/core/constants'; import { CoreWindow } from '@singletons/window'; import { CoreColors } from '@singletons/colors'; +import { CorePromisedValue } from '@classes/promised-value'; type TreeNode = T & { children: TreeNode[] }; @@ -48,7 +49,7 @@ export class CoreUtilsProvider { protected logger: CoreLogger; protected iabInstance?: InAppBrowserObject; protected uniqueIds: {[name: string]: number} = {}; - protected qrScanData?: {deferred: PromiseDefer; observable: Subscription}; + protected qrScanData?: {deferred: CorePromisedValue; observable: Subscription}; protected initialColorSchemeContent = 'light dark'; constructor() { @@ -1325,18 +1326,13 @@ export class CoreUtilsProvider { } /** - * Similar to AngularJS $q.defer(). + * Create a deferred promise that can be resolved or rejected explicitly. * * @return The deferred promise. + * @deprecated since app 4.1. Use CorePromisedValue instead. */ - promiseDefer(): PromiseDefer { - const deferred: Partial> = {}; - deferred.promise = new Promise((resolve, reject): void => { - deferred.resolve = resolve as (value?: T | undefined) => void; - deferred.reject = reject; - }); - - return deferred as PromiseDefer; + promiseDefer(): CorePromisedValue { + return new CorePromisedValue(); } /** @@ -1635,12 +1631,12 @@ export class CoreUtilsProvider { if (this.qrScanData && this.qrScanData.deferred) { // Already scanning. - return this.qrScanData.deferred.promise; + return this.qrScanData.deferred; } // Start scanning. this.qrScanData = { - deferred: this.promiseDefer(), + deferred: new CorePromisedValue(), // When text is received, stop scanning and return the text. observable: QRScanner.scan().subscribe(text => this.stopScanQR(text, false)), @@ -1659,7 +1655,7 @@ export class CoreUtilsProvider { colorSchemeMeta.setAttribute('content', 'normal'); } - return this.qrScanData.deferred.promise; + return this.qrScanData.deferred; } catch (e) { this.stopScanQR(e, true); @@ -1697,7 +1693,7 @@ export class CoreUtilsProvider { this.qrScanData.observable.unsubscribe(); // Stop scanning. if (error) { - this.qrScanData.deferred.reject(data); + this.qrScanData.deferred.reject(typeof data === 'string' ? new Error(data) : data); } else if (data !== undefined) { this.qrScanData.deferred.resolve(data as string); } else { @@ -1769,30 +1765,6 @@ export class CoreUtilsProvider { export const CoreUtils = makeSingleton(CoreUtilsProvider); -/** - * Deferred promise. It's similar to the result of $q.defer() in AngularJS. - */ -export type PromiseDefer = { - /** - * The promise. - */ - promise: Promise; - - /** - * Function to resolve the promise. - * - * @param value The resolve value. - */ - resolve: (value?: T) => void; // Function to resolve the promise. - - /** - * Function to reject the promise. - * - * @param reason The reject param. - */ - reject: (reason?: unknown) => void; -}; - /** * Data for each entry of executeOrderedPromises. */ diff --git a/src/core/services/ws.ts b/src/core/services/ws.ts index 38eb55151..306851383 100644 --- a/src/core/services/ws.ts +++ b/src/core/services/ws.ts @@ -26,7 +26,6 @@ import { CoreApp } from '@services/app'; import { CoreFile, CoreFileFormat } from '@services/file'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreTextErrorObject, CoreTextUtils } from '@services/utils/text'; -import { CoreUtils, PromiseDefer } from '@services/utils/utils'; import { CoreConstants } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; import { CoreInterceptor } from '@classes/interceptor'; @@ -38,6 +37,7 @@ import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreSite } from '@classes/site'; import { CoreHttpError } from '@classes/errors/httperror'; +import { CorePromisedValue } from '@classes/promised-value'; /** * This service allows performing WS calls and download/upload files. @@ -76,12 +76,12 @@ export class CoreWSProvider { siteUrl, data, preSets, - deferred: CoreUtils.promiseDefer(), + deferred: new CorePromisedValue(), }; this.retryCalls.push(call); - return call.deferred.promise; + return call.deferred; } /** @@ -1363,7 +1363,7 @@ type RetryCall = { siteUrl: string; data: Record; preSets: CoreWSPreSets; - deferred: PromiseDefer; + deferred: CorePromisedValue; }; /**