MOBILE-4616 chore: Create CoreWait to add waiting functions

main
Pau Ferrer Ocaña 2024-07-22 17:06:19 +02:00
parent 7068db3f62
commit 7d9c9b6fe9
46 changed files with 228 additions and 124 deletions

View File

@ -23,6 +23,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreEvents } from '@singletons/events'; import { CoreEvents } from '@singletons/events';
import { CoreSite } from '@classes/sites/site'; import { CoreSite } from '@classes/sites/site';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
import { CoreWait } from '@singletons/wait';
/** /**
* Handler to support the MathJax filter. * Handler to support the MathJax filter.
@ -321,7 +322,7 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan
return; return;
} }
await CoreUtils.wait(250); await CoreWait.wait(250);
await CoreUtils.ignoreErrors(this.waitForReady(retries + 1)); await CoreUtils.ignoreErrors(this.waitForReady(retries + 1));
} }

View File

@ -46,6 +46,7 @@ import { CoreConstants } from '@/core/constants';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreKeyboard } from '@singletons/keyboard'; import { CoreKeyboard } from '@singletons/keyboard';
import { CoreText } from '@singletons/text'; import { CoreText } from '@singletons/text';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that displays a message discussion page. * Page that displays a message discussion page.
@ -884,7 +885,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
return; return;
} }
await CoreUtils.wait(400); await CoreWait.wait(400);
await CoreUtils.ignoreErrors(this.waitForFetch()); await CoreUtils.ignoreErrors(this.waitForFetch());
} }
@ -1072,7 +1073,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
this.setNewMessagesBadge(0); this.setNewMessagesBadge(0);
// Leave time for the view to be rendered. // Leave time for the view to be rendered.
await CoreUtils.nextTicks(5); await CoreWait.nextTicks(5);
if (!this.viewDestroyed && this.content) { if (!this.viewDestroyed && this.content) {
this.content.scrollToBottom(0); this.content.scrollToBottom(0);

View File

@ -32,6 +32,7 @@ import { CoreUser } from '@features/user/services/user';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreTextErrorObject, CoreTextUtils } from '@services/utils/text'; import { CoreTextErrorObject, CoreTextUtils } from '@services/utils/text';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { CoreWait } from '@singletons/wait';
/** /**
* Service to sync messages. * Service to sync messages.
@ -252,7 +253,7 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider<AddonMessage
// In some Moodle versions, wait 1 second to make sure timecreated is different. // In some Moodle versions, wait 1 second to make sure timecreated is different.
// This is because there was a bug where messages with the same timecreated had a wrong order. // This is because there was a bug where messages with the same timecreated had a wrong order.
if (!groupMessagingEnabled && i < messages.length - 1) { if (!groupMessagingEnabled && i < messages.length - 1) {
await CoreUtils.wait(1000); await CoreWait.wait(1000);
} }
} }

View File

@ -16,7 +16,7 @@ import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page'; import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
import { AddonModAssignIndexComponent } from '../../components/index/index'; import { AddonModAssignIndexComponent } from '../../components/index/index';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
/** /**
* Page that displays an assign. * Page that displays an assign.
@ -44,7 +44,7 @@ export class AddonModAssignIndexPage extends CoreCourseModuleMainActivityPage<Ad
async ngAfterViewInit(): Promise<void> { async ngAfterViewInit(): Promise<void> {
switch (this.action) { switch (this.action) {
case 'editsubmission': case 'editsubmission':
await CoreUtils.waitFor(() => !!this.activityComponent?.submissionComponent, { timeout: 5000 }); await CoreWait.waitFor(() => !!this.activityComponent?.submissionComponent, { timeout: 5000 });
await this.activityComponent?.submissionComponent?.goToEdit(); await this.activityComponent?.submissionComponent?.goToEdit();
break; break;

View File

@ -30,6 +30,7 @@ import { AddonModChatFormattedMessage, AddonModChatHelper } from '../../services
import { CoreTime } from '@singletons/time'; import { CoreTime } from '@singletons/time';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreKeyboard } from '@singletons/keyboard'; import { CoreKeyboard } from '@singletons/keyboard';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that displays a chat session. * Page that displays a chat session.
@ -358,7 +359,7 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave {
*/ */
async scrollToBottom(): Promise<void> { async scrollToBottom(): Promise<void> {
// Need a timeout to leave time to the view to be rendered. // Need a timeout to leave time to the view to be rendered.
await CoreUtils.nextTick(); await CoreWait.nextTick();
if (!this.viewDestroyed) { if (!this.viewDestroyed) {
this.content?.scrollToBottom(); this.content?.scrollToBottom();
} }

View File

@ -54,6 +54,7 @@ import { CoreDirectivesRegistry } from '@singletons/directives-registry';
import { CoreWSError } from '@classes/errors/wserror'; import { CoreWSError } from '@classes/errors/wserror';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT, AddonModQuizAttemptStates, ADDON_MOD_QUIZ_COMPONENT } from '../../constants'; import { ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT, AddonModQuizAttemptStates, ADDON_MOD_QUIZ_COMPONENT } from '../../constants';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that allows attempting a quiz. * Page that allows attempting a quiz.
@ -831,7 +832,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
* @param slot Slot of the question to scroll to. * @param slot Slot of the question to scroll to.
*/ */
protected async scrollToQuestion(slot: number): Promise<void> { protected async scrollToQuestion(slot: number): Promise<void> {
await CoreUtils.nextTick(); await CoreWait.nextTick();
await CoreDirectivesRegistry.waitDirectivesReady(this.elementRef.nativeElement, 'core-question'); await CoreDirectivesRegistry.waitDirectivesReady(this.elementRef.nativeElement, 'core-question');
await CoreDom.scrollToElement( await CoreDom.scrollToElement(
this.elementRef.nativeElement, this.elementRef.nativeElement,

View File

@ -49,6 +49,7 @@ import {
ADDON_MOD_SCORM_DATA_AUTO_SYNCED, ADDON_MOD_SCORM_DATA_AUTO_SYNCED,
ADDON_MOD_SCORM_PAGE_NAME, ADDON_MOD_SCORM_PAGE_NAME,
} from '../../constants'; } from '../../constants';
import { CoreWait } from '@singletons/wait';
/** /**
* Component that displays a SCORM entry page. * Component that displays a SCORM entry page.
@ -616,7 +617,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
if (CoreSync.isBlocked(ADDON_MOD_SCORM_COMPONENT, this.scorm.id) && retries < 5) { if (CoreSync.isBlocked(ADDON_MOD_SCORM_COMPONENT, this.scorm.id) && retries < 5) {
// Sync is currently blocked, this can happen when SCORM player is left. Retry in a bit. // Sync is currently blocked, this can happen when SCORM player is left. Retry in a bit.
await CoreUtils.wait(400); await CoreWait.wait(400);
return this.sync(retries + 1); return this.sync(retries + 1);
} }

View File

@ -43,6 +43,7 @@ import {
ADDON_MOD_SCORM_LAUNCH_PREV_SCO_EVENT, ADDON_MOD_SCORM_LAUNCH_PREV_SCO_EVENT,
ADDON_MOD_SCORM_UPDATE_TOC_EVENT, ADDON_MOD_SCORM_UPDATE_TOC_EVENT,
} from '../../constants'; } from '../../constants';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that allows playing a SCORM. * Page that allows playing a SCORM.
@ -436,7 +437,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy {
// Changing SCO. First unload the existing SCO to make sure the callback to send the data has been called. // Changing SCO. First unload the existing SCO to make sure the callback to send the data has been called.
this.src = ''; this.src = '';
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Load the SCO in the existing model. // Load the SCO in the existing model.
this.dataModel.loadSco(sco.id); this.dataModel.loadSco(sco.id);

View File

@ -14,12 +14,12 @@
import { CoreFormatTextDirective } from '@directives/format-text'; import { CoreFormatTextDirective } from '@directives/format-text';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreDirectivesRegistry } from '@singletons/directives-registry';
import { CoreCoordinates, CoreDom } from '@singletons/dom'; import { CoreCoordinates, CoreDom } from '@singletons/dom';
import { CoreEventObserver } from '@singletons/events'; import { CoreEventObserver } from '@singletons/events';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { AddonModQuizDdwtosQuestionData } from '../component/ddwtos'; import { AddonModQuizDdwtosQuestionData } from '../component/ddwtos';
import { CoreWait } from '@singletons/wait';
/** /**
* Class to make a question of ddwtos type work. * Class to make a question of ddwtos type work.
@ -491,7 +491,7 @@ export class AddonQtypeDdwtosQuestion {
} else { } else {
// Group items should always have a parent, add a fallback just in case. // Group items should always have a parent, add a fallback just in case.
await CoreDom.waitToBeInDOM(groupItems[0]); await CoreDom.waitToBeInDOM(groupItems[0]);
await CoreUtils.nextTicks(5); await CoreWait.nextTicks(5);
} }
// Find max height and width. // Find max height and width.

View File

@ -18,7 +18,7 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper'
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { ItemReorderEventDetail } from '@ionic/angular'; import { ItemReorderEventDetail } from '@ionic/angular';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
/** /**
@ -153,7 +153,7 @@ export class AddonQtypeOrderingComponent extends CoreQuestionBaseComponent<Addon
complete: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function complete: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
}); });
await CoreUtils.nextTick(); await CoreWait.nextTick();
// When moving an item to the first or last position, the button that was clicked will be hidden. In this case, we need to // When moving an item to the first or last position, the button that was clicked will be hidden. In this case, we need to
// focus the other button. Otherwise, re-focus the same button since the focus is lost in some cases. // focus the other button. Otherwise, re-focus the same button since the focus is lost in some cases.

View File

@ -27,6 +27,7 @@ import { CorePlatform } from '@services/platform';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { register } from 'swiper/element/bundle'; import { register } from 'swiper/element/bundle';
import { CoreWait } from '@singletons/wait';
register(); register();
@ -83,7 +84,7 @@ export class AppComponent implements OnInit, AfterViewInit {
// Check if the path changes due to the back navigation handler, to know if we're at root level. // Check if the path changes due to the back navigation handler, to know if we're at root level.
// Ionic doc recommends IonRouterOutlet.canGoBack, but there's no easy way to get the current outlet from here. // Ionic doc recommends IonRouterOutlet.canGoBack, but there's no easy way to get the current outlet from here.
// The path seems to change immediately (0 ms timeout), but use 50ms just in case. // The path seems to change immediately (0 ms timeout), but use 50ms just in case.
await CoreUtils.wait(50); await CoreWait.wait(50);
if (CoreNavigator.getCurrentPath() != initialPath) { if (CoreNavigator.getCurrentPath() != initialPath) {
// Ionic has navigated back, nothing else to do. // Ionic has navigated back, nothing else to do.

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { LoadingController } from '@singletons'; import { LoadingController } from '@singletons';
/** /**
@ -105,7 +105,7 @@ export class CoreIonLoadingElement {
// Wait a bit before presenting the modal, to prevent it being displayed if dismiss is called fast. // Wait a bit before presenting the modal, to prevent it being displayed if dismiss is called fast.
this.scheduled = true; this.scheduled = true;
await CoreUtils.wait(40); await CoreWait.wait(40);
if (!this.scheduled) { if (!this.scheduled) {
return; return;

View File

@ -19,6 +19,7 @@ import { AsyncDirective } from './async-directive';
import { PageLoadsManager } from './page-loads-manager'; import { PageLoadsManager } from './page-loads-manager';
import { CorePromisedValue } from './promised-value'; import { CorePromisedValue } from './promised-value';
import { WSObservable } from './sites/authenticated-site'; import { WSObservable } from './sites/authenticated-site';
import { CoreWait } from '@singletons/wait';
/** /**
* Class to watch requests from a page load (including requests from page sub-components). * Class to watch requests from a page load (including requests from page sub-components).
@ -111,7 +112,7 @@ export class PageLoadWatcher {
this.checkHasLoaded(); this.checkHasLoaded();
// Subscription variable might not be set because the observable completed immediately. Wait for next tick. // Subscription variable might not be set because the observable completed immediately. Wait for next tick.
await CoreUtils.nextTick(); await CoreWait.nextTick();
subscription?.unsubscribe(); subscription?.unsubscribe();
}; };

View File

@ -42,6 +42,7 @@ import { Md5 } from 'ts-md5';
import { CoreUrlUtils } from '@services/utils/url'; import { CoreUrlUtils } from '@services/utils/url';
import { CoreSiteWSCacheRecord } from '@services/database/sites'; import { CoreSiteWSCacheRecord } from '@services/database/sites';
import { CoreErrorLogs } from '@singletons/error-logs'; import { CoreErrorLogs } from '@singletons/error-logs';
import { CoreWait } from '@singletons/wait';
/** /**
* Class that represents a site (combination of site + user) where the user has authenticated but the site hasn't been validated * Class that represents a site (combination of site + user) where the user has authenticated but the site hasn't been validated
@ -1584,7 +1585,7 @@ export function chainRequests<T, O extends ObservableInput<any>>(
firstValue = false; firstValue = false;
// Wait to see if the observable is completed (no more values). // Wait to see if the observable is completed (no more values).
await CoreUtils.nextTick(); await CoreWait.nextTick();
if (isCompleted) { if (isCompleted) {
// Current request only returns cached data. Let chained requests update in background. // Current request only returns cached data. Let chained requests update in background.
@ -1601,7 +1602,7 @@ export function chainRequests<T, O extends ObservableInput<any>>(
complete: async () => { complete: async () => {
isCompleted = true; isCompleted = true;
await CoreUtils.nextTick(); await CoreWait.nextTick();
subscriber.complete(); subscriber.complete();
}, },

View File

@ -31,7 +31,7 @@ import { CoreSettingsHelper } from '@features/settings/services/settings-helper'
import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from './aria-role-tab'; import { CoreAriaRoleTab, CoreAriaRoleTabFindable } from './aria-role-tab';
import { CoreEventObserver } from '@singletons/events'; import { CoreEventObserver } from '@singletons/events';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { CoreError } from './errors/error'; import { CoreError } from './errors/error';
import { CorePromisedValue } from './promised-value'; import { CorePromisedValue } from './promised-value';
import { AsyncDirective } from './async-directive'; import { AsyncDirective } from './async-directive';
@ -218,7 +218,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
this.slideChanged(); this.slideChanged();
this.swiper.update(); this.swiper.update();
await CoreUtils.nextTick(); await CoreWait.nextTick();
if (!this.hasSliddenToInitial && this.selectedIndex && this.selectedIndex >= this.swiper.slidesPerViewDynamic()) { if (!this.hasSliddenToInitial && this.selectedIndex && this.selectedIndex >= this.swiper.slidesPerViewDynamic()) {
this.hasSliddenToInitial = true; this.hasSliddenToInitial = true;
@ -344,7 +344,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
} }
this.maxSlides = 3; this.maxSlides = 3;
await CoreUtils.nextTick(); await CoreWait.nextTick();
if (!this.swiper.width) { if (!this.swiper.width) {
return; return;

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { CoreQueueRunner } from '@classes/queue-runner'; import { CoreQueueRunner } from '@classes/queue-runner';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
describe('CoreQueueRunner', () => { describe('CoreQueueRunner', () => {
@ -26,7 +26,7 @@ describe('CoreQueueRunner', () => {
// Act // Act
await Promise.all(range.map((i) => lock.run(async () => { await Promise.all(range.map((i) => lock.run(async () => {
await CoreUtils.wait(Math.floor(Math.random() * 10)); await CoreWait.wait(Math.floor(Math.random() * 10));
items.push(`Item #${i}`); items.push(`Item #${i}`);
}))); })));

View File

@ -14,7 +14,7 @@
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, ViewChild, ElementRef } from '@angular/core'; import { Component, Input, Output, EventEmitter, OnChanges, SimpleChange, ViewChild, ElementRef } from '@angular/core';
import { IonInfiniteScroll } from '@ionic/angular'; import { IonInfiniteScroll } from '@ionic/angular';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
const THRESHOLD = .15; // % of the scroll element height that must be close to the edge to consider loading more items necessary. const THRESHOLD = .15; // % of the scroll element height that must be close to the edge to consider loading more items necessary.
@ -77,8 +77,8 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
} }
// Wait to allow items to render and scroll content to grow. // Wait to allow items to render and scroll content to grow.
await CoreUtils.nextTick(); await CoreWait.nextTick();
await CoreUtils.waitFor(() => scrollElement.scrollHeight > scrollElement.clientHeight, { timeout: 1000 }); await CoreWait.waitFor(() => scrollElement.scrollHeight > scrollElement.clientHeight, { timeout: 1000 });
// Calculate distance from edge. // Calculate distance from edge.
const infiniteHeight = this.hostElement.getBoundingClientRect().height; const infiniteHeight = this.hostElement.getBoundingClientRect().height;
@ -116,7 +116,7 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
*/ */
async complete(): Promise<void> { async complete(): Promise<void> {
// Wait a bit before allowing loading more, otherwise it could be re-triggered automatically when it shouldn't. // Wait a bit before allowing loading more, otherwise it could be re-triggered automatically when it shouldn't.
await CoreUtils.wait(400); await CoreWait.wait(400);
await this.completeLoadMore(); await this.completeLoadMore();
} }

View File

@ -21,6 +21,7 @@ import { CoreDirectivesRegistry } from '@singletons/directives-registry';
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { AsyncDirective } from '@classes/async-directive'; import { AsyncDirective } from '@classes/async-directive';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreWait } from '@singletons/wait';
/** /**
* Component to show a loading spinner and message while data is being loaded. * Component to show a loading spinner and message while data is being loaded.
@ -72,13 +73,13 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
// Throttle 20ms to let mutations resolve. // Throttle 20ms to let mutations resolve.
const throttleMutation = CoreUtils.throttle(async () => { const throttleMutation = CoreUtils.throttle(async () => {
await CoreUtils.nextTick(); await CoreWait.nextTick();
if (!this.loaded) { if (!this.loaded) {
return; return;
} }
this.element.style.display = 'inline'; this.element.style.display = 'inline';
await CoreUtils.nextTick(); await CoreWait.nextTick();
this.element.style.removeProperty('display'); this.element.style.removeProperty('display');
}, 20); }, 20);

View File

@ -17,7 +17,7 @@ import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular
import { CoreModalComponent } from '@classes/modal-component'; import { CoreModalComponent } from '@classes/modal-component';
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { CoreModals } from '@services/modals'; import { CoreModals } from '@services/modals';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { AngularFrameworkDelegate } from '@singletons'; import { AngularFrameworkDelegate } from '@singletons';
import { CoreDirectivesRegistry } from '@singletons/directives-registry'; import { CoreDirectivesRegistry } from '@singletons/directives-registry';
@ -64,13 +64,13 @@ export class CoreSheetModalComponent<T extends CoreModalComponent> implements Af
const wrapper = await this.wrapperElement; const wrapper = await this.wrapperElement;
this.content = await AngularFrameworkDelegate.attachViewToDom(wrapper, this.component, this.componentProps ?? {}); this.content = await AngularFrameworkDelegate.attachViewToDom(wrapper, this.component, this.componentProps ?? {});
await CoreUtils.nextTick(); await CoreWait.nextTick();
this.element.classList.add('active'); this.element.classList.add('active');
this.element.style.zIndex = `${20000 + CoreModals.getTopOverlayIndex()}`; this.element.style.zIndex = `${20000 + CoreModals.getTopOverlayIndex()}`;
await CoreUtils.nextTick(); await CoreWait.nextTick();
await CoreUtils.wait(300); await CoreWait.wait(300);
const instance = CoreDirectivesRegistry.resolve(this.content, this.component); const instance = CoreDirectivesRegistry.resolve(this.content, this.component);
@ -89,8 +89,8 @@ export class CoreSheetModalComponent<T extends CoreModalComponent> implements Af
this.element.classList.remove('active'); this.element.classList.remove('active');
await CoreUtils.nextTick(); await CoreWait.nextTick();
await CoreUtils.wait(300); await CoreWait.wait(300);
await AngularFrameworkDelegate.removeViewFromDom(wrapper, this.content); await AngularFrameworkDelegate.removeViewFromDom(wrapper, this.content);
} }

View File

@ -20,7 +20,7 @@ import { CoreSwipeSlidesItemsManager } from '@classes/items-management/swipe-sli
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { IonContent } from '@ionic/angular'; import { IonContent } from '@ionic/angular';
import { CoreDomUtils, VerticalPoint } from '@services/utils/dom'; import { CoreDomUtils, VerticalPoint } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { NgZone } from '@singletons'; import { NgZone } from '@singletons';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreEventObserver } from '@singletons/events'; import { CoreEventObserver } from '@singletons/events';
@ -230,7 +230,7 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
*/ */
protected async onItemsUpdated(): Promise<void> { protected async onItemsUpdated(): Promise<void> {
// Wait for slides to be added in DOM. // Wait for slides to be added in DOM.
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Update the slides component so the slides list reflects the new items. // Update the slides component so the slides list reflects the new items.
await this.updateSlidesComponent(); await this.updateSlidesComponent();
@ -348,7 +348,7 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
this.swiper.update(); this.swiper.update();
// We need to ensure the slides are updated before continuing. // We need to ensure the slides are updated before continuing.
await CoreUtils.nextTicks(2); await CoreWait.nextTicks(2);
} }
/** /**

View File

@ -17,6 +17,7 @@ import { Directive, Input, ElementRef, AfterViewInit } from '@angular/core';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreWait } from '@singletons/wait';
/** /**
* Directive to auto focus an element when a view is loaded. * Directive to auto focus an element when a view is loaded.
@ -51,7 +52,7 @@ export class CoreAutoFocusDirective implements AfterViewInit {
// Wait in case there is an animation to enter the page, otherwise the interaction // Wait in case there is an animation to enter the page, otherwise the interaction
// between the keyboard appearing and the animation causes a visual glitch. // between the keyboard appearing and the animation causes a visual glitch.
await CoreUtils.wait(540); await CoreWait.wait(540);
CoreDomUtils.focusElement(this.element); CoreDomUtils.focusElement(this.element);

View File

@ -23,6 +23,7 @@ import { CoreEventObserver } from '@singletons/events';
import { CoreLoadingComponent } from '@components/loading/loading'; import { CoreLoadingComponent } from '@components/loading/loading';
import { CoreCancellablePromise } from '@classes/cancellable-promise'; import { CoreCancellablePromise } from '@classes/cancellable-promise';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreWait } from '@singletons/wait';
/** /**
* Directive to make an element fixed at the bottom collapsible when scrolling. * Directive to make an element fixed at the bottom collapsible when scrolling.
@ -101,7 +102,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
await this.viewportPromise; await this.viewportPromise;
this.element.classList.remove('is-active'); this.element.classList.remove('is-active');
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Set a minimum height value. // Set a minimum height value.
this.initialHeight = this.element.getBoundingClientRect().height || this.initialHeight; this.initialHeight = this.element.getBoundingClientRect().height || this.initialHeight;

View File

@ -27,6 +27,7 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreMath } from '@singletons/math'; import { CoreMath } from '@singletons/math';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { CoreFormatTextDirective } from './format-text'; import { CoreFormatTextDirective } from './format-text';
import { CoreWait } from '@singletons/wait';
declare module '@singletons/events' { declare module '@singletons/events' {
@ -344,7 +345,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
await this.visiblePromise; await this.visiblePromise;
this.page.classList.remove('collapsible-header-page-is-active'); this.page.classList.remove('collapsible-header-page-is-active');
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Add floating title and measure initial position. // Add floating title and measure initial position.
const collapsedHeaderTitle = this.collapsedHeader.querySelector('h1') as HTMLHeadingElement; const collapsedHeaderTitle = this.collapsedHeader.querySelector('h1') as HTMLHeadingElement;
@ -429,7 +430,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
} }
// Make sure elements have been added to the DOM. // Make sure elements have been added to the DOM.
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Wait all loadings and tabs to finish loading. // Wait all loadings and tabs to finish loading.
await CoreDirectivesRegistry.waitMultipleDirectivesReady(this.page, [ await CoreDirectivesRegistry.waitMultipleDirectivesReady(this.page, [

View File

@ -56,6 +56,7 @@ import { FrameElement, FrameElementController } from '@classes/element-controlle
import { CoreUrl } from '@singletons/url'; import { CoreUrl } from '@singletons/url';
import { CoreIcons } from '@singletons/icons'; import { CoreIcons } from '@singletons/icons';
import { ContextLevel } from '../constants'; import { ContextLevel } from '../constants';
import { CoreWait } from '@singletons/wait';
/** /**
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective * Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
@ -339,7 +340,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
// Show the element again. // Show the element again.
this.element.classList.remove('core-loading'); this.element.classList.remove('core-loading');
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Emit the afterRender output. // Emit the afterRender output.
this.afterRender.emit(); this.afterRender.emit();
@ -380,7 +381,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
this.elementControllers.forEach(controller => controller.destroy()); this.elementControllers.forEach(controller => controller.destroy());
this.elementControllers = result.elementControllers; this.elementControllers = result.elementControllers;
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Add magnifying glasses to images. // Add magnifying glasses to images.
this.addImageViewerButton(); this.addImageViewerButton();
@ -705,7 +706,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
const previousDisplay = getComputedStyle(this.element).display; const previousDisplay = getComputedStyle(this.element).display;
this.element.style.display = 'inline-block'; this.element.style.display = 'inline-block';
await CoreUtils.nextTick(); await CoreWait.nextTick();
width = this.element.getBoundingClientRect().width; width = this.element.getBoundingClientRect().width;

View File

@ -23,6 +23,7 @@ import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { ContextLevel } from '@/core/constants'; import { ContextLevel } from '@/core/constants';
import { CoreWait } from '@singletons/wait';
/** /**
* Component that displays the list of side blocks. * Component that displays the list of side blocks.
@ -135,8 +136,8 @@ export class CoreBlockSideBlocksComponent implements OnInit {
const selector = '#block-' + this.initialBlockInstanceId; const selector = '#block-' + this.initialBlockInstanceId;
await CoreUtils.waitFor(() => !!this.elementRef.nativeElement.querySelector(selector)); await CoreWait.waitFor(() => !!this.elementRef.nativeElement.querySelector(selector));
await CoreUtils.wait(200); await CoreWait.wait(200);
CoreDom.scrollToElement(this.elementRef.nativeElement, selector, { addYAxis: -10 }); CoreDom.scrollToElement(this.elementRef.nativeElement, selector, { addYAxis: -10 });
} }

View File

@ -85,6 +85,7 @@ import { CorePath } from '@singletons/path';
import { CoreText } from '@singletons/text'; import { CoreText } from '@singletons/text';
import { CoreTime } from '@singletons/time'; import { CoreTime } from '@singletons/time';
import { CoreUrl } from '@singletons/url'; import { CoreUrl } from '@singletons/url';
import { CoreWait } from '@singletons/wait';
import { CoreWindow } from '@singletons/window'; import { CoreWindow } from '@singletons/window';
import { CoreCache } from '@classes/cache'; import { CoreCache } from '@classes/cache';
import { CoreDelegate } from '@classes/delegate'; import { CoreDelegate } from '@classes/delegate';
@ -314,6 +315,7 @@ export class CoreCompileProvider {
instance['CoreText'] = CoreText; instance['CoreText'] = CoreText;
instance['CoreTime'] = CoreTime; instance['CoreTime'] = CoreTime;
instance['CoreUrl'] = CoreUrl; instance['CoreUrl'] = CoreUrl;
instance['CoreWait'] = CoreWait;
instance['CoreWindow'] = CoreWindow; instance['CoreWindow'] = CoreWindow;
instance['CoreCache'] = CoreCache; // @deprecated since 4.4, plugins should use plain objects instead. instance['CoreCache'] = CoreCache; // @deprecated since 4.4, plugins should use plain objects instead.
instance['CoreDelegate'] = CoreDelegate; instance['CoreDelegate'] = CoreDelegate;

View File

@ -24,7 +24,7 @@ import { CoreCourseFormatDelegate } from '@features/course/services/format-deleg
import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; import { CoreCoursesHelper } from '@features/courses/services/courses-helper';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { ModalController } from '@singletons'; import { ModalController } from '@singletons';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
@ -123,11 +123,11 @@ export class CoreCourseCourseIndexComponent implements OnInit {
this.highlighted = CoreCourseFormatDelegate.getSectionHightlightedName(this.course); this.highlighted = CoreCourseFormatDelegate.getSectionHightlightedName(this.course);
// Wait a bit to render the data, otherwise the modal takes a while to appear in big courses or slow devices. // Wait a bit to render the data, otherwise the modal takes a while to appear in big courses or slow devices.
await CoreUtils.wait(400); await CoreWait.wait(400);
this.loaded = true; this.loaded = true;
await CoreUtils.nextTick(); await CoreWait.nextTick();
CoreDom.scrollToElement( CoreDom.scrollToElement(
this.elementRef.nativeElement, this.elementRef.nativeElement,

View File

@ -42,6 +42,7 @@ import { CoreNavigator } from '@services/navigator';
import { CoreRefreshContext, CORE_REFRESH_CONTEXT } from '@/core/utils/refresh-context'; import { CoreRefreshContext, CORE_REFRESH_CONTEXT } from '@/core/utils/refresh-context';
import { CoreCoursesHelper } from '@features/courses/services/courses-helper'; import { CoreCoursesHelper } from '@features/courses/services/courses-helper';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that displays the contents of a course. * Page that displays the contents of a course.
@ -411,7 +412,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
if (scrollTop > 0) { if (scrollTop > 0) {
await CoreUtils.nextTick(); await CoreWait.nextTick();
this.content?.scrollToPoint(0, scrollTop, 0); this.content?.scrollToPoint(0, scrollTop, 0);
} }
} }

View File

@ -30,6 +30,7 @@ import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/course
import { CoreColors } from '@singletons/colors'; import { CoreColors } from '@singletons/colors';
import { CorePath } from '@singletons/path'; import { CorePath } from '@singletons/path';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that displays the list of courses the user is enrolled in. * Page that displays the list of courses the user is enrolled in.
@ -209,7 +210,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
// Select the tab if needed. // Select the tab if needed.
this.firstTabName = undefined; this.firstTabName = undefined;
if (tabToLoad) { if (tabToLoad) {
await CoreUtils.nextTick(); await CoreWait.nextTick();
this.tabsComponent?.selectByIndex(tabToLoad); this.tabsComponent?.selectByIndex(tabToLoad);
} }

View File

@ -31,6 +31,7 @@ import { CoreCourses } from '../../services/courses';
import { CoreTime } from '@singletons/time'; import { CoreTime } from '@singletons/time';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreWait } from '@singletons/wait';
/** /**
* Page that shows a my courses. * Page that shows a my courses.
@ -129,7 +130,7 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective {
this.loadedBlock = blocks.mainBlocks.concat(blocks.sideBlocks).find((block) => block.name == 'myoverview'); this.loadedBlock = blocks.mainBlocks.concat(blocks.sideBlocks).find((block) => block.name == 'myoverview');
this.hasSideBlocks = supportsMyParam && CoreBlockDelegate.hasSupportedBlock(blocks.sideBlocks); this.hasSideBlocks = supportsMyParam && CoreBlockDelegate.hasSupportedBlock(blocks.sideBlocks);
await CoreUtils.nextTicks(2); await CoreWait.nextTicks(2);
this.myOverviewBlock = this.block?.dynamicComponent?.instance as AddonBlockMyOverviewComponent; this.myOverviewBlock = this.block?.dynamicComponent?.instance as AddonBlockMyOverviewComponent;

View File

@ -46,6 +46,7 @@ import { SwiperOptions } from 'swiper/types';
import { ContextLevel } from '@/core/constants'; import { ContextLevel } from '@/core/constants';
import { CoreSwiper } from '@singletons/swiper'; import { CoreSwiper } from '@singletons/swiper';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { CoreWait } from '@singletons/wait';
/** /**
* Component to display a rich text editor if enabled. * Component to display a rich text editor if enabled.
@ -369,7 +370,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
* @returns Blank height in px. Will be negative if no blank space. * @returns Blank height in px. Will be negative if no blank space.
*/ */
protected async getBlankHeightInContent(): Promise<number> { protected async getBlankHeightInContent(): Promise<number> {
await CoreUtils.nextTicks(5); // Ensure content is completely loaded in the DOM. await CoreWait.nextTicks(5); // Ensure content is completely loaded in the DOM.
let content: Element | null = this.element.closest('ion-content'); let content: Element | null = this.element.closest('ion-content');
const contentHeight = await CoreDomUtils.getContentHeight(this.content); const contentHeight = await CoreDomUtils.getContentHeight(this.content);
@ -485,7 +486,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
this.textareaElement?.removeAttribute('hidden'); this.textareaElement?.removeAttribute('hidden');
} }
await CoreUtils.nextTick(); await CoreWait.nextTick();
this.focusRTE(event); this.focusRTE(event);
} }
@ -826,7 +827,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
this.toolbarArrows = true; this.toolbarArrows = true;
} }
await CoreUtils.nextTick(); await CoreWait.nextTick();
this.toolbarSlides.update(); this.toolbarSlides.update();

