diff --git a/src/core/course/classes/main-activity-component.ts b/src/core/course/classes/main-activity-component.ts index f1fe1bf30..d0d8489f0 100644 --- a/src/core/course/classes/main-activity-component.ts +++ b/src/core/course/classes/main-activity-component.ts @@ -84,25 +84,6 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR } } - /** - * Refresh the data. - * - * @param {any} [refresher] Refresher. - * @param {Function} [done] Function to call when done. - * @param {boolean} [showErrors=false] If show errors to the user of hide them. - * @return {Promise} Promise resolved when done. - */ - doRefresh(refresher?: any, done?: () => void, showErrors: boolean = false): Promise { - if (this.loaded) { - return this.refreshContent(true, showErrors).finally(() => { - refresher && refresher.complete(); - done && done(); - }); - } - - return Promise.resolve(); - } - /** * Compares sync event data with current data to check if refresh content is needed. * diff --git a/src/core/course/classes/main-resource-component.ts b/src/core/course/classes/main-resource-component.ts index 2023b1175..5d753824f 100644 --- a/src/core/course/classes/main-resource-component.ts +++ b/src/core/course/classes/main-resource-component.ts @@ -17,7 +17,8 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreCourseHelperProvider } from '@core/course/providers/helper'; -import { CoreCourseModuleMainComponent } from '@core/course/providers/module-delegate'; +import { CoreCourseModuleMainComponent, CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; +import { CoreCourseSectionPage } from '@core/course/pages/section/section.ts'; /** * Template class to easily create CoreCourseModuleMainComponent of resources (or activities without syncing). @@ -50,12 +51,16 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, protected courseHelper: CoreCourseHelperProvider; protected translate: TranslateService; protected domUtils: CoreDomUtilsProvider; + protected moduleDelegate: CoreCourseModuleDelegate; + protected courseSectionPage: CoreCourseSectionPage; constructor(injector: Injector) { this.textUtils = injector.get(CoreTextUtilsProvider); this.courseHelper = injector.get(CoreCourseHelperProvider); this.translate = injector.get(TranslateService); this.domUtils = injector.get(CoreDomUtilsProvider); + this.moduleDelegate = injector.get(CoreCourseModuleDelegate); + this.courseSectionPage = injector.get(CoreCourseSectionPage, null); this.dataRetrieved = new EventEmitter(); } @@ -73,15 +78,27 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, /** * Refresh the data. * - * @param {any} [refresher] Refresher. - * @param {Function} [done] Function to call when done. + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @param {boolean} [showErrors=false] If show errors to the user of hide them. * @return {Promise} Promise resolved when done. */ - doRefresh(refresher?: any, done?: () => void): Promise { + doRefresh(refresher?: any, done?: () => void, showErrors: boolean = false): Promise { if (this.loaded) { - return this.refreshContent().finally(() => { - refresher && refresher.complete(); - done && done(); + /* If it's a single activity course and the refresher is displayed within the component, + call doRefresh on the section page to refresh the course data. */ + let promise; + if (this.courseSectionPage && !this.moduleDelegate.displayRefresherInSingleActivity(this.module.modname)) { + promise = this.courseSectionPage.doRefresh(); + } else { + promise = Promise.resolve(); + } + + return promise.finally(() => { + return this.refreshContent(true, showErrors).finally(() => { + refresher && refresher.complete(); + done && done(); + }); }); } @@ -91,9 +108,11 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, /** * Perform the refresh content function. * + * @param {boolean} [sync=false] If the refresh needs syncing. + * @param {boolean} [showErrors=false] Wether to show errors to the user or hide them. * @return {Promise} Resolved when done. */ - protected refreshContent(): Promise { + protected refreshContent(sync: boolean = false, showErrors: boolean = false): Promise { this.refreshIcon = 'spinner'; return this.invalidateContent().catch(() => { diff --git a/src/core/course/formats/singleactivity/providers/handler.ts b/src/core/course/formats/singleactivity/providers/handler.ts index 67090c9ee..31a967606 100644 --- a/src/core/course/formats/singleactivity/providers/handler.ts +++ b/src/core/course/formats/singleactivity/providers/handler.ts @@ -14,6 +14,7 @@ import { Injectable, Injector } from '@angular/core'; import { CoreCourseFormatHandler } from '../../../providers/format-delegate'; +import { CoreCourseModuleDelegate } from '../../../providers/module-delegate'; import { CoreCourseFormatSingleActivityComponent } from '../components/singleactivity'; /** @@ -24,7 +25,7 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa name = 'CoreCourseFormatSingleActivity'; format = 'singleactivity'; - constructor() { + constructor(private moduleDelegate: CoreCourseModuleDelegate) { // Nothing to do. } @@ -83,6 +84,22 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa return false; } + /** + * Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format, + * and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true. + * + * @param {any} course The course to check. + * @param {any[]} sections List of course sections. + * @return {boolean} Whether the refresher should be displayed. + */ + displayRefresher(course: any, sections: any[]): boolean { + if (sections && sections[0] && sections[0].modules) { + return this.moduleDelegate.displayRefresherInSingleActivity(sections[0].modules[0].modname); + } else { + return true; + } + } + /** * Return the Component to use to display the course format instead of using the default one. * Use it if you want to display a format completely different from the default one. diff --git a/src/core/course/pages/section/section.html b/src/core/course/pages/section/section.html index cb9f66456..16a95b37b 100644 --- a/src/core/course/pages/section/section.html +++ b/src/core/course/pages/section/section.html @@ -17,7 +17,7 @@ - + diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index 3c5f55f39..2d31e9a50 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -54,6 +54,7 @@ export class CoreCourseSectionPage implements OnDestroy { }; moduleId: number; displayEnableDownload: boolean; + displayRefresher: boolean; protected module: any; protected completionObserver; @@ -188,6 +189,9 @@ export class CoreCourseSectionPage implements OnDestroy { // Get the title again now that we have sections. this.title = this.courseFormatDelegate.getCourseTitle(this.course, this.sections); + + // Get whether to show the refresher now that we have sections. + this.displayRefresher = this.courseFormatDelegate.displayRefresher(this.course, this.sections); }); })); @@ -212,13 +216,23 @@ export class CoreCourseSectionPage implements OnDestroy { /** * Refresh the data. * - * @param {any} refresher Refresher. + * @param {any} [refresher] Refresher. + * @return {Promise} Promise resolved when done. */ - doRefresh(refresher: any): void { - this.invalidateData().finally(() => { - this.loadData(true).finally(() => { - this.formatComponent.doRefresh(refresher).finally(() => { - refresher.complete(); + doRefresh(refresher?: any): Promise { + return this.invalidateData().finally(() => { + return this.loadData(true).finally(() => { + /* Do not call doRefresh on the format component if the refresher is defined in the format component + to prevent an inifinite loop. */ + let promise; + if (this.displayRefresher) { + promise = this.formatComponent.doRefresh(refresher); + } else { + promise = Promise.resolve(); + } + + return promise.finally(() => { + refresher && refresher.complete(); }); }); }); diff --git a/src/core/course/providers/default-format.ts b/src/core/course/providers/default-format.ts index 7c9ad4648..10919e155 100644 --- a/src/core/course/providers/default-format.ts +++ b/src/core/course/providers/default-format.ts @@ -77,6 +77,18 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler { return true; } + /** + * Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format, + * and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true. + * + * @param {any} course The course to check. + * @param {any[]} sections List of course sections. + * @return {boolean} Whether the refresher should be displayed. + */ + displayRefresher?(course: any, sections: any[]): boolean { + return true; + } + /** * Given a list of sections, get the "current" section that should be displayed first. * diff --git a/src/core/course/providers/default-module.ts b/src/core/course/providers/default-module.ts index 65b52a9df..04596a46e 100644 --- a/src/core/course/providers/default-module.ts +++ b/src/core/course/providers/default-module.ts @@ -89,4 +89,14 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { // We can't inject CoreCourseUnsupportedModuleComponent here due to circular dependencies. // Don't return anything, by default it will use CoreCourseUnsupportedModuleComponent. } + + /** + * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be + * included in the template that calls the doRefresh method of the component. Defaults to true. + * + * @return {boolean} Whether the refresher should be displayed. + */ + displayRefresherInSingleActivity(): boolean { + return true; + } } diff --git a/src/core/course/providers/format-delegate.ts b/src/core/course/providers/format-delegate.ts index e79841ace..218a9bfdf 100644 --- a/src/core/course/providers/format-delegate.ts +++ b/src/core/course/providers/format-delegate.ts @@ -65,6 +65,16 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler { */ displaySectionSelector?(course: any): boolean; + /** + * Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format, + * and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true. + * + * @param {any} course The course to check. + * @param {any[]} sections List of course sections. + * @type {boolean} Whether the refresher should be displayed. + */ + displayRefresher?(course: any, sections: any[]): boolean; + /** * Given a list of sections, get the "current" section that should be displayed first. Defaults to first section. * @@ -183,6 +193,18 @@ export class CoreCourseFormatDelegate extends CoreDelegate { return this.executeFunctionOnEnabled(course.format, 'displayEnableDownload', [course]); } + /** + * Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format, + * and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true. + * + * @param {any} course The course to check. + * @param {any[]} sections List of course sections. + * @return {boolean} Whether the refresher should be displayed. + */ + displayRefresher(course: any, sections: any[]): boolean { + return this.executeFunctionOnEnabled(course.format, 'displayRefresher', [course, sections]); + } + /** * Whether the default section selector should be displayed. Defaults to true. * diff --git a/src/core/course/providers/module-delegate.ts b/src/core/course/providers/module-delegate.ts index 530fd0ec1..b4981020e 100644 --- a/src/core/course/providers/module-delegate.ts +++ b/src/core/course/providers/module-delegate.ts @@ -53,6 +53,14 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler { * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ getMainComponent(injector: Injector, course: any, module: any): any | Promise; + + /** + * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be + * included in the template that calls the doRefresh method of the component. Defaults to true. + * + * @return {boolean} Whether the refresher should be displayed. + */ + displayRefresherInSingleActivity?(): boolean; } /** @@ -247,4 +255,15 @@ export class CoreCourseModuleDelegate extends CoreDelegate { return false; } + + /** + * Whether to display the course refresher in single activity course format. If it returns false, a refresher must be + * included in the template that calls the doRefresh method of the component. Defaults to true. + * + * @param {any} modname The name of the module type. + * @return {boolean} Whether the refresher should be displayed. + */ + displayRefresherInSingleActivity(modname: string): boolean { + return this.executeFunctionOnEnabled(modname, 'displayRefresherInSingleActivity'); + } }