From 979e9951666e202f0830c075ef0fd1f0463967f7 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 25 Jul 2022 10:39:59 +0200 Subject: [PATCH] MOBILE-3817 filter: Apply update in background to filters --- src/core/classes/site.ts | 2 +- src/core/features/course/services/course.ts | 184 +++++++++++------- .../features/filter/services/filter-helper.ts | 16 +- src/core/features/filter/services/filter.ts | 8 +- src/core/services/sites.ts | 4 +- 5 files changed, 138 insertions(+), 76 deletions(-) diff --git a/src/core/classes/site.ts b/src/core/classes/site.ts index ec047eb8a..dfbeef584 100644 --- a/src/core/classes/site.ts +++ b/src/core/classes/site.ts @@ -2409,7 +2409,7 @@ export function chainRequests>( return source.subscribe({ next: async (value) => { - if (readingStrategy !== CoreSitesReadingStrategy.UPDATE_IN_BACKGROUND) { + if (readingStrategy !== CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE) { // Just use same strategy. subscriber.next({ data: value, readingStrategy }); diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index f5a283831..d382d49be 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -54,6 +54,9 @@ import { CoreDatabaseCachingStrategy } from '@classes/database/database-table-pr import { SQLiteDB } from '@classes/sqlitedb'; import { CorePlatform } from '@services/platform'; import { CoreTime } from '@singletons/time'; +import { Observable } from 'rxjs'; +import { asyncObservable, firstValueFrom } from '@/core/utils/rxjs'; +import { map } from 'rxjs/operators'; const ROOT_CACHE_KEY = 'mmCourse:'; @@ -402,19 +405,36 @@ export class CoreCourseProvider { * @return Promise resolved with the list of blocks. * @since 3.7 */ - async getCourseBlocks(courseId: number, siteId?: string): Promise { - const site = await CoreSites.getSite(siteId); - const params: CoreBlockGetCourseBlocksWSParams = { - courseid: courseId, - returncontents: true, - }; - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getCourseBlocksCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY, - }; - const result = await site.read('core_block_get_course_blocks', params, preSets); + getCourseBlocks(courseId: number, siteId?: string): Promise { + return firstValueFrom(this.getCourseBlocksObservable(courseId, { siteId })); + } - return result.blocks || []; + /** + * Get course blocks. + * + * @param courseId Course ID. + * @param options Options. + * @return Observable that returns the blocks. + * @since 3.7 + */ + getCourseBlocksObservable(courseId: number, options: CoreSitesCommonWSOptions = {}): Observable { + return asyncObservable(async () => { + const site = await CoreSites.getSite(options.siteId); + + const params: CoreBlockGetCourseBlocksWSParams = { + courseid: courseId, + returncontents: true, + }; + const preSets: CoreSiteWSPreSets = { + cacheKey: this.getCourseBlocksCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), + }; + + return site.readObservable('core_block_get_course_blocks', params, preSets).pipe( + map(result => result.blocks), + ); + }); } /** @@ -908,7 +928,7 @@ export class CoreCourseProvider { * @param includeStealthModules Whether to include stealth modules. Defaults to true. * @return The reject contains the error message, else contains the sections. */ - async getSections( + getSections( courseId: number, excludeModules: boolean = false, excludeContents: boolean = false, @@ -916,63 +936,83 @@ export class CoreCourseProvider { siteId?: string, includeStealthModules: boolean = true, ): Promise { - - const site = await CoreSites.getSite(siteId); - preSets = preSets || {}; - preSets.cacheKey = this.getSectionsCacheKey(courseId); - preSets.updateFrequency = preSets.updateFrequency || CoreSite.FREQUENCY_RARELY; - - const params: CoreCourseGetContentsParams = { - courseid: courseId, - }; - params.options = [ - { - name: 'excludemodules', - value: excludeModules, - }, - { - name: 'excludecontents', - value: excludeContents, - }, - ]; - - if (this.canRequestStealthModules(site)) { - params.options.push({ - name: 'includestealthmodules', - value: includeStealthModules, - }); - } - - let sections: CoreCourseGetContentsWSSection[]; - try { - sections = await site.read('core_course_get_contents', params, preSets); - } catch { - // Error getting the data, it could fail because we added a new parameter and the call isn't cached. - // Retry without the new parameter and forcing cache. - preSets.omitExpires = true; - params.options.splice(-1, 1); - sections = await site.read('core_course_get_contents', params, preSets); - } - - const siteHomeId = site.getSiteHomeId(); - let showSections = true; - if (courseId == siteHomeId) { - const storedNumSections = site.getStoredConfig('numsections'); - showSections = storedNumSections !== undefined && !!storedNumSections; - } - - if (showSections !== undefined && !showSections && sections.length > 0) { - // Get only the last section (Main menu block section). - sections.pop(); - } - - // Add course to all modules. - return sections.map((section) => ({ - ...section, - modules: section.modules.map((module) => this.addAdditionalModuleData(module, courseId, section.id)), + return firstValueFrom(this.getSectionsObservable(courseId, { + excludeModules, + excludeContents, + includeStealthModules, + preSets, + siteId, })); } + /** + * Get the course sections. + * + * @param courseId The course ID. + * @param options Options. + * @return Observable that returns the sections. + */ + getSectionsObservable( + courseId: number, + options: CoreCourseGetSectionsOptions = {}, + ): Observable { + options.includeStealthModules = options.includeStealthModules ?? true; + + return asyncObservable(async () => { + const site = await CoreSites.getSite(options.siteId); + + const preSets: CoreSiteWSPreSets = { + ...options.preSets, + cacheKey: this.getSectionsCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), + }; + + const params: CoreCourseGetContentsParams = { + courseid: courseId, + }; + params.options = [ + { + name: 'excludemodules', + value: !!options.excludeModules, + }, + { + name: 'excludecontents', + value: !!options.excludeContents, + }, + ]; + + if (this.canRequestStealthModules(site)) { + params.options.push({ + name: 'includestealthmodules', + value: !!options.includeStealthModules, + }); + } + + return site.readObservable('core_course_get_contents', params, preSets).pipe( + map(sections => { + const siteHomeId = site.getSiteHomeId(); + let showSections = true; + if (courseId == siteHomeId) { + const storedNumSections = site.getStoredConfig('numsections'); + showSections = storedNumSections !== undefined && !!storedNumSections; + } + + if (showSections !== undefined && !showSections && sections.length > 0) { + // Get only the last section (Main menu block section). + sections.pop(); + } + + // Add course to all modules. + return sections.map((section) => ({ + ...section, + modules: section.modules.map((module) => this.addAdditionalModuleData(module, courseId, section.id)), + })); + }), + ); + }); + } + /** * Get cache key for section WS call. * @@ -1933,3 +1973,13 @@ export type CoreCourseStoreModuleViewedOptions = { timeaccess?: number; siteId?: string; }; + +/** + * Options for getSections. + */ +export type CoreCourseGetSectionsOptions = CoreSitesCommonWSOptions & { + excludeModules?: boolean; + excludeContents?: boolean; + includeStealthModules?: boolean; // Defaults to true. + preSets?: CoreSiteWSPreSets; +}; diff --git a/src/core/features/filter/services/filter-helper.ts b/src/core/features/filter/services/filter-helper.ts index 6467f1b67..ea3fbfce4 100644 --- a/src/core/features/filter/services/filter-helper.ts +++ b/src/core/features/filter/services/filter-helper.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreNetwork } from '@services/network'; -import { CoreSites } from '@services/sites'; +import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreFilterDelegate } from './filter-delegate'; import { CoreFilter, @@ -31,6 +31,7 @@ import { CoreEvents, CoreEventSiteData } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; import { CoreSite } from '@classes/site'; import { CoreCourseHelper } from '@features/course/services/course-helper'; +import { firstValueFrom } from '@/core/utils/rxjs'; /** * Helper service to provide filter functionalities. @@ -75,7 +76,11 @@ export class CoreFilterHelperProvider { * @return Promise resolved with the contexts. */ async getBlocksContexts(courseId: number, siteId?: string): Promise { - const blocks = await CoreCourse.getCourseBlocks(courseId, siteId); + // Use stale while revalidate, but always use the first value. If data is updated it will be stored in DB. + const blocks = await firstValueFrom(CoreCourse.getCourseBlocksObservable(courseId, { + readingStrategy: CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE, + siteId, + })); const contexts: CoreFiltersGetAvailableInContextWSParamContext[] = []; @@ -153,7 +158,12 @@ export class CoreFilterHelperProvider { * @return Promise resolved with the contexts. */ async getCourseModulesContexts(courseId: number, siteId?: string): Promise { - const sections = await CoreCourse.getSections(courseId, false, true, undefined, siteId); + // Use stale while revalidate, but always use the first value. If data is updated it will be stored in DB. + const sections = await firstValueFrom(CoreCourse.getSectionsObservable(courseId, { + excludeContents: true, + readingStrategy: CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE, + siteId, + })); const contexts: CoreFiltersGetAvailableInContextWSParamContext[] = []; diff --git a/src/core/features/filter/services/filter.ts b/src/core/features/filter/services/filter.ts index 321748b32..a1735684f 100644 --- a/src/core/features/filter/services/filter.ts +++ b/src/core/features/filter/services/filter.ts @@ -15,8 +15,8 @@ import { Injectable } from '@angular/core'; import { CoreNetwork } from '@services/network'; -import { CoreSites } from '@services/sites'; -import { CoreSite } from '@classes/site'; +import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; +import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { CoreWSExternalWarning } from '@services/ws'; import { CoreTextUtils } from '@services/utils/text'; import { CoreFilterDelegate } from './filter-delegate'; @@ -284,13 +284,15 @@ export class CoreFilterProvider { const data: CoreFiltersGetAvailableInContextWSParams = { contexts: contextsToSend, }; - const preSets = { + const preSets: CoreSiteWSPreSets = { cacheKey: this.getAvailableInContextsCacheKey(contextsToSend), updateFrequency: CoreSite.FREQUENCY_RARELY, splitRequest: { param: 'contexts', maxLength: 300, }, + // Use stale while revalidate, but always use the first value. If data is updated it will be stored in DB. + ...CoreSites.getReadingStrategyPreSets(CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE), }; const result = await site.read( diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index c16638ac3..7217ef250 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -1796,7 +1796,7 @@ export class CoreSitesProvider { getFromCache: false, emergencyCache: false, }; - case CoreSitesReadingStrategy.UPDATE_IN_BACKGROUND: + case CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE: return { updateInBackground: true, getFromCache: true, @@ -2023,7 +2023,7 @@ export const enum CoreSitesReadingStrategy { PREFER_CACHE, ONLY_NETWORK, PREFER_NETWORK, - UPDATE_IN_BACKGROUND, + STALE_WHILE_REVALIDATE, } /**