View File

@ -15,7 +15,7 @@
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { ILocalNotification, ILocalNotificationAction, LocalNotifications } from '@awesome-cordova-plugins/local-notifications/ngx'; import { ILocalNotification, ILocalNotificationAction, LocalNotifications } from '@awesome-cordova-plugins/local-notifications/ngx';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
/** /**
@ -340,7 +340,7 @@ export class LocalNotificationsMock extends LocalNotifications {
// In some testing environments, Notification.requestPermission gets stuck and never returns. // In some testing environments, Notification.requestPermission gets stuck and never returns.
// Given that we don't actually need browser notifications to work in Behat tests, we can just // Given that we don't actually need browser notifications to work in Behat tests, we can just
// continue if the permissions haven't been granted after 1 second. // continue if the permissions haven't been granted after 1 second.
permissionRequests.push(CoreUtils.wait(1000).then(() => 'granted')); permissionRequests.push(CoreWait.wait(1000).then(() => 'granted'));
} }
const permission = await Promise.race(permissionRequests); const permission = await Promise.race(permissionRequests);

View File

@ -21,6 +21,7 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreCancellablePromise } from '@classes/cancellable-promise'; import { CoreCancellablePromise } from '@classes/cancellable-promise';
import { SubPartial } from '@/core/utils/types'; import { SubPartial } from '@/core/utils/types';
import { CoreSharedModule } from '@/core/shared.module'; import { CoreSharedModule } from '@/core/shared.module';
import { CoreWait } from '@singletons/wait';
/** /**
* Component that displays help to connect to a site. * Component that displays help to connect to a site.
@ -118,7 +119,7 @@ export class CoreLoginSiteHelpComponent implements AfterViewInit, OnDestroy {
const answers = Array.from(this.el.nativeElement.querySelectorAll<HTMLElement>('.core-login-site-help--answer')); const answers = Array.from(this.el.nativeElement.querySelectorAll<HTMLElement>('.core-login-site-help--answer'));
await Promise.all(answers.map(async answer => { await Promise.all(answers.map(async answer => {
await this.track(CoreUtils.waitFor(() => answer.clientHeight !== 0)); await this.track(CoreWait.waitFor(() => answer.clientHeight !== 0));
await this.track(CoreDomUtils.waitForImages(answer)); await this.track(CoreDomUtils.waitForImages(answer));
answer.style.setProperty('--height', `${answer.clientHeight}px`); answer.style.setProperty('--height', `${answer.clientHeight}px`);

View File

@ -31,6 +31,7 @@ import { CoreSites } from '@services/sites';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CorePlatform } from '@services/platform'; import { CorePlatform } from '@services/platform';
import { CoreWait } from '@singletons/wait';
const ANIMATION_DURATION = 500; const ANIMATION_DURATION = 500;
@ -203,7 +204,7 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
if (this.loaded && (!mainMenuTab || removedHandlersPages.includes(mainMenuTab))) { if (this.loaded && (!mainMenuTab || removedHandlersPages.includes(mainMenuTab))) {
// No tab selected or handler no longer available, select the first one. // No tab selected or handler no longer available, select the first one.
await CoreUtils.nextTick(); await CoreWait.nextTick();
const tabPage = this.tabs[0] ? this.tabs[0].page : this.morePageName; const tabPage = this.tabs[0] ? this.tabs[0].page : this.morePageName;
const tabPageParams = this.tabs[0] ? this.tabs[0].pageParams : {}; const tabPageParams = this.tabs[0] ? this.tabs[0].pageParams : {};
@ -330,9 +331,9 @@ export class CoreMainMenuPage implements OnInit, OnDestroy {
* Notify that the menu visibility has been updated. * Notify that the menu visibility has been updated.
*/ */
protected async notifyVisibilityUpdated(): Promise<void> { protected async notifyVisibilityUpdated(): Promise<void> {
await CoreUtils.nextTick(); await CoreWait.nextTick();
await CoreUtils.wait(ANIMATION_DURATION); await CoreWait.wait(ANIMATION_DURATION);
await CoreUtils.nextTick(); await CoreWait.nextTick();
CoreEvents.trigger(CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED); CoreEvents.trigger(CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED);
} }

