commit
4e57e31284
|
@ -711,10 +711,10 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
* @param newCourses New courses.
|
* @param newCourses New courses.
|
||||||
* @return Whether it has meaningful changes.
|
* @return Whether it has meaningful changes.
|
||||||
*/
|
*/
|
||||||
protected coursesHaveMeaningfulChanges(
|
protected async coursesHaveMeaningfulChanges(
|
||||||
previousCourses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
previousCourses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
||||||
newCourses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
newCourses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
||||||
): boolean {
|
): Promise<boolean> {
|
||||||
if (previousCourses.length !== newCourses.length) {
|
if (previousCourses.length !== newCourses.length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -746,10 +746,10 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
* @param newCourses New courses.
|
* @param newCourses New courses.
|
||||||
* @return Whether it has meaningful changes.
|
* @return Whether it has meaningful changes.
|
||||||
*/
|
*/
|
||||||
protected customFilterCoursesHaveMeaningfulChanges(
|
protected async customFilterCoursesHaveMeaningfulChanges(
|
||||||
previousCourses: CoreCourseSummaryData[],
|
previousCourses: CoreCourseSummaryData[],
|
||||||
newCourses: CoreCourseSummaryData[],
|
newCourses: CoreCourseSummaryData[],
|
||||||
): boolean {
|
): Promise<boolean> {
|
||||||
if (previousCourses.length !== newCourses.length) {
|
if (previousCourses.length !== newCourses.length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ export class PageLoadWatcher {
|
||||||
protected ongoingRequests = 0;
|
protected ongoingRequests = 0;
|
||||||
protected components = new Set<AsyncComponent>();
|
protected components = new Set<AsyncComponent>();
|
||||||
protected loadedTimeout?: number;
|
protected loadedTimeout?: number;
|
||||||
|
protected hasChangesPromises: Promise<boolean>[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected loadsManager: PageLoadsManager,
|
protected loadsManager: PageLoadsManager,
|
||||||
|
@ -97,7 +98,7 @@ export class PageLoadWatcher {
|
||||||
*/
|
*/
|
||||||
watchRequest<T>(
|
watchRequest<T>(
|
||||||
observable: WSObservable<T>,
|
observable: WSObservable<T>,
|
||||||
hasMeaningfulChanges?: (previousValue: T, newValue: T) => boolean,
|
hasMeaningfulChanges?: (previousValue: T, newValue: T) => Promise<boolean>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const promisedValue = new CorePromisedValue<T>();
|
const promisedValue = new CorePromisedValue<T>();
|
||||||
let subscription: Subscription | null = null;
|
let subscription: Subscription | null = null;
|
||||||
|
@ -124,9 +125,11 @@ export class PageLoadWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second value, it means data was updated in background. Compare data.
|
// Second value, it means data was updated in background. Compare data.
|
||||||
if (hasMeaningfulChanges?.(firstValue, value)) {
|
if (!hasMeaningfulChanges) {
|
||||||
this.hasChanges = true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.hasChangesPromises.push(CoreUtils.ignoreErrors(hasMeaningfulChanges(firstValue, value), false));
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
promisedValue.reject(error);
|
promisedValue.reject(error);
|
||||||
|
@ -150,8 +153,11 @@ export class PageLoadWatcher {
|
||||||
// It seems load has finished. Wait to make sure no new component has been rendered and started loading.
|
// It seems load has finished. Wait to make sure no new component has been rendered and started loading.
|
||||||
// If a new component or a new request starts the timeout will be cancelled, no need to double check it.
|
// If a new component or a new request starts the timeout will be cancelled, no need to double check it.
|
||||||
clearTimeout(this.loadedTimeout);
|
clearTimeout(this.loadedTimeout);
|
||||||
this.loadedTimeout = window.setTimeout(() => {
|
this.loadedTimeout = window.setTimeout(async () => {
|
||||||
// Loading finished.
|
// Loading finished. Calculate has changes.
|
||||||
|
const values = await Promise.all(this.hasChangesPromises);
|
||||||
|
this.hasChanges = this.hasChanges || values.includes(true);
|
||||||
|
|
||||||
this.loadsManager.onPageLoaded(this);
|
this.loadsManager.onPageLoaded(this);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ export class CoreSite {
|
||||||
protected lastAutoLogin = 0;
|
protected lastAutoLogin = 0;
|
||||||
protected offlineDisabled = false;
|
protected offlineDisabled = false;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
protected ongoingRequests: { [cacheId: string]: WSObservable<any> } = {};
|
protected ongoingRequests: Record<string, Record<OngoingRequestType, WSObservable<any> | undefined>> = {};
|
||||||
protected requestQueue: RequestQueueItem[] = [];
|
protected requestQueue: RequestQueueItem[] = [];
|
||||||
protected requestQueueTimeout: number | null = null;
|
protected requestQueueTimeout: number | null = null;
|
||||||
protected tokenPluginFileWorks?: boolean;
|
protected tokenPluginFileWorks?: boolean;
|
||||||
|
@ -636,9 +636,10 @@ export class CoreSite {
|
||||||
|
|
||||||
const cacheId = this.getCacheId(method, data);
|
const cacheId = this.getCacheId(method, data);
|
||||||
|
|
||||||
// Check for an ongoing identical request if we're not ignoring cache.
|
// Check for an ongoing identical request.
|
||||||
if (preSets.getFromCache && this.ongoingRequests[cacheId] !== undefined) {
|
const ongoingRequest = this.getOngoingRequest<T>(cacheId, preSets);
|
||||||
return this.ongoingRequests[cacheId];
|
if (ongoingRequest) {
|
||||||
|
return ongoingRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
const observable = this.performRequest<T>(method, data, preSets, wsPreSets).pipe(
|
const observable = this.performRequest<T>(method, data, preSets, wsPreSets).pipe(
|
||||||
|
@ -646,18 +647,68 @@ export class CoreSite {
|
||||||
map((data) => CoreUtils.clone(data)),
|
map((data) => CoreUtils.clone(data)),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.ongoingRequests[cacheId] = observable;
|
this.setOngoingRequest(cacheId, preSets, observable);
|
||||||
|
|
||||||
return observable.pipe(
|
return observable.pipe(
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
// Clear the ongoing request unless it has changed (e.g. a new request that ignores cache).
|
this.clearOngoingRequest(cacheId, preSets, observable);
|
||||||
if (this.ongoingRequests[cacheId] === observable) {
|
|
||||||
delete this.ongoingRequests[cacheId];
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an ongoing request if there's one already.
|
||||||
|
*
|
||||||
|
* @param cacheId Cache ID.
|
||||||
|
* @param preSets Presets.
|
||||||
|
* @return Ongoing request if it exists.
|
||||||
|
*/
|
||||||
|
protected getOngoingRequest<T = unknown>(cacheId: string, preSets: CoreSiteWSPreSets): WSObservable<T> | undefined {
|
||||||
|
if (preSets.updateInBackground) {
|
||||||
|
return this.ongoingRequests[cacheId]?.[OngoingRequestType.UPDATE_IN_BACKGROUND];
|
||||||
|
} else if (preSets.getFromCache) { // Only reuse ongoing request when using cache.
|
||||||
|
return this.ongoingRequests[cacheId]?.[OngoingRequestType.STANDARD];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store an ongoing request in memory.
|
||||||
|
*
|
||||||
|
* @param cacheId Cache ID.
|
||||||
|
* @param preSets Presets.
|
||||||
|
* @param request Request to store.
|
||||||
|
*/
|
||||||
|
protected setOngoingRequest<T = unknown>(cacheId: string, preSets: CoreSiteWSPreSets, request: WSObservable<T>): void {
|
||||||
|
this.ongoingRequests[cacheId] = this.ongoingRequests[cacheId] ?? {};
|
||||||
|
|
||||||
|
if (preSets.updateInBackground) {
|
||||||
|
this.ongoingRequests[cacheId][OngoingRequestType.UPDATE_IN_BACKGROUND] = request;
|
||||||
|
} else {
|
||||||
|
this.ongoingRequests[cacheId][OngoingRequestType.STANDARD] = request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the ongoing request unless it has changed (e.g. a new request that ignores cache).
|
||||||
|
*
|
||||||
|
* @param cacheId Cache ID.
|
||||||
|
* @param preSets Presets.
|
||||||
|
* @param request Current request.
|
||||||
|
*/
|
||||||
|
protected clearOngoingRequest<T = unknown>(cacheId: string, preSets: CoreSiteWSPreSets, request: WSObservable<T>): void {
|
||||||
|
this.ongoingRequests[cacheId] = this.ongoingRequests[cacheId] ?? {};
|
||||||
|
|
||||||
|
if (preSets.updateInBackground) {
|
||||||
|
if (this.ongoingRequests[cacheId][OngoingRequestType.UPDATE_IN_BACKGROUND] === request) {
|
||||||
|
delete this.ongoingRequests[cacheId][OngoingRequestType.UPDATE_IN_BACKGROUND];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.ongoingRequests[cacheId][OngoingRequestType.STANDARD] === request) {
|
||||||
|
delete this.ongoingRequests[cacheId][OngoingRequestType.STANDARD];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a request, getting the response either from cache or WebService.
|
* Perform a request, getting the response either from cache or WebService.
|
||||||
*
|
*
|
||||||
|
@ -1640,8 +1691,11 @@ export class CoreSite {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an ongoing identical request if we're not ignoring cache.
|
// Check for an ongoing identical request if we're not ignoring cache.
|
||||||
if (cachePreSets.getFromCache && this.ongoingRequests[cacheId] !== undefined) {
|
|
||||||
return await firstValueFrom(this.ongoingRequests[cacheId]);
|
// Check for an ongoing identical request.
|
||||||
|
const ongoingRequest = this.getOngoingRequest<CoreSitePublicConfigResponse>(cacheId, cachePreSets);
|
||||||
|
if (ongoingRequest) {
|
||||||
|
return firstValueFrom(ongoingRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
const subject = new Subject<CoreSitePublicConfigResponse>();
|
const subject = new Subject<CoreSitePublicConfigResponse>();
|
||||||
|
@ -1649,14 +1703,11 @@ export class CoreSite {
|
||||||
// Return a clone of the original object, this may prevent errors if in the callback the object is modified.
|
// Return a clone of the original object, this may prevent errors if in the callback the object is modified.
|
||||||
map((data) => CoreUtils.clone(data)),
|
map((data) => CoreUtils.clone(data)),
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
// Clear the ongoing request unless it has changed (e.g. a new request that ignores cache).
|
this.clearOngoingRequest(cacheId, cachePreSets, observable);
|
||||||
if (this.ongoingRequests[cacheId] === observable) {
|
|
||||||
delete this.ongoingRequests[cacheId];
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.ongoingRequests[cacheId] = observable;
|
this.setOngoingRequest(cacheId, cachePreSets, observable);
|
||||||
|
|
||||||
this.getFromCache<CoreSitePublicConfigResponse>(method, {}, cachePreSets, false)
|
this.getFromCache<CoreSitePublicConfigResponse>(method, {}, cachePreSets, false)
|
||||||
.then(cachedData => cachedData.response)
|
.then(cachedData => cachedData.response)
|
||||||
|
@ -1916,16 +1967,16 @@ export class CoreSite {
|
||||||
// Return the requested setting.
|
// Return the requested setting.
|
||||||
for (const x in config.settings) {
|
for (const x in config.settings) {
|
||||||
if (config.settings[x].name == name) {
|
if (config.settings[x].name == name) {
|
||||||
return config.settings[x].value;
|
return String(config.settings[x].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CoreError('Site config not found: ' + name);
|
throw new CoreError('Site config not found: ' + name);
|
||||||
} else {
|
} else {
|
||||||
// Return all settings in the same array.
|
// Return all settings in the same array.
|
||||||
const settings = {};
|
const settings: CoreSiteConfig = {};
|
||||||
config.settings.forEach((setting) => {
|
config.settings.forEach((setting) => {
|
||||||
settings[setting.name] = setting.value;
|
settings[setting.name] = String(setting.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
|
@ -2686,7 +2737,7 @@ export enum CoreSiteInfoUserHomepage {
|
||||||
export type CoreSiteConfigResponse = {
|
export type CoreSiteConfigResponse = {
|
||||||
settings: { // Settings.
|
settings: { // Settings.
|
||||||
name: string; // The name of the setting.
|
name: string; // The name of the setting.
|
||||||
value: string; // The value of the setting.
|
value: string | number; // The value of the setting.
|
||||||
}[];
|
}[];
|
||||||
warnings?: CoreWSExternalWarning[];
|
warnings?: CoreWSExternalWarning[];
|
||||||
};
|
};
|
||||||
|
@ -2804,3 +2855,11 @@ type WSCachedError = {
|
||||||
* Otherwise, it will only return 1 value, either coming from cache or from the server. After this, it will complete.
|
* Otherwise, it will only return 1 value, either coming from cache or from the server. After this, it will complete.
|
||||||
*/
|
*/
|
||||||
export type WSObservable<T> = Observable<T>;
|
export type WSObservable<T> = Observable<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of ongoing requests stored in memory to avoid duplicating them.
|
||||||
|
*/
|
||||||
|
enum OngoingRequestType {
|
||||||
|
STANDARD = 0,
|
||||||
|
UPDATE_IN_BACKGROUND = 1,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue