MOBILE-2341 core: Support split views in course formats

main
Albert Gasset 2018-05-11 19:22:22 +02:00
parent bc71bbbdfc
commit b34adcaa42
9 changed files with 129 additions and 35 deletions

View File

@ -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<any>} Promise resolved when done.
*/
doRefresh(refresher?: any, done?: () => void, showErrors: boolean = false): Promise<any> {
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.
*

View File

@ -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<any>} Promise resolved when done.
*/
doRefresh(refresher?: any, done?: () => void): Promise<any> {
doRefresh(refresher?: any, done?: () => void, showErrors: boolean = false): Promise<any> {
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<any>} Resolved when done.
*/
protected refreshContent(): Promise<any> {
protected refreshContent(sync: boolean = false, showErrors: boolean = false): Promise<any> {
this.refreshIcon = 'spinner';
return this.invalidateContent().catch(() => {

View File

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

View File

@ -17,7 +17,7 @@
</core-context-menu>
</core-navbar-buttons>
<ion-content>
<ion-refresher [enabled]="dataLoaded" (ionRefresh)="doRefresh($event)">
<ion-refresher [enabled]="dataLoaded && displayRefresher" (ionRefresh)="doRefresh($event)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>

View File

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

View File

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

View File

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

View File

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

View File

@ -53,6 +53,14 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
*/
getMainComponent(injector: Injector, course: any, module: any): any | Promise<any>;
/**
* 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');
}
}