View File

@ -30,6 +30,7 @@ import { IonContent } from '@ionic/angular';
import { CoreScreen } from '@services/screen'; import { CoreScreen } from '@services/screen';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreWait } from '@singletons/wait';
/** /**
* Page to accept a site policy. * Page to accept a site policy.
@ -337,7 +338,7 @@ export class CorePolicySitePolicyPage implements OnInit, OnDestroy {
* Check if the content has scroll. * Check if the content has scroll.
*/ */
protected async checkScroll(): Promise<void> { protected async checkScroll(): Promise<void> {
await CoreUtils.wait(400); await CoreWait.wait(400);
const scrollElement = await this.content?.getScrollElement(); const scrollElement = await this.content?.getScrollElement();

View File

@ -20,7 +20,7 @@ import {
CoreReminderValueAndUnit, CoreReminderValueAndUnit,
} from '@features/reminders/services/reminders'; } from '@features/reminders/services/reminders';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { PopoverController } from '@singletons'; import { PopoverController } from '@singletons';
import { CoreRemindersSetReminderCustomComponent } from '../set-reminder-custom/set-reminder-custom'; import { CoreRemindersSetReminderCustomComponent } from '../set-reminder-custom/set-reminder-custom';
@ -175,7 +175,7 @@ export class CoreRemindersSetReminderMenuComponent implements OnInit {
this.customLabel = CoreReminders.getUnitValueLabel(this.customValue, this.customUnits); this.customLabel = CoreReminders.getUnitValueLabel(this.customValue, this.customUnits);
// Let the dimissed popover to be removed. // Let the dimissed popover to be removed.
await CoreUtils.nextTick(); await CoreWait.nextTick();
PopoverController.dismiss({ timeBefore: Math.abs(this.customValue) * this.customUnits }); PopoverController.dismiss({ timeBefore: Math.abs(this.customValue) * this.customUnits });
} }

