MOBILE-2310 courses: Apply download courses
parent
fe2c4cce74
commit
04085d5929
|
@ -242,6 +242,10 @@ export class CoreCourseSectionPage implements OnDestroy {
|
||||||
// Ignore errors (shouldn't happen).
|
// Ignore errors (shouldn't happen).
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
if (!this.isDestroyed) {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<a ion-item text-wrap detail-none (click)="openCourse(course)" [title]="course.fullname">
|
<a ion-item text-wrap detail-none (click)="openCourse(course)" [title]="course.fullname">
|
||||||
<h2 float-start><core-format-text [text]="course.fullname"></core-format-text></h2>
|
<h2 float-start><core-format-text [text]="course.fullname"></core-format-text></h2>
|
||||||
<!-- Download course. -->
|
<!-- Download course. -->
|
||||||
<!--<button [hidden]="!downloadButton.isDownload" ion-button icon-only clear color="dark" float-end>
|
<button *ngIf="prefetchCourseData.prefetchCourseIcon != 'spinner'" ion-button icon-only clear color="dark" float-end (click)="prefetchCourse($event)">
|
||||||
<ion-icon name="cloud-download"></ion-icon>
|
<ion-icon [name]="prefetchCourseData.prefetchCourseIcon"></ion-icon>
|
||||||
</button>-->
|
</button>
|
||||||
<!-- Download course spinner. -->
|
<!-- Download course spinner. -->
|
||||||
<!-- <ion-spinner *ngIf="prefetchCourseIcon == 'spinner'" class="core-course-download-spinner"></ion-spinner> -->
|
<ion-spinner *ngIf="prefetchCourseData.prefetchCourseIcon == 'spinner'" class="core-course-download-spinner" float-end></ion-spinner>
|
||||||
</a>
|
</a>
|
||||||
<ion-item text-wrap *ngIf="course.summary && course.summary.length">
|
<ion-item text-wrap *ngIf="course.summary && course.summary.length">
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -12,10 +12,15 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { NavController } from 'ionic-angular';
|
import { NavController } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreEventsProvider } from '../../../../providers/events';
|
||||||
|
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
import { CoreCourseFormatDelegate } from '../../../course/providers/format-delegate';
|
import { CoreCourseFormatDelegate } from '../../../course/providers/format-delegate';
|
||||||
|
import { CoreCourseProvider } from '../../../course/providers/course';
|
||||||
|
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is meant to display a course for a list of courses with progress.
|
* This component is meant to display a course for a list of courses with progress.
|
||||||
|
@ -29,32 +34,52 @@ import { CoreCourseFormatDelegate } from '../../../course/providers/format-deleg
|
||||||
selector: 'core-courses-course-progress',
|
selector: 'core-courses-course-progress',
|
||||||
templateUrl: 'course-progress.html'
|
templateUrl: 'course-progress.html'
|
||||||
})
|
})
|
||||||
export class CoreCoursesCourseProgressComponent implements OnInit {
|
export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy {
|
||||||
@Input() course: any; // The course to render.
|
@Input() course: any; // The course to render.
|
||||||
|
|
||||||
isDownloading: boolean;
|
isDownloading: boolean;
|
||||||
|
prefetchCourseData = {
|
||||||
protected obsStatus;
|
prefetchCourseIcon: 'spinner'
|
||||||
protected downloadText;
|
|
||||||
protected downloadingText;
|
|
||||||
protected downloadButton = {
|
|
||||||
isDownload: true,
|
|
||||||
className: 'core-download-course',
|
|
||||||
priority: 1000
|
|
||||||
};
|
};
|
||||||
protected buttons;
|
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, private translate: TranslateService,
|
protected isDestroyed = false;
|
||||||
private courseFormatDelegate: CoreCourseFormatDelegate) {
|
protected courseStatusObserver;
|
||||||
this.downloadText = this.translate.instant('core.course.downloadcourse');
|
|
||||||
this.downloadingText = this.translate.instant('core.downloading');
|
constructor(private navCtrl: NavController, private translate: TranslateService, private courseHelper: CoreCourseHelperProvider,
|
||||||
|
private courseFormatDelegate: CoreCourseFormatDelegate, private domUtils: CoreDomUtilsProvider,
|
||||||
|
private courseProvider: CoreCourseProvider, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) {
|
||||||
|
// Listen for status change in course.
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// @todo: Handle course prefetch.
|
// Determine course prefetch icon.
|
||||||
|
this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => {
|
||||||
|
this.prefetchCourseData.prefetchCourseIcon = icon;
|
||||||
|
|
||||||
|
if (icon == 'spinner') {
|
||||||
|
// Course is being downloaded. Get the download promise.
|
||||||
|
const promise = this.courseHelper.getCourseDownloadPromise(this.course.id);
|
||||||
|
if (promise) {
|
||||||
|
// There is a download promise. If it fails, show an error.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,4 +89,30 @@ export class CoreCoursesCourseProgressComponent implements OnInit {
|
||||||
this.courseFormatDelegate.openCourse(this.navCtrl, course);
|
this.courseFormatDelegate.openCourse(this.navCtrl, course);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch the course.
|
||||||
|
*
|
||||||
|
* @param {Event} e Click event.
|
||||||
|
*/
|
||||||
|
prefetchCourse(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course).catch((error) => {
|
||||||
|
if (!this.isDestroyed) {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.isDestroyed = true;
|
||||||
|
|
||||||
|
if (this.courseStatusObserver) {
|
||||||
|
this.courseStatusObserver.off();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,22 +17,22 @@
|
||||||
<p *ngIf="course.startdate">{{course.startdate * 1000 | coreFormatDate:"dfdaymonthyear"}} <span *ngIf="course.enddate"> - {{course.enddate * 1000 | coreFormatDate:"dfdaymonthyear"}}</span></p>
|
<p *ngIf="course.startdate">{{course.startdate * 1000 | coreFormatDate:"dfdaymonthyear"}} <span *ngIf="course.enddate"> - {{course.enddate * 1000 | coreFormatDate:"dfdaymonthyear"}}</span></p>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ion-item text-wrap *ngIf="course.summary">
|
<ion-item text-wrap *ngIf="course.summary" detail-none>
|
||||||
<core-format-text [text]="course.summary" maxHeight="120"></core-format-text>
|
<core-format-text [text]="course.summary" maxHeight="120"></core-format-text>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<a ion-item text-wrap *ngIf="course.contacts && course.contacts.length" core-user-link [attr.aria-label]="'core.viewprofile' | translate">
|
<a ion-item text-wrap *ngIf="course.contacts && course.contacts.length" detail-none>
|
||||||
<p class="item-heading">{{ 'core.teachers' | translate }}</p>
|
<p class="item-heading">{{ 'core.teachers' | translate }}</p>
|
||||||
<p *ngFor="let contact of course.contacts">{{contact.fullname}}</p>
|
<p *ngFor="let contact of course.contacts">{{contact.fullname}}</p>
|
||||||
</a>
|
</a>
|
||||||
<core-file *ngFor="let file of course.overviewfiles" [file]="file" [component]="component" [componentId]="course.id"></core-file>
|
<core-file *ngFor="let file of course.overviewfiles" [file]="file" [component]="component" [componentId]="course.id"></core-file>
|
||||||
<div *ngIf="!isEnrolled">
|
<div *ngIf="!isEnrolled" detail-none>
|
||||||
<ion-item text-wrap *ngFor="let instance of selfEnrolInstances">
|
<ion-item text-wrap *ngFor="let instance of selfEnrolInstances">
|
||||||
<p class="item-heading">{{ instance.name }}</p>
|
<p class="item-heading">{{ instance.name }}</p>
|
||||||
<button ion-button block margin-top (click)="selfEnrolClicked(instance.id)">{{ 'core.courses.enrolme' | translate }}</button>
|
<button ion-button block margin-top (click)="selfEnrolClicked(instance.id)">{{ 'core.courses.enrolme' | translate }}</button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
<ion-item text-wrap *ngIf="!isEnrolled && paypalEnabled">
|
<ion-item text-wrap *ngIf="!isEnrolled && paypalEnabled" detail-none>
|
||||||
<p class="item-heading">{{ 'core.courses.paypalaccepted' | translate }}</p>
|
<p class="item-heading">{{ 'core.courses.paypalaccepted' | translate }}</p>
|
||||||
<p>{{ 'core.paymentinstant' | translate }}</p>
|
<p>{{ 'core.paymentinstant' | translate }}</p>
|
||||||
<button ion-button block margin-top (click)="paypalEnrol()">{{ 'core.courses.sendpaymentbutton' | translate }}</button>
|
<button ion-button block margin-top (click)="paypalEnrol()">{{ 'core.courses.sendpaymentbutton' | translate }}</button>
|
||||||
|
@ -40,12 +40,11 @@
|
||||||
<ion-item *ngIf="!isEnrolled && !selfEnrolInstances.length && !paypalEnabled">
|
<ion-item *ngIf="!isEnrolled && !selfEnrolInstances.length && !paypalEnabled">
|
||||||
<p>{{ 'core.courses.notenrollable' | translate }}</p>
|
<p>{{ 'core.courses.notenrollable' | translate }}</p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<!-- @todo: Prefetch course.
|
<a ion-item *ngIf="handlersShouldBeShown" (click)="prefetchCourse()" detail-none>
|
||||||
<a class="item item-icon-left" ng-if="handlersShouldBeShown" ng-click="prefetchCourse()">
|
<ion-icon *ngIf="prefetchCourseData.prefetchCourseIcon != 'spinner'" [name]="prefetchCourseData.prefetchCourseIcon" item-start></ion-icon>
|
||||||
<i ng-if="prefetchCourseIcon != 'spinner'" class="icon {{prefetchCourseIcon}}"></i>
|
<ion-spinner *ngIf="prefetchCourseData.prefetchCourseIcon == 'spinner'" item-start></ion-spinner>
|
||||||
<ion-spinner ng-if="prefetchCourseIcon == 'spinner'" class="icon"></ion-spinner>
|
|
||||||
<h2>{{ 'core.course.downloadcourse' | translate }}</h2>
|
<h2>{{ 'core.course.downloadcourse' | translate }}</h2>
|
||||||
</a> -->
|
</a>
|
||||||
<a ion-item (click)="openCourse()" [title]="course.fullname" *ngIf="handlersShouldBeShown">
|
<a ion-item (click)="openCourse()" [title]="course.fullname" *ngIf="handlersShouldBeShown">
|
||||||
<ion-icon name="briefcase" item-start></ion-icon>
|
<ion-icon name="briefcase" item-start></ion-icon>
|
||||||
<h2>{{ 'core.course.contents' | translate }}</h2>
|
<h2>{{ 'core.course.contents' | translate }}</h2>
|
||||||
|
|
|
@ -22,6 +22,8 @@ import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
|
import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
|
||||||
import { CoreCoursesProvider } from '../../providers/courses';
|
import { CoreCoursesProvider } from '../../providers/courses';
|
||||||
import { CoreCoursesDelegate } from '../../providers/delegate';
|
import { CoreCoursesDelegate } from '../../providers/delegate';
|
||||||
|
import { CoreCourseProvider } from '../../../course/providers/course';
|
||||||
|
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that allows "previewing" a course and enrolling in it if enabled and not enrolled.
|
* Page that allows "previewing" a course and enrolling in it if enabled and not enrolled.
|
||||||
|
@ -40,7 +42,9 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
|
||||||
selfEnrolInstances: any[] = [];
|
selfEnrolInstances: any[] = [];
|
||||||
paypalEnabled: boolean;
|
paypalEnabled: boolean;
|
||||||
dataLoaded: boolean;
|
dataLoaded: boolean;
|
||||||
prefetchCourseIcon: string;
|
prefetchCourseData = {
|
||||||
|
prefetchCourseIcon: 'spinner'
|
||||||
|
};
|
||||||
|
|
||||||
protected guestWSAvailable: boolean;
|
protected guestWSAvailable: boolean;
|
||||||
protected isGuestEnabled: boolean = false;
|
protected isGuestEnabled: boolean = false;
|
||||||
|
@ -55,15 +59,24 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
|
||||||
protected selfEnrolModal: Modal;
|
protected selfEnrolModal: Modal;
|
||||||
protected pageDestroyed = false;
|
protected pageDestroyed = false;
|
||||||
protected currentInstanceId: number;
|
protected currentInstanceId: number;
|
||||||
|
protected courseStatusObserver;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, navParams: NavParams, private sitesProvider: CoreSitesProvider,
|
constructor(private navCtrl: NavController, navParams: NavParams, private sitesProvider: CoreSitesProvider,
|
||||||
private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider, appProvider: CoreAppProvider,
|
private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider, appProvider: CoreAppProvider,
|
||||||
private coursesProvider: CoreCoursesProvider, private platform: Platform, private modalCtrl: ModalController,
|
private coursesProvider: CoreCoursesProvider, private platform: Platform, private modalCtrl: ModalController,
|
||||||
private translate: TranslateService, private eventsProvider: CoreEventsProvider,
|
private translate: TranslateService, private eventsProvider: CoreEventsProvider,
|
||||||
private coursesDelegate: CoreCoursesDelegate) {
|
private coursesDelegate: CoreCoursesDelegate, private courseHelper: CoreCourseHelperProvider,
|
||||||
|
private courseProvider: CoreCourseProvider) {
|
||||||
this.course = navParams.get('course');
|
this.course = navParams.get('course');
|
||||||
this.isMobile = appProvider.isMobile();
|
this.isMobile = appProvider.isMobile();
|
||||||
this.isDesktop = appProvider.isDesktop();
|
this.isDesktop = appProvider.isDesktop();
|
||||||
|
|
||||||
|
// Listen for status change in course.
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +101,26 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getCourse().finally(() => {
|
this.getCourse().finally(() => {
|
||||||
// @todo: Prefetch course.
|
// Determine course prefetch icon.
|
||||||
|
this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => {
|
||||||
|
this.prefetchCourseData.prefetchCourseIcon = icon;
|
||||||
|
|
||||||
|
if (icon == 'spinner') {
|
||||||
|
// Course is being downloaded. Get the download promise.
|
||||||
|
let promise = this.courseHelper.getCourseDownloadPromise(this.course.id);
|
||||||
|
if (promise) {
|
||||||
|
// There is a download promise. If it fails, show an error.
|
||||||
|
promise.catch((error) => {
|
||||||
|
if (!this.pageDestroyed) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +129,10 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
|
||||||
*/
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.pageDestroyed = true;
|
this.pageDestroyed = true;
|
||||||
|
|
||||||
|
if (this.courseStatusObserver) {
|
||||||
|
this.courseStatusObserver.off();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -387,4 +423,16 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch the course.
|
||||||
|
*/
|
||||||
|
prefetchCourse() {
|
||||||
|
this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, undefined, this.course._handlers)
|
||||||
|
.catch((error) => {
|
||||||
|
if (!this.pageDestroyed) {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<ion-icon name="search"></ion-icon>
|
<ion-icon name="search"></ion-icon>
|
||||||
</button>
|
</button>
|
||||||
<core-context-menu>
|
<core-context-menu>
|
||||||
|
<core-context-menu-item [hidden]="!courses || courses.length < 2" [priority]="800" [content]="'core.courses.downloadcourses' | translate" (action)="prefetchCourses()" [iconAction]="prefetchCoursesData.icon" [closeOnClick]="false" [badge]="prefetchCoursesData.badge"></core-context-menu-item>
|
||||||
<core-context-menu-item [hidden]="!courses || courses.length <= 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="switchFilter()" [iconAction]="'funnel'"></core-context-menu-item>
|
<core-context-menu-item [hidden]="!courses || courses.length <= 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="switchFilter()" [iconAction]="'funnel'"></core-context-menu-item>
|
||||||
</core-context-menu>
|
</core-context-menu>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { CoreEventsProvider } from '../../../../providers/events';
|
||||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
import { CoreCoursesProvider } from '../../providers/courses';
|
import { CoreCoursesProvider } from '../../providers/courses';
|
||||||
|
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of courses the user is enrolled in.
|
* Page that displays the list of courses the user is enrolled in.
|
||||||
|
@ -34,14 +35,16 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
|
||||||
filter = '';
|
filter = '';
|
||||||
showFilter = false;
|
showFilter = false;
|
||||||
coursesLoaded = false;
|
coursesLoaded = false;
|
||||||
|
prefetchCoursesData: any = {};
|
||||||
|
|
||||||
protected prefetchIconInitialized = false;
|
protected prefetchIconInitialized = false;
|
||||||
protected myCoursesObserver;
|
protected myCoursesObserver;
|
||||||
protected siteUpdatedObserver;
|
protected siteUpdatedObserver;
|
||||||
|
protected isDestroyed = false;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
||||||
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
|
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
|
||||||
private sitesProvider: CoreSitesProvider) {}
|
private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View loaded.
|
* View loaded.
|
||||||
|
@ -81,7 +84,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
|
||||||
this.filteredCourses = this.courses;
|
this.filteredCourses = this.courses;
|
||||||
this.filter = '';
|
this.filter = '';
|
||||||
|
|
||||||
// this.initPrefetchCoursesIcon();
|
this.initPrefetchCoursesIcon();
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
|
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
|
||||||
|
@ -139,10 +142,60 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch all the courses.
|
||||||
|
*/
|
||||||
|
prefetchCourses() {
|
||||||
|
let initialIcon = this.prefetchCoursesData.icon;
|
||||||
|
|
||||||
|
this.prefetchCoursesData.icon = 'spinner';
|
||||||
|
this.prefetchCoursesData.badge = '';
|
||||||
|
return this.courseHelper.confirmAndPrefetchCourses(this.courses, (progress) => {
|
||||||
|
this.prefetchCoursesData.badge = progress.count + ' / ' + progress.total;
|
||||||
|
}).then((downloaded) => {
|
||||||
|
this.prefetchCoursesData.icon = downloaded ? 'ion-android-refresh' : initialIcon;
|
||||||
|
}, (error) => {
|
||||||
|
if (!this.isDestroyed) {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||||
|
this.prefetchCoursesData.icon = initialIcon;
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.prefetchCoursesData.badge = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the prefetch icon for the list of courses.
|
||||||
|
*/
|
||||||
|
protected initPrefetchCoursesIcon() {
|
||||||
|
if (this.prefetchIconInitialized) {
|
||||||
|
// Already initialized.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prefetchIconInitialized = true;
|
||||||
|
|
||||||
|
if (!this.courses || this.courses.length < 2) {
|
||||||
|
// Not enough courses.
|
||||||
|
this.prefetchCoursesData.icon = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.courseHelper.determineCoursesStatus(this.courses).then((status) => {
|
||||||
|
let icon = this.courseHelper.getCourseStatusIconFromStatus(status);
|
||||||
|
if (icon == 'spinner') {
|
||||||
|
// It seems all courses are being downloaded, show a download button instead.
|
||||||
|
icon = 'cloud-download';
|
||||||
|
}
|
||||||
|
this.prefetchCoursesData.icon = icon;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page destroyed.
|
* Page destroyed.
|
||||||
*/
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
this.isDestroyed = true;
|
||||||
this.myCoursesObserver && this.myCoursesObserver.off();
|
this.myCoursesObserver && this.myCoursesObserver.off();
|
||||||
this.siteUpdatedObserver && this.siteUpdatedObserver.off();
|
this.siteUpdatedObserver && this.siteUpdatedObserver.off();
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,22 +46,30 @@
|
||||||
<!-- Courses tab. -->
|
<!-- Courses tab. -->
|
||||||
<core-tab [title]="'core.courses.courses' | translate" (ionSelect)="tabChanged('courses')">
|
<core-tab [title]="'core.courses.courses' | translate" (ionSelect)="tabChanged('courses')">
|
||||||
<core-loading [hideUntil]="courses.loaded" class="core-loading-center">
|
<core-loading [hideUntil]="courses.loaded" class="core-loading-center">
|
||||||
|
<!-- "Time" selector. -->
|
||||||
<div no-padding class="clearfix" [hidden]="showFilter">
|
<div no-padding class="clearfix" [hidden]="showFilter">
|
||||||
<ion-select [title]="'core.show' | translate" [(ngModel)]="courses.selected" float-start (ngModelChange)="selectedChanged()">
|
<ion-select [title]="'core.show' | translate" [(ngModel)]="courses.selected" float-start (ngModelChange)="selectedChanged()">
|
||||||
<ion-option value="inprogress">{{ 'core.courses.inprogress' | translate }}</ion-option>
|
<ion-option value="inprogress">{{ 'core.courses.inprogress' | translate }}</ion-option>
|
||||||
<ion-option value="future">{{ 'core.courses.future' | translate }}</ion-option>
|
<ion-option value="future">{{ 'core.courses.future' | translate }}</ion-option>
|
||||||
<ion-option value="past">{{ 'core.courses.past' | translate }}</ion-option>
|
<ion-option value="past">{{ 'core.courses.past' | translate }}</ion-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
<button [hidden]="!courses[courses.selected] || !courses[courses.selected].length" ion-button icon-only clear color="dark" float-end>
|
<!-- Download all courses. -->
|
||||||
<ion-icon name="cloud-download"></ion-icon>
|
<div *ngIf="courses[courses.selected] && courses[courses.selected].length > 1">
|
||||||
</button>
|
<button *ngIf="prefetchCoursesData[courses.selected].icon && prefetchCoursesData[courses.selected].icon != 'spinner'" ion-button icon-only clear color="dark" float-end (click)="prefetchCourses()">
|
||||||
|
<ion-icon [name]="prefetchCoursesData[courses.selected].icon"></ion-icon>
|
||||||
|
</button>
|
||||||
|
<ion-spinner *ngIf="!prefetchCoursesData[courses.selected].icon || prefetchCoursesData[courses.selected].icon == 'spinner'" float-end></ion-spinner>
|
||||||
|
<span float-end *ngIf="prefetchCoursesData[courses.selected].badge">{{prefetchCoursesData[courses.selected].badge}}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Filter courses. -->
|
||||||
<div no-padding padding-bottom [hidden]="!showFilter">
|
<div no-padding padding-bottom [hidden]="!showFilter">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
<ion-label><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
||||||
<ion-input type="text" name="filter" clearInput [(ngModel)]="courses.filter" (ngModelChange)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate"></ion-input>
|
<ion-input type="text" name="filter" clearInput [(ngModel)]="courses.filter" (ngModelChange)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate"></ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- List of courses. -->
|
||||||
<div>
|
<div>
|
||||||
<ion-grid no-padding>
|
<ion-grid no-padding>
|
||||||
<ion-row no-padding>
|
<ion-row no-padding>
|
||||||
|
|
|
@ -12,11 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
import { IonicPage, NavController } from 'ionic-angular';
|
import { IonicPage, NavController } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
import { CoreCoursesProvider } from '../../providers/courses';
|
import { CoreCoursesProvider } from '../../providers/courses';
|
||||||
import { CoreCoursesMyOverviewProvider } from '../../providers/my-overview';
|
import { CoreCoursesMyOverviewProvider } from '../../providers/my-overview';
|
||||||
|
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +28,7 @@ import * as moment from 'moment';
|
||||||
selector: 'page-core-courses-my-overview',
|
selector: 'page-core-courses-my-overview',
|
||||||
templateUrl: 'my-overview.html',
|
templateUrl: 'my-overview.html',
|
||||||
})
|
})
|
||||||
export class CoreCoursesMyOverviewPage {
|
export class CoreCoursesMyOverviewPage implements OnDestroy {
|
||||||
tabShown = 'courses';
|
tabShown = 'courses';
|
||||||
timeline = {
|
timeline = {
|
||||||
sort: 'sortbydates',
|
sort: 'sortbydates',
|
||||||
|
@ -52,21 +53,24 @@ export class CoreCoursesMyOverviewPage {
|
||||||
searchEnabled: boolean;
|
searchEnabled: boolean;
|
||||||
filteredCourses: any[];
|
filteredCourses: any[];
|
||||||
tabs = [];
|
tabs = [];
|
||||||
|
prefetchCoursesData = {
|
||||||
|
inprogress: {},
|
||||||
|
past: {},
|
||||||
|
future: {}
|
||||||
|
};
|
||||||
|
|
||||||
protected prefetchIconInitialized = false;
|
protected prefetchIconsInitialized = false;
|
||||||
protected myCoursesObserver;
|
protected isDestroyed;
|
||||||
protected siteUpdatedObserver;
|
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
||||||
private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider) {}
|
private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider,
|
||||||
|
private courseHelper: CoreCourseHelperProvider) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View loaded.
|
* View loaded.
|
||||||
*/
|
*/
|
||||||
ionViewDidLoad() {
|
ionViewDidLoad() {
|
||||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||||
|
|
||||||
// @todo: Course download.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,6 +148,8 @@ export class CoreCoursesMyOverviewPage {
|
||||||
this.courses.filter = '';
|
this.courses.filter = '';
|
||||||
this.showFilter = false;
|
this.showFilter = false;
|
||||||
this.filteredCourses = this.courses[this.courses.selected];
|
this.filteredCourses = this.courses[this.courses.selected];
|
||||||
|
|
||||||
|
this.initPrefetchCoursesIcons();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'Error getting my overview data.');
|
this.domUtils.showErrorModalDefault(error, 'Error getting my overview data.');
|
||||||
});
|
});
|
||||||
|
@ -229,6 +235,7 @@ export class CoreCoursesMyOverviewPage {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'courses':
|
case 'courses':
|
||||||
|
this.prefetchIconsInitialized = false;
|
||||||
return this.fetchMyOverviewCourses();
|
return this.fetchMyOverviewCourses();
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
@ -315,4 +322,65 @@ export class CoreCoursesMyOverviewPage {
|
||||||
selectedChanged() {
|
selectedChanged() {
|
||||||
this.filteredCourses = this.courses[this.courses.selected];
|
this.filteredCourses = this.courses[this.courses.selected];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch all the shown courses.
|
||||||
|
*/
|
||||||
|
prefetchCourses() {
|
||||||
|
let selected = this.courses.selected,
|
||||||
|
selectedData = this.prefetchCoursesData[selected],
|
||||||
|
initialIcon = selectedData.icon;
|
||||||
|
|
||||||
|
selectedData.icon = 'spinner';
|
||||||
|
selectedData.badge = '';
|
||||||
|
return this.courseHelper.confirmAndPrefetchCourses(this.courses[selected], (progress) => {
|
||||||
|
selectedData.badge = progress.count + ' / ' + progress.total;
|
||||||
|
}).then((downloaded) => {
|
||||||
|
selectedData.icon = downloaded ? 'refresh' : initialIcon;
|
||||||
|
}, (error) => {
|
||||||
|
if (!this.isDestroyed) {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||||
|
selectedData.icon = initialIcon;
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
selectedData.badge = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the prefetch icon for selected courses.
|
||||||
|
*/
|
||||||
|
protected initPrefetchCoursesIcons() {
|
||||||
|
if (this.prefetchIconsInitialized) {
|
||||||
|
// Already initialized.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prefetchIconsInitialized = true;
|
||||||
|
|
||||||
|
Object.keys(this.prefetchCoursesData).forEach((filter) => {
|
||||||
|
if (!this.courses[filter] || this.courses[filter].length < 2) {
|
||||||
|
// Not enough courses.
|
||||||
|
this.prefetchCoursesData[filter].icon = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.courseHelper.determineCoursesStatus(this.courses[filter]).then((status) => {
|
||||||
|
let icon = this.courseHelper.getCourseStatusIconFromStatus(status);
|
||||||
|
if (icon == 'spinner') {
|
||||||
|
// It seems all courses are being downloaded, show a download button instead.
|
||||||
|
icon = 'cloud-download';
|
||||||
|
}
|
||||||
|
this.prefetchCoursesData[filter].icon = icon;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.isDestroyed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue