From aa95a20287938b933df2c64d60c53459bd8fb2cb Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 2 Feb 2018 08:57:34 +0100 Subject: [PATCH] MOBILE-2335 book: Support single activity format with book --- src/addon/mod/book/components/index/index.ts | 3 ++- .../mod/book/providers/module-handler.ts | 4 +++- .../mod/label/providers/module-handler.ts | 1 + src/classes/delegate.ts | 2 +- .../dynamic-component/dynamic-component.ts | 13 ++++++++++ src/core/course/components/format/format.ts | 24 ++++++++++++++++++- .../components/singleactivity.ts | 16 ++++++++++++- src/core/course/pages/section/section.ts | 10 +++++--- src/core/course/providers/module-delegate.ts | 15 ++++++++++++ 9 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/addon/mod/book/components/index/index.ts b/src/addon/mod/book/components/index/index.ts index f79093719..64b9ea7a0 100644 --- a/src/addon/mod/book/components/index/index.ts +++ b/src/addon/mod/book/components/index/index.ts @@ -20,6 +20,7 @@ import { CoreDomUtilsProvider } from '../../../../../providers/utils/dom'; import { CoreTextUtilsProvider } from '../../../../../providers/utils/text'; import { CoreCourseProvider } from '../../../../../core/course/providers/course'; import { CoreCourseHelperProvider } from '../../../../../core/course/providers/helper'; +import { CoreCourseModuleMainComponent } from '../../../../../core/course/providers/module-delegate'; import { AddonModBookProvider, AddonModBookContentsMap, AddonModBookTocChapter } from '../../providers/book'; import { AddonModBookPrefetchHandler } from '../../providers/prefetch-handler'; import { AddonModBookTocPopoverComponent } from '../../components/toc-popover/toc-popover'; @@ -31,7 +32,7 @@ import { AddonModBookTocPopoverComponent } from '../../components/toc-popover/to selector: 'addon-mod-book-index', templateUrl: 'index.html', }) -export class AddonModBookIndexComponent implements OnInit { +export class AddonModBookIndexComponent implements OnInit, CoreCourseModuleMainComponent { @Input() module: any; // The module of the book. @Input() courseId: number; // Course ID the book belongs to. @Output() bookRetrieved?: EventEmitter; diff --git a/src/addon/mod/book/providers/module-handler.ts b/src/addon/mod/book/providers/module-handler.ts index 495bdbbd3..7fc1a41ac 100644 --- a/src/addon/mod/book/providers/module-handler.ts +++ b/src/addon/mod/book/providers/module-handler.ts @@ -15,6 +15,7 @@ import { Injectable } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; import { AddonModBookProvider } from './book'; +import { AddonModBookIndexComponent } from '../components/index/index'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../../../../core/course/providers/module-delegate'; import { CoreCourseProvider } from '../../../../core/course/providers/course'; @@ -57,12 +58,13 @@ export class AddonModBookModuleHandler implements CoreCourseModuleHandler { /** * Get the component to render the module. This is needed to support singleactivity course format. + * The component returned must implement CoreCourseModuleMainComponent. * * @param {any} course The course object. * @param {any} module The module object. * @return {any} The component to use, undefined if not found. */ getMainComponent(course: any, module: any): any { - // @todo + return AddonModBookIndexComponent; } } diff --git a/src/addon/mod/label/providers/module-handler.ts b/src/addon/mod/label/providers/module-handler.ts index a32b9474a..5b76b9818 100644 --- a/src/addon/mod/label/providers/module-handler.ts +++ b/src/addon/mod/label/providers/module-handler.ts @@ -57,6 +57,7 @@ export class AddonModLabelModuleHandler implements CoreCourseModuleHandler { /** * Get the component to render the module. This is needed to support singleactivity course format. + * The component returned must implement CoreCourseModuleMainComponent. * * @param {any} course The course object. * @param {any} module The module object. diff --git a/src/classes/delegate.ts b/src/classes/delegate.ts index 187e9d615..b26d26a32 100644 --- a/src/classes/delegate.ts +++ b/src/classes/delegate.ts @@ -134,7 +134,7 @@ export class CoreDelegate { if (handler && handler[fnName]) { return handler[fnName].apply(handler, params); } else if (this.defaultHandler && this.defaultHandler[fnName]) { - return this.defaultHandler[fnName].apply(this, params); + return this.defaultHandler[fnName].apply(this.defaultHandler, params); } } diff --git a/src/components/dynamic-component/dynamic-component.ts b/src/components/dynamic-component/dynamic-component.ts index 01fa9532d..2c8e5898b 100644 --- a/src/components/dynamic-component/dynamic-component.ts +++ b/src/components/dynamic-component/dynamic-component.ts @@ -99,6 +99,19 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { } } + /** + * Call a certain function on the component. + * + * @param {string} name Name of the function to call. + * @param {any[]} params List of params to send to the function. + * @return {any} Result of the call. Undefined if no component instance or the function doesn't exist. + */ + callComponentFunction(name: string, params: any[]): any { + if (this.instance && typeof this.instance[name] == 'function') { + return this.instance[name].apply(this.instance, params); + } + } + /** * Create a component, add it to a container and set the input data. * diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index 999c78ad1..ceb8780ca 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter } from '@angular/core'; +import { + Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter, ViewChildren, QueryList +} from '@angular/core'; import { Content } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '../../../../providers/events'; @@ -22,6 +24,7 @@ import { CoreCourseProvider } from '../../../course/providers/course'; import { CoreCourseHelperProvider } from '../../../course/providers/helper'; import { CoreCourseFormatDelegate } from '../../../course/providers/format-delegate'; import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate'; +import { CoreDynamicComponent } from '../../../../components/dynamic-component/dynamic-component'; /** * Component to display course contents using a certain format. If the format isn't found, use default one. @@ -46,6 +49,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { @Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section. @Output() completionChanged?: EventEmitter; // Will emit an event when any module completion changes. + @ViewChildren(CoreDynamicComponent) dynamicComponents: QueryList; + // All the possible component classes. courseFormatComponent: any; courseSummaryComponent: any; @@ -286,6 +291,23 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { }); } + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any, done?: () => void): Promise { + const promises = []; + + this.dynamicComponents.forEach((component) => { + promises.push(Promise.resolve(component.callComponentFunction('doRefresh', [refresher, done]))); + }); + + return Promise.all(promises); + } + /** * Component destroyed. */ diff --git a/src/core/course/formats/singleactivity/components/singleactivity.ts b/src/core/course/formats/singleactivity/components/singleactivity.ts index cd5cae32c..a94f8730f 100644 --- a/src/core/course/formats/singleactivity/components/singleactivity.ts +++ b/src/core/course/formats/singleactivity/components/singleactivity.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnChanges, SimpleChange } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChange, ViewChild } from '@angular/core'; import { CoreCourseModuleDelegate } from '../../../providers/module-delegate'; import { CoreCourseUnsupportedModuleComponent } from '../../../components/unsupported-module/unsupported-module'; +import { CoreDynamicComponent } from '../../../../../components/dynamic-component/dynamic-component'; /** * Component to display single activity format. It will determine the right component to use and instantiate it. @@ -30,6 +31,8 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges { @Input() sections: any[]; // List of course sections. @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled. + @ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent; + componentClass: any; // The class of the component to render. data: any = {}; // Data to pass to the component. @@ -52,4 +55,15 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges { this.data.module = module; } } + + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any, done?: () => void): Promise { + return Promise.resolve(this.dynamicComponent.callComponentFunction('doRefresh', [refresher, done])); + } } diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index 1d30000b2..633fa480f 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -24,6 +24,7 @@ import { CoreCourseHelperProvider } from '../../providers/helper'; import { CoreCourseFormatDelegate } from '../../providers/format-delegate'; import { CoreCourseModulePrefetchDelegate } from '../../providers/module-prefetch-delegate'; import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from '../../providers/options-delegate'; +import { CoreCourseFormatComponent } from '../../components/format/format'; import { CoreCoursesProvider } from '../../../courses/providers/courses'; /** @@ -36,6 +37,7 @@ import { CoreCoursesProvider } from '../../../courses/providers/courses'; }) export class CoreCourseSectionPage implements OnDestroy { @ViewChild(Content) content: Content; + @ViewChild(CoreCourseFormatComponent) formatComponent: CoreCourseFormatComponent; title: string; course: any; @@ -150,7 +152,7 @@ export class CoreCourseSectionPage implements OnDestroy { promises.push(promise.then((completionStatus) => { // Get all the sections. - promises.push(this.courseProvider.getSections(this.course.id, false, true).then((sections) => { + return this.courseProvider.getSections(this.course.id, false, true).then((sections) => { if (refresh) { // Invalidate the recently downloaded module list. To ensure info can be prefetched. const modules = this.courseProvider.getSectionsModules(sections); @@ -185,7 +187,7 @@ export class CoreCourseSectionPage implements OnDestroy { // Get the title again now that we have sections. this.title = this.courseFormatDelegate.getCourseTitle(this.course, this.sections); - })); + }); })); // Load the course handlers. @@ -207,7 +209,9 @@ export class CoreCourseSectionPage implements OnDestroy { doRefresh(refresher: any): void { this.invalidateData().finally(() => { this.loadData(true).finally(() => { - refresher.complete(); + this.formatComponent.doRefresh(refresher).finally(() => { + refresher.complete(); + }); }); }); } diff --git a/src/core/course/providers/module-delegate.ts b/src/core/course/providers/module-delegate.ts index dd2dec7c9..1fbc7e2be 100644 --- a/src/core/course/providers/module-delegate.ts +++ b/src/core/course/providers/module-delegate.ts @@ -37,6 +37,7 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler { /** * Get the component to render the module. This is needed to support singleactivity course format. + * The component returned must implement CoreCourseModuleMainComponent. * * @param {any} course The course object. * @param {any} module The module object. @@ -91,6 +92,20 @@ export interface CoreCourseModuleHandlerData { action?(event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions): void; } +/** + * Interface that all the components to render the module in singleactivity must implement. + */ +export interface CoreCourseModuleMainComponent { + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any, done?: () => void): Promise; +} + /** * A button to display in a module item. */