View File

@ -27,6 +27,7 @@ import { CoreSubscriptions } from '@singletons/subscriptions';
import { CoreUserToursUserTourComponent } from '../components/user-tour/user-tour'; import { CoreUserToursUserTourComponent } from '../components/user-tour/user-tour';
import { APP_SCHEMA, CoreUserToursDBEntry, USER_TOURS_TABLE_NAME } from './database/user-tours'; import { APP_SCHEMA, CoreUserToursDBEntry, USER_TOURS_TABLE_NAME } from './database/user-tours';
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { CoreWait } from '@singletons/wait';
/** /**
* Service to manage User Tours. * Service to manage User Tours.
@ -113,7 +114,7 @@ export class CoreUserToursService {
protected async show(options: CoreUserToursBasicOptions | CoreUserToursFocusedOptions): Promise<CoreUserToursUserTour> { protected async show(options: CoreUserToursBasicOptions | CoreUserToursFocusedOptions): Promise<CoreUserToursUserTour> {
const { delay, ...componentOptions } = options; const { delay, ...componentOptions } = options;
await CoreUtils.wait(delay ?? 200); await CoreWait.wait(delay ?? 200);
options.after && await this.waitForUserTour(options.after, options.afterTimeout); options.after && await this.waitForUserTour(options.after, options.afterTimeout);

View File

@ -14,7 +14,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Translate, makeSingleton } from '@singletons'; import { Translate, makeSingleton } from '@singletons';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { CoreDom } from '@singletons/dom'; import { CoreDom } from '@singletons/dom';
import { CoreForms } from '@singletons/form'; import { CoreForms } from '@singletons/form';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
@ -113,7 +113,7 @@ export class CoreErrorAccordionService {
wrapper.style.setProperty('--description-height', `${description.clientHeight}px`); wrapper.style.setProperty('--description-height', `${description.clientHeight}px`);
wrapper.classList.add('hydrated'); wrapper.classList.add('hydrated');
await CoreUtils.nextTick(); await CoreWait.nextTick();
hideText.style.display = 'revert'; hideText.style.display = 'revert';
} }

View File

@ -22,7 +22,7 @@ import { Http } from '@singletons';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { CoreSite } from '@classes/sites/site'; import { CoreSite } from '@classes/sites/site';
import { CoreHTMLClasses } from '@singletons/html-classes'; import { CoreHTMLClasses } from '@singletons/html-classes';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
describe('CoreSitesProvider', () => { describe('CoreSitesProvider', () => {
@ -74,7 +74,7 @@ describe('CoreSitesProvider', () => {
CoreEvents.trigger(CoreEvents.LOGIN, {}, '42'); CoreEvents.trigger(CoreEvents.LOGIN, {}, '42');
// Wait the event to be processed. // Wait the event to be processed.
await CoreUtils.nextTick(); await CoreWait.nextTick();
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(true); expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(true);
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false); expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
@ -86,7 +86,7 @@ describe('CoreSitesProvider', () => {
CoreEvents.trigger(CoreEvents.SITE_UPDATED, site.infos , '42'); CoreEvents.trigger(CoreEvents.SITE_UPDATED, site.infos , '42');
// Wait the event to be processed. // Wait the event to be processed.
await CoreUtils.nextTick(); await CoreWait.nextTick();
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true); expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false); expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
@ -99,7 +99,7 @@ describe('CoreSitesProvider', () => {
CoreEvents.trigger(CoreEvents.SITE_ADDED, site.infos , '42'); CoreEvents.trigger(CoreEvents.SITE_ADDED, site.infos , '42');
// Wait the event to be processed. // Wait the event to be processed.
await CoreUtils.nextTick(); await CoreWait.nextTick();
expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true); expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false); expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);

View File

@ -59,6 +59,7 @@ import { CorePasswordModalParams, CorePasswordModalResponse } from '@components/
import { CoreWSError } from '@classes/errors/wserror'; import { CoreWSError } from '@classes/errors/wserror';
import { CoreErrorLogs } from '@singletons/error-logs'; import { CoreErrorLogs } from '@singletons/error-logs';
import { CoreKeyboard } from '@singletons/keyboard'; import { CoreKeyboard } from '@singletons/keyboard';
import { CoreWait } from '@singletons/wait';
/* /*
* "Utils" service with helper functions for UI, DOM elements and HTML code. * "Utils" service with helper functions for UI, DOM elements and HTML code.
@ -333,7 +334,7 @@ export class CoreDomUtilsProvider {
elementToFocus.focus(); elementToFocus.focus();
if (elementToFocus === document.activeElement || (isIonButton && element === document.activeElement)) { if (elementToFocus === document.activeElement || (isIonButton && element === document.activeElement)) {
await CoreUtils.nextTick(); await CoreWait.nextTick();
if (CorePlatform.isAndroid() && this.supportsInputKeyboard(elementToFocus)) { if (CorePlatform.isAndroid() && this.supportsInputKeyboard(elementToFocus)) {
// On some Android versions the keyboard doesn't open automatically. // On some Android versions the keyboard doesn't open automatically.
CoreKeyboard.open(); CoreKeyboard.open();
@ -342,7 +343,7 @@ export class CoreDomUtilsProvider {
} }
// @TODO Probably a Mutation Observer would get this working. // @TODO Probably a Mutation Observer would get this working.
await CoreUtils.wait(50); await CoreWait.wait(50);
retries--; retries--;
} }
} }
@ -1762,7 +1763,7 @@ export class CoreDomUtilsProvider {
} }
// Wait a bit and try again. // Wait a bit and try again.
await CoreUtils.wait(50); await CoreWait.wait(50);
return this.waitForResizeDone(windowWidth, windowHeight, retries+1); return this.waitForResizeDone(windowWidth, windowHeight, retries+1);
} }

View File

@ -41,6 +41,7 @@ import { CoreUrlUtils } from './url';
import { QRScanner } from '@features/native/plugins'; import { QRScanner } from '@features/native/plugins';
import { CoreArray } from '@singletons/array'; import { CoreArray } from '@singletons/array';
import { CoreText } from '@singletons/text'; import { CoreText } from '@singletons/text';
import { CoreWait, CoreWaitOptions } from '@singletons/wait';
export type TreeNode<T> = T & { children: TreeNode<T>[] }; export type TreeNode<T> = T & { children: TreeNode<T>[] };
@ -1783,10 +1784,10 @@ export class CoreUtilsProvider {
* Wait some time. * Wait some time.
* *
* @param milliseconds Number of milliseconds to wait. * @param milliseconds Number of milliseconds to wait.
* @returns Promise resolved after the time has passed. * @deprecated since 4.5. Use CoreWait.wait instead.
*/ */
wait(milliseconds: number): Promise<void> { async wait(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds)); await CoreWait.wait(milliseconds);
} }
/** /**
@ -1794,51 +1795,34 @@ export class CoreUtilsProvider {
* *
* @param condition Condition. * @param condition Condition.
* @returns Cancellable promise. * @returns Cancellable promise.
* @deprecated since 4.5. Use CoreWait.waitFor instead.
*/ */
waitFor(condition: () => boolean): CoreCancellablePromise<void>; waitFor(condition: () => boolean): CoreCancellablePromise<void>;
waitFor(condition: () => boolean, options: CoreUtilsWaitOptions): CoreCancellablePromise<void>; waitFor(condition: () => boolean, options: CoreWaitOptions): CoreCancellablePromise<void>;
waitFor(condition: () => boolean, interval: number): CoreCancellablePromise<void>; waitFor(condition: () => boolean, interval: number): CoreCancellablePromise<void>;
waitFor(condition: () => boolean, optionsOrInterval: CoreUtilsWaitOptions | number = {}): CoreCancellablePromise<void> { waitFor(condition: () => boolean, optionsOrInterval: CoreWaitOptions | number = {}): CoreCancellablePromise<void> {
const options = typeof optionsOrInterval === 'number' ? { interval: optionsOrInterval } : optionsOrInterval; const options = typeof optionsOrInterval === 'number' ? { interval: optionsOrInterval } : optionsOrInterval;
if (condition()) { return CoreWait.waitFor(condition, options);
return CoreCancellablePromise.resolve();
}
const startTime = Date.now();
let intervalId: number | undefined;
return new CoreCancellablePromise<void>(
async (resolve) => {
intervalId = window.setInterval(() => {
if (!condition() && (!options.timeout || (Date.now() - startTime < options.timeout))) {
return;
}
resolve();
window.clearInterval(intervalId);
}, options.interval ?? 50);
},
() => window.clearInterval(intervalId),
);
} }
/** /**
* Wait until the next tick. * Wait until the next tick.
* *
* @returns Promise resolved when tick has been done. * @deprecated since 4.5. Use CoreWait.nextTick instead.
*/ */
nextTick(): Promise<void> { async nextTick(): Promise<void> {
return this.wait(0); await CoreWait.nextTick();
} }
/** /**
* Wait until several next ticks. * Wait until several next ticks.
*
* @param numTicks Number of ticks to wait.
* @deprecated since 4.5. Use CoreWait.nextTicks instead.
*/ */
async nextTicks(numTicks = 0): Promise<void> { async nextTicks(numTicks = 0): Promise<void> {
for (let i = 0; i < numTicks; i++) { await CoreWait.nextTicks(numTicks);
await this.wait(0);
}
} }
/** /**
@ -1916,11 +1900,10 @@ export type CoreUtilsOpenInAppOptions = InAppBrowserOptions & {
/** /**
* Options for waiting. * Options for waiting.
*
* @deprecated since 4.5. Use CoreWaitOptions instead.
*/ */
export type CoreUtilsWaitOptions = { export type CoreUtilsWaitOptions = CoreWaitOptions;
interval?: number;
timeout?: number;
};
/** /**
* Possible default picker actions. * Possible default picker actions.

View File

@ -14,7 +14,7 @@
import { Directive } from '@angular/core'; import { Directive } from '@angular/core';
import { AsyncDirective } from '@classes/async-directive'; import { AsyncDirective } from '@classes/async-directive';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from './wait';
import { CoreLogger } from './logger'; import { CoreLogger } from './logger';
/** /**
@ -135,7 +135,7 @@ export class CoreDirectivesRegistry {
})); }));
// Wait for next tick to ensure directives are completely rendered. // Wait for next tick to ensure directives are completely rendered.
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Check if there are new elements now that the found elements are ready (there could be nested elements). // Check if there are new elements now that the found elements are ready (there could be nested elements).
if (elements.length !== findElements().length) { if (elements.length !== findElements().length) {
@ -181,7 +181,7 @@ export class CoreDirectivesRegistry {
})); }));
// Wait for next tick to ensure directives are completely rendered. // Wait for next tick to ensure directives are completely rendered.
await CoreUtils.nextTick(); await CoreWait.nextTick();
// Check if there are new elements now that the found elements are ready (there could be nested elements). // Check if there are new elements now that the found elements are ready (there could be nested elements).
const elementsAfterReady = directives.reduce((elements, directive) => { const elementsAfterReady = directives.reduce((elements, directive) => {

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { EventEmitter } from '@angular/core'; import { EventEmitter } from '@angular/core';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from './wait';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
/** /**
@ -52,7 +52,7 @@ export class CoreSubscriptions {
}; };
const unsubscribe = async () => { const unsubscribe = async () => {
// Subscription variable might not be set because we can receive a value immediately. Wait for next tick. // Subscription variable might not be set because we can receive a value immediately. Wait for next tick.
await CoreUtils.nextTick(); await CoreWait.nextTick();
subscription?.unsubscribe(); subscription?.unsubscribe();
}; };

View File

@ -0,0 +1,94 @@
// (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 { CoreCancellablePromise } from '@classes/cancellable-promise';
/**
* Singleton with helper functions to wait.
*/
export class CoreWait {
/**
* Wait until the next tick.
*
* @returns Promise resolved when tick has been done.
*/
static async nextTick(): Promise<void> {
return CoreWait.wait(0);
}
/**
* Wait until several next ticks.
*
* @param numTicks Number of ticks to wait.
*/
static async nextTicks(numTicks = 0): Promise<void> {
for (let i = 0; i < numTicks; i++) {
await CoreWait.wait(0);
}
}
/**
* Wait some time.
*
* @param milliseconds Number of milliseconds to wait.
* @returns Promise resolved after the time has passed.
*/
static wait(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
/**
* Wait until a given condition is met.
*
* @param condition Condition.
* @returns Cancellable promise.
*/
static waitFor(condition: () => boolean): CoreCancellablePromise<void>;
static waitFor(condition: () => boolean, options: CoreWaitOptions): CoreCancellablePromise<void>;
static waitFor(condition: () => boolean, interval: number): CoreCancellablePromise<void>;
static waitFor(condition: () => boolean, optionsOrInterval: CoreWaitOptions | number = {}): CoreCancellablePromise<void> {
const options = typeof optionsOrInterval === 'number' ? { interval: optionsOrInterval } : optionsOrInterval;
if (condition()) {
return CoreCancellablePromise.resolve();
}
const startTime = Date.now();
let intervalId: number | undefined;
return new CoreCancellablePromise<void>(
async (resolve) => {
intervalId = window.setInterval(() => {
if (!condition() && (!options.timeout || (Date.now() - startTime < options.timeout))) {
return;
}
resolve();
window.clearInterval(intervalId);
}, options.interval ?? 50);
},
() => window.clearInterval(intervalId),
);
}
}
/**
* Options for waiting.
*/
export type CoreWaitOptions = {
interval?: number;
timeout?: number;
};

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { makeSingleton, NgZone } from '@singletons'; import { makeSingleton, NgZone } from '@singletons';
import { BehatTestsWindow, TestingBehatRuntime } from './behat-runtime'; import { BehatTestsWindow, TestingBehatRuntime } from './behat-runtime';
@ -117,7 +117,7 @@ export class TestingBehatBlockingService {
// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
// "This API does not guarantee that timers will run exactly on schedule. // "This API does not guarantee that timers will run exactly on schedule.
// Delays due to CPU load, other tasks, etc, are to be expected." // Delays due to CPU load, other tasks, etc, are to be expected."
await CoreUtils.nextTicks(10); await CoreWait.nextTicks(10);
} }
// Check there isn't a spinner... // Check there isn't a spinner...
@ -193,7 +193,7 @@ export class TestingBehatBlockingService {
* (and if not, removes it). * (and if not, removes it).
*/ */
protected async checkUIBlocked(): Promise<void> { protected async checkUIBlocked(): Promise<void> {
await CoreUtils.nextTick(); await CoreWait.nextTick();
const blockingElements = Array.from( const blockingElements = Array.from(
document.querySelectorAll<HTMLElement>('div.core-loading-container, ion-loading'), document.querySelectorAll<HTMLElement>('div.core-loading-container, ion-loading'),

View File

@ -14,7 +14,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CorePromisedValue } from '@classes/promised-value'; import { CorePromisedValue } from '@classes/promised-value';
import { CoreUtils } from '@services/utils/utils'; import { CoreWait } from '@singletons/wait';
import { makeSingleton, NgZone } from '@singletons'; import { makeSingleton, NgZone } from '@singletons';
import { TestingBehatElementLocator, TestingBehatFindOptions } from './behat-runtime'; import { TestingBehatElementLocator, TestingBehatFindOptions } from './behat-runtime';
@ -756,7 +756,7 @@ export class TestingBehatDomUtilsService {
// Pretend we have cut and pasted the new text. // Pretend we have cut and pasted the new text.
if (element.tagName !== 'ION-SELECT' && getValue() !== '') { if (element.tagName !== 'ION-SELECT' && getValue() !== '') {
await CoreUtils.nextTick(); await CoreWait.nextTick();
await setValue(''); await setValue('');
element.dispatchEvent(new InputEvent('input', { element.dispatchEvent(new InputEvent('input', {
@ -768,7 +768,7 @@ export class TestingBehatDomUtilsService {
} }
if (value !== '') { if (value !== '') {
await CoreUtils.nextTick(); await CoreWait.nextTick();
await setValue(value); await setValue(value);
element.dispatchEvent(new InputEvent('input', { element.dispatchEvent(new InputEvent('input', {