diff --git a/src/addon/mod/book/book.module.ts b/src/addon/mod/book/book.module.ts index e1be5e8e2..5ff680aad 100644 --- a/src/addon/mod/book/book.module.ts +++ b/src/addon/mod/book/book.module.ts @@ -16,8 +16,10 @@ import { NgModule } from '@angular/core'; import { AddonModBookProvider } from './providers/book'; import { AddonModBookModuleHandler } from './providers/module-handler'; import { AddonModBookLinkHandler } from './providers/link-handler'; +import { AddonModBookPrefetchHandler } from './providers/prefetch-handler'; import { CoreCourseModuleDelegate } from '../../../core/course/providers/module-delegate'; import { CoreContentLinksDelegate } from '../../../core/contentlinks/providers/delegate'; +import { CoreCourseModulePrefetchDelegate } from '../../../core/course/providers/module-prefetch-delegate'; @NgModule({ declarations: [ @@ -27,13 +29,16 @@ import { CoreContentLinksDelegate } from '../../../core/contentlinks/providers/d providers: [ AddonModBookProvider, AddonModBookModuleHandler, - AddonModBookLinkHandler + AddonModBookLinkHandler, + AddonModBookPrefetchHandler ] }) export class AddonModBookModule { constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModBookModuleHandler, - contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModBookLinkHandler) { + contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModBookLinkHandler, + prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModBookPrefetchHandler) { moduleDelegate.registerHandler(moduleHandler); contentLinksDelegate.registerHandler(linkHandler); + prefetchDelegate.registerHandler(prefetchHandler); } } diff --git a/src/addon/mod/book/providers/prefetch-handler.ts b/src/addon/mod/book/providers/prefetch-handler.ts new file mode 100644 index 000000000..90b4fcce8 --- /dev/null +++ b/src/addon/mod/book/providers/prefetch-handler.ts @@ -0,0 +1,103 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable, Injector } from '@angular/core'; +import { CoreCourseModulePrefetchHandlerBase } from '../../../../core/course/classes/module-prefetch-handler'; +import { AddonModBookProvider } from './book'; + +/** + * Handler to prefetch books. + */ +@Injectable() +export class AddonModBookPrefetchHandler extends CoreCourseModulePrefetchHandlerBase { + name = 'book'; + component = AddonModBookProvider.COMPONENT; + updatesNames = /^configuration$|^.*files$|^entries$/; + isResource = true; + + constructor(injector: Injector, protected bookProvider: AddonModBookProvider) { + super(injector); + } + + /** + * Download or prefetch the content. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @param {boolean} [prefetch] True to prefetch, false to download right away. + * @param {string} [dirPath] Path of the directory where to store all the content files. This is to keep the files + * relative paths and make the package work in an iframe. Undefined to download the files + * in the filepool root folder. + * @return {Promise} Promise resolved when all content is downloaded. Data returned is not reliable. + */ + downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { + const promises = []; + + promises.push(super.downloadOrPrefetch(module, courseId, prefetch)); + promises.push(this.bookProvider.getBook(courseId, module.id)); + + return Promise.all(promises); + } + + /** + * Returns module intro files. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @return {Promise} Promise resolved with list of intro files. + */ + getIntroFiles(module: any, courseId: number): Promise { + return this.bookProvider.getBook(courseId, module.id).catch(() => { + // Not found, return undefined so module description is used. + }).then((book) => { + return this.getIntroFilesFromInstance(module, book); + }); + } + + /** + * Invalidate the prefetched content. + * + * @param {number} moduleId The module ID. + * @param {number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateContent(moduleId: number, courseId: number): Promise { + return this.bookProvider.invalidateContent(moduleId, courseId); + } + + /** + * Invalidate WS calls needed to determine module status. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when invalidated. + */ + invalidateModule(module: any, courseId: number): Promise { + const promises = []; + + promises.push(this.bookProvider.invalidateBookData(courseId)); + promises.push(this.courseProvider.invalidateModule(module.id)); + + return Promise.all(promises); + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. + */ + isEnabled(): boolean | Promise { + return this.bookProvider.isPluginEnabled(); + } +} diff --git a/src/classes/delegate.ts b/src/classes/delegate.ts index cfbb953d4..187e9d615 100644 --- a/src/classes/delegate.ts +++ b/src/classes/delegate.ts @@ -143,9 +143,9 @@ export class CoreDelegate { * * @param {string} handlerName The handler name. * @param {boolean} [enabled] Only enabled, or any. - * @return {any} Handler. + * @return {CoreDelegateHandler} Handler. */ - protected getHandler(handlerName: string, enabled: boolean = false): any { + protected getHandler(handlerName: string, enabled: boolean = false): CoreDelegateHandler { return enabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName]; } diff --git a/src/core/course/classes/module-prefetch-handler.ts b/src/core/course/classes/module-prefetch-handler.ts index 7d1c9623d..47b115140 100644 --- a/src/core/course/classes/module-prefetch-handler.ts +++ b/src/core/course/classes/module-prefetch-handler.ts @@ -53,17 +53,11 @@ export type prefetchFunction = (module: any, courseId: number, single: boolean, * recommended to call the prefetchPackage function since it'll handle changing the status of the module. */ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePrefetchHandler { - /** - * A name to identify the addon. - * @type {string} - */ - name = 'CoreCourseModulePrefetchHandlerBase'; - /** * Name of the module. It should match the "modname" of the module returned in core_course_get_contents. * @type {string} */ - modname = ''; + name = ''; /** * The handler's component. @@ -235,7 +229,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref * @param {number} courseId Course ID the module belongs to. * @return {number|Promise} Size, or promise resolved with the size. */ - getDownloadedSize?(module: any, courseId: number): number | Promise { + getDownloadedSize(module: any, courseId: number): number | Promise { const siteId = this.sitesProvider.getCurrentSiteId(); return this.filepoolProvider.getFilesSizeByComponent(siteId, this.component, module.id); @@ -324,9 +318,10 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref * Invalidate the prefetched content. * * @param {number} moduleId The module ID. + * @param {number} courseId The course ID the module belongs to. * @return {Promise} Promise resolved when the data is invalidated. */ - invalidateContent(moduleId: number): Promise { + invalidateContent(moduleId: number, courseId: number): Promise { const promises = [], siteId = this.sitesProvider.getCurrentSiteId(); diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index 35a434feb..1d30000b2 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -151,6 +151,18 @@ 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) => { + if (refresh) { + // Invalidate the recently downloaded module list. To ensure info can be prefetched. + const modules = this.courseProvider.getSectionsModules(sections); + + return this.prefetchDelegate.invalidateModules(modules, this.course.id).then(() => { + return sections; + }); + } else { + return sections; + } + }).then((sections) => { + this.courseHelper.addHandlerDataForModules(sections, this.course.id, completionStatus); // Format the name of each section and check if it has content. diff --git a/src/core/course/providers/module-prefetch-delegate.ts b/src/core/course/providers/module-prefetch-delegate.ts index 6980c3cad..de95a6287 100644 --- a/src/core/course/providers/module-prefetch-delegate.ts +++ b/src/core/course/providers/module-prefetch-delegate.ts @@ -202,9 +202,6 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { }; protected ROOT_CACHE_KEY = 'mmCourse:'; - - protected handlers: { [s: string]: CoreCourseModulePrefetchHandler } = {}; // All registered handlers. - protected enabledHandlers: { [s: string]: CoreCourseModulePrefetchHandler } = {}; // Handlers enabled for the current site. protected statusCache = new CoreCache(); // Promises for check updates, to prevent performing the same request twice at the same time. @@ -225,7 +222,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { private courseProvider: CoreCourseProvider, private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider, private fileProvider: CoreFileProvider, protected eventsProvider: CoreEventsProvider) { - super('CoreCourseModulePrefetchDelegate', loggerProvider, sitesProvider); + super('CoreCourseModulePrefetchDelegate', loggerProvider, sitesProvider, eventsProvider); this.sitesProvider.createTableFromSchema(this.checkUpdatesTableSchema); } @@ -859,7 +856,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { * @return {CoreCourseModulePrefetchHandler} Prefetch handler. */ getPrefetchHandlerFor(module: any): CoreCourseModulePrefetchHandler { - return this.enabledHandlers[module.modname]; + return this.getHandler(module.modname, true); } /** diff --git a/src/core/user/providers/user-profile-field-delegate.ts b/src/core/user/providers/user-profile-field-delegate.ts index 5f924896d..c3e8b7282 100644 --- a/src/core/user/providers/user-profile-field-delegate.ts +++ b/src/core/user/providers/user-profile-field-delegate.ts @@ -99,7 +99,8 @@ export class CoreUserProfileFieldDelegate extends CoreDelegate { */ getDataForField(field: any, signup: boolean, registerAuth: string, formValues: any): Promise { const type = field.type || field.datatype, - handler = this.getHandler(type, !signup); + handler = this.getHandler(type, !signup); + if (handler) { const name = 'profile_field_' + field.shortname; if (handler.getData) { diff --git a/src/directives/link.ts b/src/directives/link.ts index d565a90c5..b93ef269b 100644 --- a/src/directives/link.ts +++ b/src/directives/link.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, OnInit, ElementRef } from '@angular/core'; +import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController, Content } from 'ionic-angular'; import { CoreSitesProvider } from '../providers/sites'; import { CoreDomUtilsProvider } from '../providers/utils/dom'; @@ -40,7 +40,7 @@ export class CoreLinkDirective implements OnInit { constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider, private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider, private contentLinksHelper: CoreContentLinksHelperProvider, private navCtrl: NavController, - private content: Content) { + @Optional() private content: Content) { // This directive can be added dynamically. In that case, the first param is the anchor HTMLElement. this.element = element.nativeElement || element; }