Merge pull request #4242 from crazyserver/MOBILE-4138
MOBILE-4138 core: Wait for ready on delegatesmain
commit
bd96f1a2ae
|
@ -16,7 +16,6 @@ import { BehaviorSubject, Subject } from 'rxjs';
|
|||
import { CoreEvents } from '@singletons/events';
|
||||
import { CoreDelegate, CoreDelegateDisplayHandler, CoreDelegateToDisplay } from './delegate';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
|
||||
/**
|
||||
* Superclass to help creating sorted delegates.
|
||||
|
@ -26,7 +25,6 @@ export class CoreSortedDelegate<
|
|||
HandlerType extends CoreDelegateDisplayHandler<DisplayType>>
|
||||
extends CoreDelegate<HandlerType> {
|
||||
|
||||
protected loaded = false;
|
||||
protected sortedHandlersRxJs: Subject<DisplayType[]> = new BehaviorSubject<DisplayType[]>([]);
|
||||
protected sortedHandlers: DisplayType[] = [];
|
||||
|
||||
|
@ -53,14 +51,14 @@ export class CoreSortedDelegate<
|
|||
* @returns True if handlers are loaded, false otherwise.
|
||||
*/
|
||||
areHandlersLoaded(): boolean {
|
||||
return this.loaded;
|
||||
return this.handlersLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current site handlers. Reserved for core use.
|
||||
*/
|
||||
protected clearSortedHandlers(): void {
|
||||
this.loaded = false;
|
||||
this.handlersLoaded = false;
|
||||
this.sortedHandlersRxJs.next([]);
|
||||
this.sortedHandlers = [];
|
||||
}
|
||||
|
@ -74,6 +72,13 @@ export class CoreSortedDelegate<
|
|||
return this.sortedHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
hasHandlers(enabled = false): boolean {
|
||||
return enabled ? !!this.sortedHandlers.length : !!this.handlers.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handlers for the current site.
|
||||
*
|
||||
|
@ -89,27 +94,15 @@ export class CoreSortedDelegate<
|
|||
* @returns Promise resolved with the handlers.
|
||||
*/
|
||||
async getHandlersWhenLoaded(): Promise<DisplayType[]> {
|
||||
if (this.loaded) {
|
||||
return this.sortedHandlers;
|
||||
}
|
||||
await this.waitForReady();
|
||||
|
||||
const promisedHandlers = new CorePromisedValue<DisplayType[]>();
|
||||
const subscription = this.getHandlersObservable().subscribe((handlers) => {
|
||||
if (this.loaded) {
|
||||
subscription?.unsubscribe();
|
||||
|
||||
// Return main handlers.
|
||||
promisedHandlers.resolve(handlers);
|
||||
}
|
||||
});
|
||||
|
||||
return promisedHandlers;
|
||||
return this.sortedHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update handlers Data.
|
||||
*/
|
||||
updateData(): void {
|
||||
protected updateData(): void {
|
||||
const displayData: DisplayType[] = [];
|
||||
|
||||
for (const name in this.enabledHandlers) {
|
||||
|
@ -125,7 +118,7 @@ export class CoreSortedDelegate<
|
|||
// Sort them by priority.
|
||||
displayData.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
||||
|
||||
this.loaded = true;
|
||||
this.handlersLoaded = true;
|
||||
this.sortedHandlersRxJs.next(displayData);
|
||||
this.sortedHandlers = displayData;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import { CoreSites } from '@services/sites';
|
|||
import { CoreEvents } from '@singletons/events';
|
||||
import { CoreSite } from '@classes/sites/site';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { Subject, BehaviorSubject } from 'rxjs';
|
||||
import { CorePromisedValue } from './promised-value';
|
||||
|
||||
/**
|
||||
* Superclass to help creating delegates
|
||||
|
@ -66,21 +68,14 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
protected updatePromises: {[siteId: string]: {[name: string]: Promise<void>}} = {};
|
||||
|
||||
/**
|
||||
* Whether handlers have been initialized.
|
||||
* Subject to subscribe to handlers changes.
|
||||
*/
|
||||
protected handlersInitialized = false;
|
||||
protected handlersUpdated: Subject<void> = new BehaviorSubject<void>(undefined);
|
||||
|
||||
/**
|
||||
* Promise to wait for handlers to be initialized.
|
||||
*
|
||||
* @returns Promise resolved when handlers are enabled.
|
||||
* Handlers loaded flag.
|
||||
*/
|
||||
protected handlersInitPromise: Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Function to resolve the handlers init promise.
|
||||
*/
|
||||
protected handlersInitResolve!: (enabled: boolean) => void;
|
||||
protected handlersLoaded = false;
|
||||
|
||||
/**
|
||||
* Constructor of the Delegate.
|
||||
|
@ -90,10 +85,6 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
constructor(delegateName: string) {
|
||||
this.logger = CoreLogger.getInstance(delegateName);
|
||||
|
||||
this.handlersInitPromise = new Promise((resolve): void => {
|
||||
this.handlersInitResolve = resolve;
|
||||
});
|
||||
|
||||
// Update handlers on this cases.
|
||||
CoreEvents.on(CoreEvents.LOGIN, () => this.updateHandlers());
|
||||
CoreEvents.on(CoreEvents.SITE_UPDATED, () => this.updateHandlers());
|
||||
|
@ -120,6 +111,7 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if handlers are loaded.
|
||||
* Execute a certain function in a enabled handler.
|
||||
* If the handler isn't found or function isn't defined, call the same function in the default handler.
|
||||
*
|
||||
|
@ -216,12 +208,13 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the delegate has at least 1 registered handler (not necessarily enabled).
|
||||
* Returns if the delegate has any handler.
|
||||
*
|
||||
* @returns If there is at least 1 handler.
|
||||
* @param enabled Check only enabled handlers or all.
|
||||
* @returns True if there's any registered handler, false otherwise.
|
||||
*/
|
||||
hasHandlers(): boolean {
|
||||
return Object.keys(this.handlers).length > 0;
|
||||
hasHandlers(enabled = false): boolean {
|
||||
return enabled ? !!this.enabledHandlers.length : !!this.handlers.length;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,13 +317,16 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
*
|
||||
* @returns Resolved when done.
|
||||
*/
|
||||
async updateHandlers(): Promise<void> {
|
||||
protected async updateHandlers(): Promise<void> {
|
||||
this.handlersLoaded = false;
|
||||
|
||||
const enabled = await this.isEnabled();
|
||||
|
||||
if (!enabled) {
|
||||
this.logger.debug('Delegate not enabled.');
|
||||
|
||||
this.handlersInitResolve(false);
|
||||
this.handlersLoaded = true;
|
||||
this.handlersUpdated.next();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -355,10 +351,10 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
|
||||
// Verify that this call is the last one that was started.
|
||||
if (this.isLastUpdateCall(now)) {
|
||||
this.handlersInitialized = true;
|
||||
this.handlersInitResolve(true);
|
||||
|
||||
this.updateData();
|
||||
|
||||
this.handlersLoaded = true;
|
||||
this.handlersUpdated.next();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,10 +362,34 @@ export class CoreDelegate<HandlerType extends CoreDelegateHandler> {
|
|||
* Update handlers Data.
|
||||
* Override this function to update handlers data.
|
||||
*/
|
||||
updateData(): void {
|
||||
protected updateData(): void {
|
||||
// To be overridden.
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits the handlers to be ready.
|
||||
*
|
||||
* @returns Resolved when the handlers are ready.
|
||||
*/
|
||||
async waitForReady(): Promise<void> {
|
||||
if (this.handlersLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = new CorePromisedValue<void>();
|
||||
|
||||
const subscription = this.handlersUpdated.subscribe(() => {
|
||||
if (this.handlersLoaded) {
|
||||
// Resolve.
|
||||
promise.resolve();
|
||||
|
||||
subscription?.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -212,7 +212,7 @@ export class CoreBlockDelegateService extends CoreDelegate<CoreBlockHandler> {
|
|||
* Called when there are new block handlers available. Informs anyone who subscribed to the
|
||||
* observable.
|
||||
*/
|
||||
updateData(): void {
|
||||
protected updateData(): void {
|
||||
this.blocksUpdateObservable.next();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ import {
|
|||
import { CoreCourseAccessDataType } from '../constants';
|
||||
import { Params } from '@angular/router';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { Subject } from 'rxjs/internal/Subject';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CORE_COURSES_MY_COURSES_REFRESHED_EVENT } from '@features/courses/constants';
|
||||
|
||||
|
@ -214,7 +216,8 @@ export interface CoreCourseOptionsMenuHandlerToDisplay {
|
|||
@Injectable( { providedIn: 'root' })
|
||||
export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOptionsHandler> {
|
||||
|
||||
protected loaded: { [courseId: number]: boolean } = {};
|
||||
protected courseHandlersUpdated: { [courseId: number]: Subject<void> } = {};
|
||||
protected courseHandlersLoaded: { [courseId: number]: boolean } = {};
|
||||
protected lastUpdateHandlersForCoursesStart: {
|
||||
[courseId: number]: number;
|
||||
} = {};
|
||||
|
@ -224,7 +227,6 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
access: CoreCourseAccess;
|
||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||
deferred: CorePromisedValue<void>;
|
||||
enabledHandlers: CoreCourseOptionsHandler[];
|
||||
enabledMenuHandlers: CoreCourseOptionsMenuHandler[];
|
||||
};
|
||||
|
@ -247,7 +249,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
* @returns True if handlers are loaded, false otherwise.
|
||||
*/
|
||||
areHandlersLoaded(courseId: number): boolean {
|
||||
return !!this.loaded[courseId];
|
||||
return !!this.courseHandlersLoaded[courseId];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -257,12 +259,12 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
*/
|
||||
protected clearCoursesHandlers(courseId?: number): void {
|
||||
if (courseId) {
|
||||
if (!this.loaded[courseId]) {
|
||||
if (!this.courseHandlersLoaded[courseId]) {
|
||||
// Don't clear if not loaded, it's probably an ongoing load and it could cause JS errors.
|
||||
return;
|
||||
}
|
||||
|
||||
this.loaded[courseId] = false;
|
||||
this.courseHandlersLoaded[courseId] = false;
|
||||
delete this.coursesHandlers[courseId];
|
||||
} else {
|
||||
for (const courseId in this.coursesHandlers) {
|
||||
|
@ -321,7 +323,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
): Promise<void> {
|
||||
|
||||
// If the handlers aren't loaded, do not refresh.
|
||||
if (!this.loaded[courseId]) {
|
||||
if (!this.courseHandlersLoaded[courseId]) {
|
||||
refresh = false;
|
||||
}
|
||||
|
||||
|
@ -331,21 +333,20 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
access: accessData,
|
||||
navOptions,
|
||||
admOptions,
|
||||
deferred: new CorePromisedValue(),
|
||||
enabledHandlers: [],
|
||||
enabledMenuHandlers: [],
|
||||
};
|
||||
this.courseHandlersUpdated[courseId] = new BehaviorSubject<void>(undefined);
|
||||
} else {
|
||||
this.coursesHandlers[courseId].access = accessData;
|
||||
this.coursesHandlers[courseId].navOptions = navOptions;
|
||||
this.coursesHandlers[courseId].admOptions = admOptions;
|
||||
this.coursesHandlers[courseId].deferred = new CorePromisedValue();
|
||||
}
|
||||
|
||||
this.updateHandlersForCourse(courseId, accessData, navOptions, admOptions);
|
||||
}
|
||||
|
||||
await this.coursesHandlers[courseId].deferred;
|
||||
await this.waitCourseHandlersForReady(courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -612,7 +613,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
/**
|
||||
* Update handlers for each course.
|
||||
*/
|
||||
updateData(): void {
|
||||
protected updateData(): void {
|
||||
// Update handlers for all courses.
|
||||
for (const courseId in this.coursesHandlers) {
|
||||
const handler = this.coursesHandlers[courseId];
|
||||
|
@ -635,6 +636,8 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||
): Promise<void> {
|
||||
this.courseHandlersLoaded[courseId] = false;
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
const enabledForCourse: CoreCourseOptionsHandler[] = [];
|
||||
const enabledForCourseMenu: CoreCourseOptionsMenuHandler[] = [];
|
||||
|
@ -675,13 +678,38 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
// Update the coursesHandlers array with the new enabled addons.
|
||||
this.coursesHandlers[courseId].enabledHandlers = enabledForCourse;
|
||||
this.coursesHandlers[courseId].enabledMenuHandlers = enabledForCourseMenu;
|
||||
this.loaded[courseId] = true;
|
||||
|
||||
// Resolve the promise.
|
||||
this.coursesHandlers[courseId].deferred.resolve();
|
||||
// Notify changes.
|
||||
this.courseHandlersLoaded[courseId] = true;
|
||||
this.courseHandlersUpdated[courseId].next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits the course handlers to be ready.
|
||||
*
|
||||
* @param courseId The course ID.
|
||||
* @returns Promise resolved when the handlers are ready.
|
||||
*/
|
||||
async waitCourseHandlersForReady(courseId: number): Promise<void> {
|
||||
if (this.courseHandlersLoaded[courseId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = new CorePromisedValue<void>();
|
||||
|
||||
const subscription = this.courseHandlersUpdated[courseId].subscribe(() => {
|
||||
if (this.courseHandlersLoaded[courseId]) {
|
||||
// Resolve.
|
||||
promise.resolve();
|
||||
|
||||
subscription?.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreCourseOptionsDelegate = makeSingleton(CoreCourseOptionsDelegateService);
|
||||
|
|
|
@ -112,9 +112,9 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
|
|||
skipFilters?: string[],
|
||||
siteId?: string,
|
||||
): Promise<string> {
|
||||
|
||||
// Wait for filters to be initialized.
|
||||
const enabled = await this.handlersInitPromise;
|
||||
await this.waitForReady();
|
||||
const enabled = this.hasHandlers(true);
|
||||
if (!enabled) {
|
||||
// No enabled filters, return the text.
|
||||
return text;
|
||||
|
@ -201,7 +201,8 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
|
|||
): Promise<void> {
|
||||
|
||||
// Wait for filters to be initialized.
|
||||
const enabled = await this.handlersInitPromise;
|
||||
await this.waitForReady();
|
||||
const enabled = this.hasHandlers(true);
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
@ -276,7 +277,8 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
|
|||
*/
|
||||
async shouldBeApplied(filters: CoreFilterFilter[], options: CoreFilterFormatTextOptions, site?: CoreSite): Promise<boolean> {
|
||||
// Wait for filters to be initialized.
|
||||
const enabled = await this.handlersInitPromise;
|
||||
await this.waitForReady();
|
||||
const enabled = this.hasHandlers(true);
|
||||
if (!enabled) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -13,10 +13,8 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreDashboardHomeHandler } from '@features/courses/services/handlers/dashboard-home';
|
||||
import { CoreSiteHomeHomeHandler } from '@features/sitehome/services/handlers/sitehome-home';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreMainMenuHomeDelegate } from '../home-delegate';
|
||||
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../mainmenu-delegate';
|
||||
|
||||
/**
|
||||
|
@ -34,12 +32,9 @@ export class CoreMainMenuHomeHandlerService implements CoreMainMenuHandler {
|
|||
* @inheritdoc
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
await CoreMainMenuHomeDelegate.waitForReady();
|
||||
|
||||
const dashboardEnabled = await CoreDashboardHomeHandler.isEnabledForSite(siteId);
|
||||
const siteHomeEnabled = await CoreSiteHomeHomeHandler.isEnabledForSite(siteId);
|
||||
|
||||
return dashboardEnabled || siteHomeEnabled;
|
||||
return CoreMainMenuHomeDelegate.hasHandlers(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ import { mock, mockSingleton } from '@/testing/utils';
|
|||
import { CoreSite } from '@classes/sites/site';
|
||||
import { CorePluginFileDelegateService, CorePluginFileHandler } from '@services/plugin-file-delegate';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
|
||||
describe('CorePluginFileDelegate', () => {
|
||||
|
@ -32,7 +33,8 @@ describe('CorePluginFileDelegate', () => {
|
|||
pluginFileDelegate = new CorePluginFileDelegateService();
|
||||
pluginFileDelegate.registerHandler(new ModFooRevisionHandler());
|
||||
|
||||
await pluginFileDelegate.updateHandlers();
|
||||
CoreEvents.trigger(CoreEvents.LOGIN, { siteId: '42' }, '42');
|
||||
await pluginFileDelegate.waitForReady();
|
||||
});
|
||||
|
||||
it('removes revision from a URL', () => {
|
||||
|
|
Loading…
Reference in New Issue