2017-12-29 09:18:17 +01:00
|
|
|
// (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.
|
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
import { Component, ViewChild, OnDestroy } from '@angular/core';
|
2018-01-23 09:08:49 +01:00
|
|
|
import { IonicPage, NavParams, Content, NavController } from 'ionic-angular';
|
2017-12-29 09:18:17 +01:00
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2018-01-02 08:47:21 +01:00
|
|
|
import { CoreEventsProvider } from '../../../../providers/events';
|
2018-01-17 12:12:23 +01:00
|
|
|
import { CoreSitesProvider } from '../../../../providers/sites';
|
2017-12-29 09:18:17 +01:00
|
|
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
|
|
|
import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
|
|
|
|
import { CoreCourseProvider } from '../../providers/course';
|
|
|
|
import { CoreCourseHelperProvider } from '../../providers/helper';
|
|
|
|
import { CoreCourseFormatDelegate } from '../../providers/format-delegate';
|
2018-01-29 10:05:20 +01:00
|
|
|
import { CoreCourseModulePrefetchDelegate } from '../../providers/module-prefetch-delegate';
|
2018-01-26 09:54:01 +01:00
|
|
|
import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from '../../providers/options-delegate';
|
2018-01-02 12:21:53 +01:00
|
|
|
import { CoreCoursesProvider } from '../../../courses/providers/courses';
|
2017-12-29 09:18:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Page that displays the list of courses the user is enrolled in.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
@IonicPage({ segment: 'core-course-section' })
|
2017-12-29 09:18:17 +01:00
|
|
|
@Component({
|
|
|
|
selector: 'page-core-course-section',
|
|
|
|
templateUrl: 'section.html',
|
|
|
|
})
|
2018-01-02 08:47:21 +01:00
|
|
|
export class CoreCourseSectionPage implements OnDestroy {
|
|
|
|
@ViewChild(Content) content: Content;
|
|
|
|
|
2017-12-29 09:18:17 +01:00
|
|
|
title: string;
|
|
|
|
course: any;
|
|
|
|
sections: any[];
|
2018-01-23 09:08:49 +01:00
|
|
|
sectionId: number;
|
2018-01-23 11:26:21 +01:00
|
|
|
sectionNumber: number;
|
2018-01-26 09:54:01 +01:00
|
|
|
courseHandlers: CoreCourseOptionsHandlerToDisplay[];
|
2018-01-30 13:16:28 +01:00
|
|
|
handlerData: any = {}; // Data to send to the handlers components.
|
2017-12-29 09:18:17 +01:00
|
|
|
dataLoaded: boolean;
|
2018-01-17 12:12:23 +01:00
|
|
|
downloadEnabled: boolean;
|
2018-01-29 10:05:20 +01:00
|
|
|
downloadEnabledIcon = 'square-outline'; // Disabled by default.
|
2018-01-17 12:12:23 +01:00
|
|
|
prefetchCourseData = {
|
|
|
|
prefetchCourseIcon: 'spinner'
|
|
|
|
};
|
2018-01-31 11:37:42 +01:00
|
|
|
moduleId: number;
|
2017-12-29 09:18:17 +01:00
|
|
|
|
2018-01-31 11:37:42 +01:00
|
|
|
protected module: any;
|
2018-01-02 08:47:21 +01:00
|
|
|
protected completionObserver;
|
2018-01-17 12:12:23 +01:00
|
|
|
protected courseStatusObserver;
|
|
|
|
protected isDestroyed = false;
|
2018-01-02 08:47:21 +01:00
|
|
|
|
2018-01-31 11:37:42 +01:00
|
|
|
constructor(navParams: NavParams, private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
|
2018-01-26 09:54:01 +01:00
|
|
|
private courseFormatDelegate: CoreCourseFormatDelegate, private courseOptionsDelegate: CoreCourseOptionsDelegate,
|
2018-01-02 12:21:53 +01:00
|
|
|
private translate: TranslateService, private courseHelper: CoreCourseHelperProvider, eventsProvider: CoreEventsProvider,
|
2018-01-17 12:12:23 +01:00
|
|
|
private textUtils: CoreTextUtilsProvider, private coursesProvider: CoreCoursesProvider,
|
2018-01-29 10:05:20 +01:00
|
|
|
sitesProvider: CoreSitesProvider, private navCtrl: NavController,
|
|
|
|
private prefetchDelegate: CoreCourseModulePrefetchDelegate) {
|
2017-12-29 09:18:17 +01:00
|
|
|
this.course = navParams.get('course');
|
2018-01-23 09:08:49 +01:00
|
|
|
this.sectionId = navParams.get('sectionId');
|
2018-01-23 11:26:21 +01:00
|
|
|
this.sectionNumber = navParams.get('sectionNumber');
|
2018-01-31 11:37:42 +01:00
|
|
|
this.module = navParams.get('module');
|
2018-01-30 13:16:28 +01:00
|
|
|
this.handlerData.courseId = this.course.id;
|
2018-01-02 08:47:21 +01:00
|
|
|
|
2018-01-18 09:50:22 +01:00
|
|
|
// Get the title to display. We dont't have sections yet.
|
|
|
|
this.title = courseFormatDelegate.getCourseTitle(this.course);
|
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
this.completionObserver = eventsProvider.on(CoreEventsProvider.COMPLETION_MODULE_VIEWED, (data) => {
|
|
|
|
if (data && data.courseId == this.course.id) {
|
|
|
|
this.refreshAfterCompletionChange();
|
|
|
|
}
|
|
|
|
});
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
// Listen for changes in course status.
|
|
|
|
this.courseStatusObserver = eventsProvider.on(CoreEventsProvider.COURSE_STATUS_CHANGED, (data) => {
|
|
|
|
if (data.courseId == this.course.id) {
|
|
|
|
this.prefetchCourseData.prefetchCourseIcon = this.courseHelper.getCourseStatusIconFromStatus(data.status);
|
|
|
|
}
|
|
|
|
}, sitesProvider.getCurrentSiteId());
|
2017-12-29 09:18:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* View loaded.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
ionViewDidLoad(): void {
|
2018-01-23 09:08:49 +01:00
|
|
|
|
2018-01-31 11:37:42 +01:00
|
|
|
if (this.module) {
|
|
|
|
this.moduleId = this.module.id;
|
|
|
|
this.courseHelper.openModule(this.navCtrl, this.module, this.course.id, this.sectionId);
|
2018-01-23 09:08:49 +01:00
|
|
|
}
|
|
|
|
|
2017-12-29 09:18:17 +01:00
|
|
|
this.loadData().finally(() => {
|
|
|
|
this.dataLoaded = true;
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
// Determine the course prefetch status.
|
|
|
|
this.determineCoursePrefetchIcon().then(() => {
|
|
|
|
if (this.prefetchCourseData.prefetchCourseIcon == 'spinner') {
|
|
|
|
// Course is being downloaded. Get the download promise.
|
|
|
|
const promise = this.courseHelper.getCourseDownloadPromise(this.course.id);
|
|
|
|
if (promise) {
|
|
|
|
// There is a download promise. Show an error if it fails.
|
|
|
|
promise.catch((error) => {
|
|
|
|
if (!this.isDestroyed) {
|
|
|
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// No download, this probably means that the app was closed while downloading. Set previous status.
|
|
|
|
this.courseProvider.setCoursePreviousStatus(this.course.id).then((status) => {
|
|
|
|
this.prefetchCourseData.prefetchCourseIcon = this.courseHelper.getCourseStatusIconFromStatus(status);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2017-12-29 09:18:17 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch and load all the data required for the view.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
protected loadData(refresh?: boolean): Promise<any> {
|
2018-01-02 12:21:53 +01:00
|
|
|
// First of all, get the course because the data might have changed.
|
2018-01-30 13:15:48 +01:00
|
|
|
return this.coursesProvider.getUserCourse(this.course.id).catch(() => {
|
|
|
|
// Error getting the course, probably guest access.
|
|
|
|
}).then((course) => {
|
2018-01-29 10:05:20 +01:00
|
|
|
const promises = [];
|
|
|
|
let promise;
|
2018-01-02 12:21:53 +01:00
|
|
|
|
2018-01-30 13:15:48 +01:00
|
|
|
if (course) {
|
|
|
|
this.course = course;
|
|
|
|
}
|
2018-01-02 12:21:53 +01:00
|
|
|
|
|
|
|
// Get the completion status.
|
|
|
|
if (this.course.enablecompletion === false) {
|
|
|
|
// Completion not enabled.
|
|
|
|
promise = Promise.resolve({});
|
|
|
|
} else {
|
|
|
|
promise = this.courseProvider.getActivitiesCompletionStatus(this.course.id).catch(() => {
|
|
|
|
// It failed, don't use completion.
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
}
|
2018-01-02 08:47:21 +01:00
|
|
|
|
2018-01-02 12:21:53 +01:00
|
|
|
promises.push(promise.then((completionStatus) => {
|
|
|
|
// Get all the sections.
|
|
|
|
promises.push(this.courseProvider.getSections(this.course.id, false, true).then((sections) => {
|
2018-01-23 09:08:49 +01:00
|
|
|
this.courseHelper.addHandlerDataForModules(sections, this.course.id, completionStatus);
|
2018-01-02 12:21:53 +01:00
|
|
|
|
|
|
|
// Format the name of each section and check if it has content.
|
|
|
|
this.sections = sections.map((section) => {
|
|
|
|
this.textUtils.formatText(section.name.trim(), true, true).then((name) => {
|
|
|
|
section.formattedName = name;
|
|
|
|
});
|
|
|
|
section.hasContent = this.courseHelper.sectionHasContent(section);
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-02 12:21:53 +01:00
|
|
|
return section;
|
2017-12-29 09:18:17 +01:00
|
|
|
});
|
|
|
|
|
2018-01-02 12:21:53 +01:00
|
|
|
if (this.courseFormatDelegate.canViewAllSections(this.course)) {
|
|
|
|
// Add a fake first section (all sections).
|
|
|
|
this.sections.unshift({
|
|
|
|
name: this.translate.instant('core.course.allsections'),
|
|
|
|
id: CoreCourseProvider.ALL_SECTIONS_ID
|
|
|
|
});
|
|
|
|
}
|
2018-01-18 09:50:22 +01:00
|
|
|
|
|
|
|
// Get the title again now that we have sections.
|
|
|
|
this.title = this.courseFormatDelegate.getCourseTitle(this.course, this.sections);
|
2018-01-02 12:21:53 +01:00
|
|
|
}));
|
2017-12-29 09:18:17 +01:00
|
|
|
}));
|
|
|
|
|
2018-01-02 12:21:53 +01:00
|
|
|
// Load the course handlers.
|
2018-01-26 09:54:01 +01:00
|
|
|
promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.course, refresh, false).then((handlers) => {
|
2018-01-02 12:21:53 +01:00
|
|
|
this.courseHandlers = handlers;
|
|
|
|
}));
|
2017-12-29 09:18:17 +01:00
|
|
|
|
2018-01-02 12:21:53 +01:00
|
|
|
return Promise.all(promises).catch((error) => {
|
|
|
|
this.domUtils.showErrorModalDefault(error, 'core.course.couldnotloadsectioncontent', true);
|
|
|
|
});
|
2017-12-29 09:18:17 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh the data.
|
|
|
|
*
|
|
|
|
* @param {any} refresher Refresher.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
doRefresh(refresher: any): void {
|
2018-01-02 12:21:53 +01:00
|
|
|
this.invalidateData().finally(() => {
|
2017-12-29 09:18:17 +01:00
|
|
|
this.loadData(true).finally(() => {
|
|
|
|
refresher.complete();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2018-01-02 08:47:21 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The completion of any of the modules have changed.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
onCompletionChange(): void {
|
2018-01-02 12:21:53 +01:00
|
|
|
this.invalidateData().finally(() => {
|
2018-01-02 08:47:21 +01:00
|
|
|
this.refreshAfterCompletionChange();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-01-02 12:21:53 +01:00
|
|
|
/**
|
|
|
|
* Invalidate the data.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
protected invalidateData(): Promise<any> {
|
|
|
|
const promises = [];
|
2018-01-02 12:21:53 +01:00
|
|
|
|
|
|
|
promises.push(this.courseProvider.invalidateSections(this.course.id));
|
|
|
|
promises.push(this.coursesProvider.invalidateUserCourses());
|
2018-01-11 13:18:17 +01:00
|
|
|
promises.push(this.courseFormatDelegate.invalidateData(this.course, this.sections));
|
2018-01-02 12:21:53 +01:00
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
if (this.sections) {
|
|
|
|
promises.push(this.prefetchDelegate.invalidateCourseUpdates(this.course.id));
|
|
|
|
}
|
2018-01-02 12:21:53 +01:00
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
/**
|
|
|
|
* Refresh list after a completion change since there could be new activities.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
protected refreshAfterCompletionChange(): void {
|
2018-01-02 08:47:21 +01:00
|
|
|
// Save scroll position to restore it once done.
|
2018-01-29 10:05:20 +01:00
|
|
|
const scrollElement = this.content.getScrollElement(),
|
2018-01-02 08:47:21 +01:00
|
|
|
scrollTop = scrollElement.scrollTop || 0,
|
|
|
|
scrollLeft = scrollElement.scrollLeft || 0;
|
|
|
|
|
|
|
|
this.dataLoaded = false;
|
|
|
|
this.content.scrollToTop(); // Scroll top so the spinner is seen.
|
|
|
|
|
|
|
|
this.loadData().finally(() => {
|
|
|
|
this.dataLoaded = true;
|
|
|
|
this.content.scrollTo(scrollLeft, scrollTop);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
/**
|
|
|
|
* Determines the prefetch icon of the course.
|
2018-01-29 10:05:20 +01:00
|
|
|
*
|
|
|
|
* @return {Promise<void>} Promise resolved when done.
|
2018-01-17 12:12:23 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
protected determineCoursePrefetchIcon(): Promise<void> {
|
2018-01-17 12:12:23 +01:00
|
|
|
return this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => {
|
|
|
|
this.prefetchCourseData.prefetchCourseIcon = icon;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prefetch the whole course.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
prefetchCourse(): void {
|
2018-01-17 12:12:23 +01:00
|
|
|
this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, this.sections, this.courseHandlers)
|
2018-01-29 10:05:20 +01:00
|
|
|
.then((downloaded) => {
|
|
|
|
if (downloaded && this.downloadEnabled) {
|
|
|
|
// Recalculate the status.
|
|
|
|
this.courseHelper.calculateSectionsStatus(this.sections, this.course.id).catch(() => {
|
|
|
|
// Ignore errors (shouldn't happen).
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}).catch((error) => {
|
|
|
|
if (!this.isDestroyed) {
|
|
|
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
|
|
|
}
|
|
|
|
});
|
2018-01-17 12:12:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle download enabled.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
toggleDownload(): void {
|
2018-01-17 12:12:23 +01:00
|
|
|
this.downloadEnabled = !this.downloadEnabled;
|
|
|
|
this.downloadEnabledIcon = this.downloadEnabled ? 'checkbox-outline' : 'square-outline';
|
|
|
|
}
|
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
/**
|
|
|
|
* Page destroyed.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
ngOnDestroy(): void {
|
2018-01-17 12:12:23 +01:00
|
|
|
this.isDestroyed = true;
|
2018-01-02 08:47:21 +01:00
|
|
|
if (this.completionObserver) {
|
|
|
|
this.completionObserver.off();
|
|
|
|
}
|
|
|
|
}
|
2017-12-29 09:18:17 +01:00
|
|
|
}
|