MOBILE-2335 book: Implement prefetch handler

main
Dani Palou 2018-02-01 10:14:47 +01:00
parent f8d71dd6ee
commit 7379ca3e71
8 changed files with 134 additions and 21 deletions

View File

@ -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);
}
}

View File

@ -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<any>} Promise resolved when all content is downloaded. Data returned is not reliable.
*/
downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise<any> {
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<any[]>} Promise resolved with list of intro files.
*/
getIntroFiles(module: any, courseId: number): Promise<any[]> {
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<any>} Promise resolved when the data is invalidated.
*/
invalidateContent(moduleId: number, courseId: number): Promise<any> {
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<any>} Promise resolved when invalidated.
*/
invalidateModule(module: any, courseId: number): Promise<any> {
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<boolean>} A boolean, or a promise resolved with a boolean, indicating if the handler is enabled.
*/
isEnabled(): boolean | Promise<boolean> {
return this.bookProvider.isPluginEnabled();
}
}

View File

@ -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];
}

View File

@ -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<number>} Size, or promise resolved with the size.
*/
getDownloadedSize?(module: any, courseId: number): number | Promise<number> {
getDownloadedSize(module: any, courseId: number): number | Promise<number> {
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<any>} Promise resolved when the data is invalidated.
*/
invalidateContent(moduleId: number): Promise<any> {
invalidateContent(moduleId: number, courseId: number): Promise<any> {
const promises = [],
siteId = this.sitesProvider.getCurrentSiteId();

View File

@ -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.

View File

@ -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 <CoreCourseModulePrefetchHandler> this.getHandler(module.modname, true);
}
/**

View File

@ -99,7 +99,8 @@ export class CoreUserProfileFieldDelegate extends CoreDelegate {
*/
getDataForField(field: any, signup: boolean, registerAuth: string, formValues: any): Promise<any> {
const type = field.type || field.datatype,
handler = this.getHandler(type, !signup);
handler = <CoreUserProfileFieldHandler> this.getHandler(type, !signup);
if (handler) {
const name = 'profile_field_' + field.shortname;
if (handler.getData) {

View File

@ -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;
}