From 7adf75f4901368a4c96582685679a67aa75f176d Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Wed, 7 Feb 2024 14:25:52 +0100 Subject: [PATCH] MOBILE-4304 core: Improve infinite-loader race conditions --- .../infinite-loading/infinite-loading.ts | 12 +++++------ src/core/services/utils/utils.ts | 20 ++++++++++++++++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/core/components/infinite-loading/infinite-loading.ts b/src/core/components/infinite-loading/infinite-loading.ts index 87e2125b7..d1ed55b0d 100644 --- a/src/core/components/infinite-loading/infinite-loading.ts +++ b/src/core/components/infinite-loading/infinite-loading.ts @@ -69,17 +69,17 @@ export class CoreInfiniteLoadingComponent implements OnChanges { return; } - // Wait until next tick to allow items to render and scroll content to grow. - await CoreUtils.nextTick(); + const scrollElement = await this.hostElement.closest('ion-content')?.getScrollElement(); - // Calculate distance from edge. - const content = this.hostElement.closest('ion-content'); - if (!content) { + if (!scrollElement) { return; } - const scrollElement = await content.getScrollElement(); + // Wait to allow items to render and scroll content to grow. + await CoreUtils.nextTick(); + await CoreUtils.waitFor(() => scrollElement.scrollHeight > scrollElement.clientHeight, { timeout: 1000 }); + // Calculate distance from edge. const infiniteHeight = this.hostElement.getBoundingClientRect().height; const scrollTop = scrollElement.scrollTop; const height = scrollElement.offsetHeight; diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts index 7b806af80..312dc74d3 100644 --- a/src/core/services/utils/utils.ts +++ b/src/core/services/utils/utils.ts @@ -1826,23 +1826,29 @@ export class CoreUtilsProvider { * @param condition Condition. * @returns Cancellable promise. */ - waitFor(condition: () => boolean, interval: number = 50): CoreCancellablePromise { + waitFor(condition: () => boolean): CoreCancellablePromise; + waitFor(condition: () => boolean, options: CoreUtilsWaitOptions): CoreCancellablePromise; + waitFor(condition: () => boolean, interval: number): CoreCancellablePromise; + waitFor(condition: () => boolean, optionsOrInterval: CoreUtilsWaitOptions | number = {}): CoreCancellablePromise { + const options = typeof optionsOrInterval === 'number' ? { interval: optionsOrInterval } : optionsOrInterval; + if (condition()) { return CoreCancellablePromise.resolve(); } + const startTime = Date.now(); let intervalId: number | undefined; return new CoreCancellablePromise( async (resolve) => { intervalId = window.setInterval(() => { - if (!condition()) { + if (!condition() && (!options.timeout || (Date.now() - startTime < options.timeout))) { return; } resolve(); window.clearInterval(intervalId); - }, interval); + }, options.interval ?? 50); }, () => window.clearInterval(intervalId), ); @@ -1939,6 +1945,14 @@ export type CoreUtilsOpenInAppOptions = InAppBrowserOptions & { originalUrl?: string; // Original URL to open (in case the URL was treated, e.g. to add a token or an auto-login). }; +/** + * Options for waiting. + */ +export type CoreUtilsWaitOptions = { + interval?: number; + timeout?: number; +}; + /** * Possible default picker actions. */