From 1c967b7813b69477983f72a295077fb4337cde97 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 4 Dec 2023 09:00:25 +0100 Subject: [PATCH 1/2] MOBILE-4479 core: Make wait directives functions recursive To be able to wait for elements inside the loading elements --- src/core/directives/collapsible-header.ts | 22 +++--- src/core/singletons/directives-registry.ts | 84 +++++++++++++++++++--- 2 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/core/directives/collapsible-header.ts b/src/core/directives/collapsible-header.ts index 1d920f4ee75..704595af480 100644 --- a/src/core/directives/collapsible-header.ts +++ b/src/core/directives/collapsible-header.ts @@ -428,19 +428,15 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest return; } - // Wait loadings to finish. - await CoreDirectivesRegistry.waitDirectivesReady(this.page, 'core-loading', CoreLoadingComponent); - - // Wait tabs to be ready. - await CoreDirectivesRegistry.waitDirectivesReady(this.page, 'core-tabs', CoreTabsComponent); - await CoreDirectivesRegistry.waitDirectivesReady(this.page, 'core-tabs-outlet', CoreTabsOutletComponent); - - // Wait loadings to finish, inside tabs (if any). - await CoreDirectivesRegistry.waitDirectivesReady( - this.page, - 'core-tab core-loading, ion-router-outlet core-loading', - CoreLoadingComponent, - ); + // Make sure elements have been added to the DOM. + await CoreUtils.nextTick(); + + // Wait all loadings and tabs to finish loading. + await CoreDirectivesRegistry.waitMultipleDirectivesReady(this.page, [ + { selector: 'core-loading', class: CoreLoadingComponent }, + { selector: 'core-tabs', class: CoreTabsComponent }, + { selector: 'core-tabs-outlet', class: CoreTabsOutletComponent }, + ]); } /** diff --git a/src/core/singletons/directives-registry.ts b/src/core/singletons/directives-registry.ts index a246ed8226f..63642e12d1a 100644 --- a/src/core/singletons/directives-registry.ts +++ b/src/core/singletons/directives-registry.ts @@ -114,15 +114,16 @@ export class CoreDirectivesRegistry { selector?: string, directiveClass?: DirectiveConstructor, ): Promise { - let elements: Element[] = []; - - if (!selector || element.matches(selector)) { - // Element to wait is myself. - elements = [element]; - } else { - elements = Array.from(element.querySelectorAll(selector)); - } - + const findElements = (): Element[] => { + if (!selector || element.matches(selector)) { + // Element to wait is myself. + return [element]; + } else { + return Array.from(element.querySelectorAll(selector)); + } + }; + + const elements = findElements(); if (!elements.length) { return; } @@ -135,6 +136,63 @@ export class CoreDirectivesRegistry { // Wait for next tick to ensure directives are completely rendered. await CoreUtils.nextTick(); + + // Check if there are new elements now that the found elements are ready (there could be nested elements). + if (elements.length !== findElements().length) { + await this.waitDirectivesReady(element, selector, directiveClass); + } + } + + /** + * Get all directive instances (with multiple types) and wait for them to be ready. + * + * @param element Root element. + * @param directives Directives to wait. + * @returns Promise resolved when done. + */ + static async waitMultipleDirectivesReady( + element: Element, + directives: DirectiveData[], + ): Promise { + const findElements = (selector?: string): Element[] => { + if (!selector || element.matches(selector)) { + // Element to wait is myself. + return [element]; + } else { + return Array.from(element.querySelectorAll(selector)); + } + }; + + let allElements: Element[] = []; + + await Promise.all(directives.map(async directive => { + const elements = findElements(directive.selector); + if (!elements.length) { + return; + } + + allElements = allElements.concat(elements); + + await Promise.all(elements.map(async element => { + const instances = this.resolveAll(element, directive.class); + + await Promise.all(instances.map(instance => instance.ready())); + })); + })); + + // Wait for next tick to ensure directives are completely rendered. + await CoreUtils.nextTick(); + + // Check if there are new elements now that the found elements are ready (there could be nested elements). + const elementsAfterReady = directives.reduce((elements, directive) => { + elements = elements.concat(findElements(directive.selector)); + + return elements; + }, []); + + if (allElements.length !== elementsAfterReady.length) { + await this.waitMultipleDirectivesReady(element, directives); + } } } @@ -144,3 +202,11 @@ export class CoreDirectivesRegistry { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export type DirectiveConstructor = { new(...args: any[]): T }; + +/** + * Data to identify a directive when waiting for ready. + */ +type DirectiveData = { + selector?: string; // If defined, CSS Selector to wait for. + class?: DirectiveConstructor; // Directive class. +}; From fec29ecca12e8d42d030916ea9ad2f062c8c1aaf Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 4 Dec 2023 09:02:13 +0100 Subject: [PATCH 2/2] MOBILE-4479 siteplugins: Only add default module-info if needed --- .../module-index/core-siteplugins-module-index.html | 3 ++- .../siteplugins/components/module-index/module-index.ts | 2 ++ .../siteplugins/components/plugin-content/plugin-content.ts | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html index 7eb7bd09acc..b5cb3a882ad 100644 --- a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html +++ b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html @@ -6,7 +6,8 @@ - + this.updateCachedContent(); - this.onContentLoaded.emit({ refresh: !!refresh, success: true }); + this.onContentLoaded.emit({ refresh: !!refresh, success: true, content: this.content }); } catch (error) { // Make it think it's loaded - otherwise it sticks on 'loading' and stops navigation working. this.content = '
'; - this.onContentLoaded.emit({ refresh: !!refresh, success: false }); + this.onContentLoaded.emit({ refresh: !!refresh, success: false, content: this.content }); CoreDomUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); } finally { @@ -282,4 +282,5 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck { export type CoreSitePluginsPluginContentLoadedData = { refresh: boolean; success: boolean; + content: string; };