MOBILE-2335 book: Support context menu prefetch and size
parent
aa95a20287
commit
e8cbead5d6
|
@ -7,8 +7,8 @@
|
|||
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" [iconAction]="'open'"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
|
||||
<core-context-menu-item [priority]="700" [content]="'core.refresh' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="600" [content]="timemodified" (action)="prefetch()" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="size" [priority]="500" [content]="size" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="trash"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="600" [content]="prefetchText" (action)="prefetch()" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="size" [priority]="500" [content]="size" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="'trash'"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Input, Output, EventEmitter, Optional } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, Optional } from '@angular/core';
|
||||
import { NavParams, NavController, Content, PopoverController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreAppProvider } from '../../../../../providers/app';
|
||||
|
@ -32,24 +32,31 @@ import { AddonModBookTocPopoverComponent } from '../../components/toc-popover/to
|
|||
selector: 'addon-mod-book-index',
|
||||
templateUrl: 'index.html',
|
||||
})
|
||||
export class AddonModBookIndexComponent implements OnInit, CoreCourseModuleMainComponent {
|
||||
export class AddonModBookIndexComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent {
|
||||
@Input() module: any; // The module of the book.
|
||||
@Input() courseId: number; // Course ID the book belongs to.
|
||||
@Output() bookRetrieved?: EventEmitter<any>;
|
||||
|
||||
externalUrl: string;
|
||||
description: string;
|
||||
loaded: boolean;
|
||||
component = AddonModBookProvider.COMPONENT;
|
||||
componentId: number;
|
||||
chapterContent: string;
|
||||
previousChapter: string;
|
||||
nextChapter: string;
|
||||
|
||||
// Data for context menu.
|
||||
externalUrl: string;
|
||||
description: string;
|
||||
refreshIcon: string;
|
||||
prefetchStatusIcon: string;
|
||||
prefetchText: string;
|
||||
size: string;
|
||||
|
||||
protected chapters: AddonModBookTocChapter[];
|
||||
protected currentChapter: string;
|
||||
protected contentsMap: AddonModBookContentsMap;
|
||||
protected isDestroyed = false;
|
||||
protected statusObserver;
|
||||
|
||||
constructor(private bookProvider: AddonModBookProvider, private courseProvider: CoreCourseProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private appProvider: CoreAppProvider, private textUtils: CoreTextUtilsProvider,
|
||||
|
@ -136,7 +143,7 @@ export class AddonModBookIndexComponent implements OnInit, CoreCourseModuleMainC
|
|||
* Prefetch the module.
|
||||
*/
|
||||
prefetch(): void {
|
||||
// @todo this.courseHelper.contextMenuPrefetch($scope, this.module, this.courseId);
|
||||
this.courseHelper.contextMenuPrefetch(this, this.module, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,7 +199,7 @@ export class AddonModBookIndexComponent implements OnInit, CoreCourseModuleMainC
|
|||
}
|
||||
|
||||
// All data obtained, now fill the context menu.
|
||||
// @todo this.courseHelper.fillContextMenu($scope, module, courseId, refresh, mmaModBookComponent);
|
||||
this.courseHelper.fillContextMenu(this, this.module, this.courseId, refresh, this.component);
|
||||
}).catch(() => {
|
||||
// Ignore errors, they're handled inside the loadChapter function.
|
||||
});
|
||||
|
@ -235,4 +242,9 @@ export class AddonModBookIndexComponent implements OnInit, CoreCourseModuleMainC
|
|||
this.refreshIcon = 'refresh';
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.statusObserver && this.statusObserver.off();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
core-context-menu-popover {
|
||||
.item-md ion-icon[item-start] + .item-inner,
|
||||
.item-md ion-icon[item-start] + .item-input {
|
||||
@include margin-horizontal(5px, null);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreEventsProvider } from '../../../providers/events';
|
||||
import { CoreFilepoolProvider } from '../../../providers/filepool';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
|
||||
|
@ -114,7 +115,8 @@ export class CoreCourseHelperProvider {
|
|||
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider,
|
||||
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
|
||||
private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider,
|
||||
private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider) { }
|
||||
private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider,
|
||||
private eventsProvider: CoreEventsProvider) { }
|
||||
|
||||
/**
|
||||
* This function treats every module on the sections provided to load the handler data, treat completion
|
||||
|
@ -358,8 +360,12 @@ export class CoreCourseHelperProvider {
|
|||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
confirmAndRemoveFiles(module: any, courseId: number): Promise<any> {
|
||||
return this.domUtils.showConfirm(this.translate.instant('course.confirmdeletemodulefiles')).then(() => {
|
||||
return this.domUtils.showConfirm(this.translate.instant('core.course.confirmdeletemodulefiles')).then(() => {
|
||||
return this.prefetchDelegate.removeModuleFiles(module, courseId);
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
this.domUtils.showErrorModal(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -405,6 +411,39 @@ export class CoreCourseHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prefetch a module, showing a confirmation modal if the size is big.
|
||||
* This function is meant to be called from a context menu option. It will also modify some data like the prefetch icon.
|
||||
*
|
||||
* @param {any} instance The component instance that has the context menu. It should have prefetchStatusIcon and isDestroyed.
|
||||
* @param {any} module Module to be prefetched
|
||||
* @param {number} courseId Course ID the module belongs to.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
contextMenuPrefetch(instance: any, module: any, courseId: number): Promise<any> {
|
||||
const initialIcon = instance.prefetchStatusIcon;
|
||||
let cancelled = false;
|
||||
|
||||
instance.prefetchStatusIcon = 'spinner'; // Show spinner since this operation might take a while.
|
||||
|
||||
// We need to call getDownloadSize, the package might have been updated.
|
||||
return this.prefetchDelegate.getModuleDownloadSize(module, courseId, true).then((size) => {
|
||||
return this.domUtils.confirmDownloadSize(size).catch(() => {
|
||||
// User hasn't confirmed, stop.
|
||||
cancelled = true;
|
||||
|
||||
return Promise.reject(null);
|
||||
}).then(() => {
|
||||
return this.prefetchDelegate.prefetchModule(module, courseId, true);
|
||||
});
|
||||
}).catch((error) => {
|
||||
instance.prefetchStatusIcon = initialIcon;
|
||||
if (!instance.isDestroyed && !cancelled) {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the status of a list of courses.
|
||||
*
|
||||
|
@ -431,6 +470,41 @@ export class CoreCourseHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the Context Menu for a certain module.
|
||||
*
|
||||
* @param {any} instance The component instance that has the context menu.
|
||||
* @param {any} module Module to be prefetched
|
||||
* @param {number} courseId Course ID the module belongs to.
|
||||
* @param {boolean} [invalidateCache] Invalidates the cache first.
|
||||
* @param {string} [component] Component of the module.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
fillContextMenu(instance: any, module: any, courseId: number, invalidateCache?: boolean, component?: string): Promise<any> {
|
||||
return this.getModulePrefetchInfo(module, courseId, invalidateCache, component).then((moduleInfo) => {
|
||||
instance.size = moduleInfo.size > 0 ? moduleInfo.sizeReadable : 0;
|
||||
instance.prefetchStatusIcon = moduleInfo.statusIcon;
|
||||
|
||||
if (moduleInfo.status != CoreConstants.NOT_DOWNLOADABLE) {
|
||||
// Module is downloadable, get the text to display to prefetch.
|
||||
if (moduleInfo.downloadTime > 0) {
|
||||
instance.prefetchText = this.translate.instant('core.lastdownloaded') + ': ' + moduleInfo.downloadTimeReadable;
|
||||
} else {
|
||||
// Module not downloaded, show a default text.
|
||||
instance.prefetchText = this.translate.instant('core.download');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof instance.statusObserver == 'undefined' && component) {
|
||||
instance.statusObserver = this.eventsProvider.on(CoreEventsProvider.PACKAGE_STATUS_CHANGED, (data) => {
|
||||
if (data.componentId == module.id && data.component == component) {
|
||||
this.fillContextMenu(instance, module, courseId, false, component);
|
||||
}
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a course download promise (if any).
|
||||
*
|
||||
|
|
|
@ -225,6 +225,11 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
super('CoreCourseModulePrefetchDelegate', loggerProvider, sitesProvider, eventsProvider);
|
||||
|
||||
this.sitesProvider.createTableFromSchema(this.checkUpdatesTableSchema);
|
||||
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, this.clearStatusCache.bind(this));
|
||||
eventsProvider.on(CoreEventsProvider.PACKAGE_STATUS_CHANGED, (data) => {
|
||||
this.updateStatusCache(data.status, data.component, data.componentId);
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -653,6 +658,8 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
promise;
|
||||
|
||||
if (!refresh && typeof status != 'undefined') {
|
||||
this.storeCourseAndSection(packageId, courseId, sectionId);
|
||||
|
||||
return Promise.resolve(this.determineModuleStatus(module, status, canCheck));
|
||||
}
|
||||
|
||||
|
@ -664,7 +671,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
|
||||
// Get the saved package status.
|
||||
return this.filepoolProvider.getPackageStatus(siteId, component, module.id).then((currentStatus) => {
|
||||
status = handler.determineStatus ? handler.determineStatus(module, status, canCheck) : status;
|
||||
status = handler.determineStatus ? handler.determineStatus(module, currentStatus, canCheck) : currentStatus;
|
||||
if (status != CoreConstants.DOWNLOADED) {
|
||||
return status;
|
||||
}
|
||||
|
@ -696,7 +703,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
// Has updates, mark the module as outdated.
|
||||
status = CoreConstants.OUTDATED;
|
||||
|
||||
return this.filepoolProvider.storePackageStatus(siteId, component, module.id, status).catch(() => {
|
||||
return this.filepoolProvider.storePackageStatus(siteId, status, component, module.id).catch(() => {
|
||||
// Ignore errors.
|
||||
}).then(() => {
|
||||
return status;
|
||||
|
@ -710,13 +717,14 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
}, () => {
|
||||
// Error getting updates, show the stored status.
|
||||
updateStatus = false;
|
||||
this.storeCourseAndSection(packageId, courseId, sectionId);
|
||||
|
||||
return currentStatus;
|
||||
});
|
||||
});
|
||||
}).then((status) => {
|
||||
if (updateStatus) {
|
||||
this.updateStatusCache(status, courseId, component, module.id, sectionId);
|
||||
this.updateStatusCache(status, component, module.id, courseId, sectionId);
|
||||
}
|
||||
|
||||
return this.determineModuleStatus(module, status, canCheck);
|
||||
|
@ -770,11 +778,6 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
|
||||
promises.push(this.getModuleStatus(module, courseId, updates, refresh).then((modStatus) => {
|
||||
if (modStatus != CoreConstants.NOT_DOWNLOADABLE) {
|
||||
if (sectionId && sectionId > 0) {
|
||||
// Store the section ID.
|
||||
this.statusCache.setValue(packageId, 'sectionId', sectionId);
|
||||
}
|
||||
|
||||
status = this.filepoolProvider.determinePackagesStatus(status, modStatus);
|
||||
result[modStatus].push(module);
|
||||
result.total++;
|
||||
|
@ -1123,7 +1126,8 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
// Update status of the module.
|
||||
const packageId = this.filepoolProvider.getPackageId(handler.component, module.id);
|
||||
this.statusCache.setValue(packageId, 'downloadedSize', 0);
|
||||
this.filepoolProvider.storePackageStatus(siteId, handler.component, module.id, CoreConstants.NOT_DOWNLOADED);
|
||||
|
||||
return this.filepoolProvider.storePackageStatus(siteId, CoreConstants.NOT_DOWNLOADED, handler.component, module.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1144,6 +1148,22 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If courseId or sectionId is set, save them in the cache.
|
||||
*
|
||||
* @param {string} packageId The package ID.
|
||||
* @param {number} [courseId] Course ID.
|
||||
* @param {number} [sectionId] Section ID.
|
||||
*/
|
||||
storeCourseAndSection(packageId: string, courseId?: number, sectionId?: number): void {
|
||||
if (courseId) {
|
||||
this.statusCache.setValue(packageId, 'courseId', courseId);
|
||||
}
|
||||
if (sectionId && sectionId > 0) {
|
||||
this.statusCache.setValue(packageId, 'sectionId', sectionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat the result of the check updates WS call.
|
||||
*
|
||||
|
@ -1181,12 +1201,12 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
* Update the status of a module in the "cache".
|
||||
*
|
||||
* @param {string} status New status.
|
||||
* @param {number} courseId Course ID of the module.
|
||||
* @param {string} component Package's component.
|
||||
* @param {string|number} [componentId] An ID to use in conjunction with the component.
|
||||
* @param {number} [courseId] Course ID of the module.
|
||||
* @param {number} [sectionId] Section ID of the module.
|
||||
*/
|
||||
updateStatusCache(status: string, courseId: number, component: string, componentId?: string | number, sectionId?: number)
|
||||
updateStatusCache(status: string, component: string, componentId?: string | number, courseId?: number, sectionId?: number)
|
||||
: void {
|
||||
const packageId = this.filepoolProvider.getPackageId(component, componentId),
|
||||
cachedStatus = this.statusCache.getValue(packageId, 'status', true);
|
||||
|
@ -1195,7 +1215,13 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
// If the status has changed, notify that the section has changed.
|
||||
notify = typeof cachedStatus != 'undefined' && cachedStatus !== status;
|
||||
|
||||
// If courseId/sectionId is set, store it.
|
||||
this.storeCourseAndSection(packageId, courseId, sectionId);
|
||||
|
||||
if (notify) {
|
||||
if (!courseId) {
|
||||
courseId = this.statusCache.getValue(packageId, 'courseId', true);
|
||||
}
|
||||
if (!sectionId) {
|
||||
sectionId = this.statusCache.getValue(packageId, 'sectionId', true);
|
||||
}
|
||||
|
@ -1205,8 +1231,6 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
this.statusCache.setValue(packageId, 'status', status);
|
||||
|
||||
if (sectionId) {
|
||||
this.statusCache.setValue(packageId, 'sectionId', sectionId);
|
||||
|
||||
this.eventsProvider.trigger(CoreEventsProvider.SECTION_STATUS_CHANGED, {
|
||||
sectionId: sectionId,
|
||||
courseId: courseId
|
||||
|
|
Loading…
Reference in New Issue