MOBILE-2975 blocks: Allow disabling blocks and always show courses
parent
c6e01bfa66
commit
8723e5b684
|
@ -15,6 +15,7 @@
|
|||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSite } from '@classes/site';
|
||||
|
||||
export interface CoreDelegateHandler {
|
||||
/**
|
||||
|
@ -272,10 +273,10 @@ export class CoreDelegate {
|
|||
* Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name.
|
||||
*
|
||||
* @param {CoreDelegateHandler} handler Handler to check.
|
||||
* @param {any} site Site to check.
|
||||
* @return {boolean} Whether is enabled or disabled in site.
|
||||
* @param {CoreSite} site Site to check.
|
||||
* @return {boolean} Whether is enabled or disabled in site.
|
||||
*/
|
||||
protected isFeatureDisabled(handler: CoreDelegateHandler, site: any): boolean {
|
||||
protected isFeatureDisabled(handler: CoreDelegateHandler, site: CoreSite): boolean {
|
||||
return typeof this.featurePrefix != 'undefined' && site.isFeatureDisabled(this.featurePrefix + handler.name);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||
import { CoreBlockDefaultHandler } from './default-block-handler';
|
||||
import { CoreSite } from '@classes/site';
|
||||
|
||||
/**
|
||||
* Interface that all blocks must implement.
|
||||
|
@ -87,6 +88,30 @@ export class CoreBlockDelegate extends CoreDelegate {
|
|||
super('CoreBlockDelegate', logger, sitesProvider, eventsProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if blocks are disabled in a certain site.
|
||||
*
|
||||
* @param {CoreSite} [site] Site. If not defined, use current site.
|
||||
* @return {boolean} Whether it's disabled.
|
||||
*/
|
||||
areBlocksDisabledInSite(site?: CoreSite): boolean {
|
||||
site = site || this.sitesProvider.getCurrentSite();
|
||||
|
||||
return site.isFeatureDisabled('NoDelegate_SiteBlocks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if blocks are disabled in a certain site.
|
||||
*
|
||||
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
areBlocksDisabled(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return this.areBlocksDisabledInSite(site);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display data for a certain block.
|
||||
*
|
||||
|
@ -121,4 +146,15 @@ export class CoreBlockDelegate extends CoreDelegate {
|
|||
isBlockSupported(name: string): boolean {
|
||||
return this.hasHandler(name, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if feature is enabled or disabled in the site, depending on the feature prefix and the handler name.
|
||||
*
|
||||
* @param {CoreDelegateHandler} handler Handler to check.
|
||||
* @param {CoreSite} site Site to check.
|
||||
* @return {boolean} Whether is enabled or disabled in site.
|
||||
*/
|
||||
protected isFeatureDisabled(handler: CoreDelegateHandler, site: CoreSite): boolean {
|
||||
return this.areBlocksDisabledInSite(site) || super.isFeatureDisabled(handler, site);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,15 +19,17 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { CoreCoursesCourseProgressComponent } from '../components/course-progress/course-progress';
|
||||
import { CoreCoursesCourseListItemComponent } from '../components/course-list-item/course-list-item';
|
||||
import { CoreCoursesCourseOptionsMenuComponent } from '../components/course-options-menu/course-options-menu';
|
||||
import { CoreCoursesCourseProgressComponent } from './course-progress/course-progress';
|
||||
import { CoreCoursesCourseListItemComponent } from './course-list-item/course-list-item';
|
||||
import { CoreCoursesCourseOptionsMenuComponent } from './course-options-menu/course-options-menu';
|
||||
import { CoreCoursesMyCoursesComponent } from './my-courses/my-courses';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCoursesCourseProgressComponent,
|
||||
CoreCoursesCourseListItemComponent,
|
||||
CoreCoursesCourseOptionsMenuComponent
|
||||
CoreCoursesCourseOptionsMenuComponent,
|
||||
CoreCoursesMyCoursesComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -42,7 +44,8 @@ import { CoreCoursesCourseOptionsMenuComponent } from '../components/course-opti
|
|||
exports: [
|
||||
CoreCoursesCourseProgressComponent,
|
||||
CoreCoursesCourseListItemComponent,
|
||||
CoreCoursesCourseOptionsMenuComponent
|
||||
CoreCoursesCourseOptionsMenuComponent,
|
||||
CoreCoursesMyCoursesComponent
|
||||
],
|
||||
entryComponents: [
|
||||
CoreCoursesCourseOptionsMenuComponent
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<core-loading [hideUntil]="coursesLoaded">
|
||||
<ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="filter" (ionInput)="filterChanged($event)" (ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate">
|
||||
</ion-searchbar>
|
||||
<ion-grid no-padding>
|
||||
<ion-row no-padding>
|
||||
<ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch>
|
||||
<core-courses-course-progress [course]="course" class="core-courseoverview" showAll="true"></core-courses-course-progress>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<core-empty-box *ngIf="!courses || !courses.length" icon="ionic" [message]="'core.courses.nocourses' | translate">
|
||||
<p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p>
|
||||
</core-empty-box>
|
||||
</core-loading>
|
|
@ -0,0 +1,242 @@
|
|||
// (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 { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { Searchbar } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
import { CoreCoursesHelperProvider } from '../../providers/helper';
|
||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
|
||||
/**
|
||||
* Component that displays the list of courses the user is enrolled in.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-courses-my-courses',
|
||||
templateUrl: 'my-courses.html',
|
||||
})
|
||||
export class CoreCoursesMyCoursesComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('searchbar') searchbar: Searchbar;
|
||||
|
||||
courses: any[];
|
||||
filteredCourses: any[];
|
||||
searchEnabled: boolean;
|
||||
filter = '';
|
||||
showFilter = false;
|
||||
coursesLoaded = false;
|
||||
prefetchCoursesData: any = {};
|
||||
downloadAllCoursesEnabled: boolean;
|
||||
|
||||
protected prefetchIconInitialized = false;
|
||||
protected myCoursesObserver;
|
||||
protected siteUpdatedObserver;
|
||||
protected isDestroyed = false;
|
||||
protected courseIds = '';
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
|
||||
private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider,
|
||||
private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider) { }
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
this.fetchCourses().finally(() => {
|
||||
this.coursesLoaded = true;
|
||||
});
|
||||
|
||||
this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => {
|
||||
this.fetchCourses();
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
// Refresh the enabled flags if site is updated.
|
||||
this.siteUpdatedObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => {
|
||||
const wasEnabled = this.downloadAllCoursesEnabled;
|
||||
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
if (!wasEnabled && this.downloadAllCoursesEnabled && this.coursesLoaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcon();
|
||||
}
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the user courses.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchCourses(): Promise<any> {
|
||||
return this.coursesProvider.getUserCourses().then((courses) => {
|
||||
const promises = [],
|
||||
courseIds = courses.map((course) => {
|
||||
return course.id;
|
||||
});
|
||||
|
||||
this.courseIds = courseIds.join(',');
|
||||
|
||||
promises.push(this.coursesHelper.loadCoursesExtraInfo(courses));
|
||||
|
||||
if (this.coursesProvider.canGetAdminAndNavOptions()) {
|
||||
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
|
||||
courses.forEach((course) => {
|
||||
course.navOptions = options.navOptions[course.id];
|
||||
course.admOptions = options.admOptions[course.id];
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
this.courses = courses;
|
||||
this.filteredCourses = this.courses;
|
||||
this.filter = '';
|
||||
|
||||
this.initPrefetchCoursesIcon();
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the courses.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshCourses(refresher: any): void {
|
||||
const promises = [];
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses());
|
||||
promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions());
|
||||
if (this.courseIds) {
|
||||
promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds));
|
||||
}
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
|
||||
this.prefetchIconInitialized = false;
|
||||
this.fetchCourses().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the filter.
|
||||
*/
|
||||
switchFilter(): void {
|
||||
this.filter = '';
|
||||
this.showFilter = !this.showFilter;
|
||||
this.filteredCourses = this.courses;
|
||||
if (this.showFilter) {
|
||||
setTimeout(() => {
|
||||
this.searchbar.setFocus();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter has changed.
|
||||
*
|
||||
* @param {any} Received Event.
|
||||
*/
|
||||
filterChanged(event: any): void {
|
||||
const newValue = event.target.value && event.target.value.trim().toLowerCase();
|
||||
if (!newValue || !this.courses) {
|
||||
this.filteredCourses = this.courses;
|
||||
} else {
|
||||
// Use displayname if avalaible, or fullname if not.
|
||||
if (this.courses.length > 0 && typeof this.courses[0].displayname != 'undefined') {
|
||||
this.filteredCourses = this.courses.filter((course) => {
|
||||
return course.displayname.toLowerCase().indexOf(newValue) > -1;
|
||||
});
|
||||
} else {
|
||||
this.filteredCourses = this.courses.filter((course) => {
|
||||
return course.fullname.toLowerCase().indexOf(newValue) > -1;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the courses.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetchCourses(): Promise<any> {
|
||||
const 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(() => {
|
||||
this.prefetchCoursesData.icon = 'ion-android-refresh';
|
||||
}).catch((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(): void {
|
||||
if (this.prefetchIconInitialized || !this.downloadAllCoursesEnabled) {
|
||||
// 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.getCourseStatusIconAndTitleFromStatus(status).icon;
|
||||
if (icon == 'spinner') {
|
||||
// It seems all courses are being downloaded, show a download button instead.
|
||||
icon = 'cloud-download';
|
||||
}
|
||||
this.prefetchCoursesData.icon = icon;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.myCoursesObserver && this.myCoursesObserver.off();
|
||||
this.siteUpdatedObserver && this.siteUpdatedObserver.off();
|
||||
}
|
||||
}
|
|
@ -7,7 +7,12 @@
|
|||
<ion-icon name="search"></ion-icon>
|
||||
</button>
|
||||
<core-context-menu>
|
||||
<core-context-menu-item *ngIf="downloadCourseEnabled || downloadCoursesEnabled" [priority]="1000" [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()" [iconAction]="downloadEnabledIcon"></core-context-menu-item>
|
||||
<!-- Action for dashboard and site home. -->
|
||||
<core-context-menu-item *ngIf="(siteHomeEnabled || dashboardEnabled) && (downloadCourseEnabled || downloadCoursesEnabled)" [priority]="1000" [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()" [iconAction]="downloadEnabledIcon"></core-context-menu-item>
|
||||
|
||||
<!-- Actions when both site home and dashboard are disabled. -->
|
||||
<core-context-menu-item *ngIf="!siteHomeEnabled && !dashboardEnabled && mcComponent && mcComponent.downloadAllCoursesEnabled && mcComponent.courses && mcComponent.courses.length >= 2" [priority]="800" [content]="'core.courses.downloadcourses' | translate" (action)="mcComponent.prefetchCourses()" [iconAction]="mcComponent.prefetchCoursesData.icon" [closeOnClick]="false" [badge]="mcComponent.prefetchCoursesData.badge"></core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="!siteHomeEnabled && !dashboardEnabled && mcComponent && mcComponent.courses && mcComponent.courses.length > 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="mcComponent.switchFilter()" [iconAction]="'funnel'"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
|
@ -46,5 +51,17 @@
|
|||
</ion-content>
|
||||
</ng-template>
|
||||
</core-tab>
|
||||
|
||||
<!-- Tab to display if both site home and dashboard are disabled. -->
|
||||
<core-tab [show]="!siteHomeEnabled && !dashboardEnabled" [title]="'core.courses.mymoodle' | translate">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="mcComponent && mcComponent.coursesLoaded" (ionRefresh)="refreshMyCourses($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-courses-my-courses></core-courses-my-courses>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</core-tab>
|
||||
</core-tabs>
|
||||
</ion-content>
|
|
@ -24,6 +24,7 @@ import { CoreSiteHomeProvider } from '@core/sitehome/providers/sitehome';
|
|||
import { CoreSiteHomeIndexComponent } from '@core/sitehome/components/index/index';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
import { CoreCoursesDashboardProvider } from '../../providers/dashboard';
|
||||
import { CoreCoursesMyCoursesComponent } from '../../components/my-courses/my-courses';
|
||||
|
||||
/**
|
||||
* Page that displays the dashboard.
|
||||
|
@ -37,6 +38,7 @@ export class CoreCoursesDashboardPage implements OnDestroy {
|
|||
@ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent;
|
||||
@ViewChild(CoreSiteHomeIndexComponent) siteHomeComponent: CoreSiteHomeIndexComponent;
|
||||
@ViewChildren(CoreBlockComponent) blocksComponents: QueryList<CoreBlockComponent>;
|
||||
@ViewChild(CoreCoursesMyCoursesComponent) mcComponent: CoreCoursesMyCoursesComponent;
|
||||
|
||||
firstSelectedTab: number;
|
||||
siteHomeEnabled = false;
|
||||
|
@ -186,6 +188,26 @@ export class CoreCoursesDashboardPage implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the dashboard data and My Courses.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshMyCourses(refresher: any): void {
|
||||
// First of all, refresh dashboard blocks, maybe a new block was added and now we can display the dashboard.
|
||||
this.dashboardProvider.invalidateDashboardBlocks().finally(() => {
|
||||
return this.loadDashboardContent();
|
||||
}).finally(() => {
|
||||
if (!this.dashboardEnabled) {
|
||||
// Dashboard still not enabled. Refresh my courses.
|
||||
this.mcComponent && this.mcComponent.refreshCourses(refresher);
|
||||
} else {
|
||||
this.tabsComponent.selectTab(1);
|
||||
refresher.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle download enabled.
|
||||
*/
|
||||
|
|
|
@ -3,33 +3,20 @@
|
|||
<ion-title>{{ 'core.courses.mycourses' | translate }}</ion-title>
|
||||
|
||||
<ion-buttons end>
|
||||
<button *ngIf="searchEnabled" ion-button icon-only (click)="openSearch()" [attr.aria-label]="'core.courses.searchcourses' | translate">
|
||||
<button *ngIf="mcComponent && mcComponent.searchEnabled" ion-button icon-only (click)="openSearch()" [attr.aria-label]="'core.courses.searchcourses' | translate">
|
||||
<ion-icon name="search"></ion-icon>
|
||||
</button>
|
||||
<core-context-menu>
|
||||
<core-context-menu-item [hidden]="!downloadAllCoursesEnabled || !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 *ngIf="mcComponent">
|
||||
<core-context-menu-item [hidden]="!mcComponent.downloadAllCoursesEnabled || !mcComponent.courses || mcComponent.courses.length < 2" [priority]="800" [content]="'core.courses.downloadcourses' | translate" (action)="mcComponent.prefetchCourses()" [iconAction]="mcComponent.prefetchCoursesData.icon" [closeOnClick]="false" [badge]="mcComponent.prefetchCoursesData.badge"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!mcComponent.courses || mcComponent.courses.length <= 5" [priority]="700" [content]="'core.courses.filtermycourses' | translate" (action)="mcComponent.switchFilter()" [iconAction]="'funnel'"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="coursesLoaded" (ionRefresh)="refreshCourses($event)">
|
||||
<ion-refresher [enabled]="mcComponent && mcComponent.coursesLoaded" (ionRefresh)="mcComponent.refreshCourses($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-loading [hideUntil]="coursesLoaded">
|
||||
<ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="filter" (ionInput)="filterChanged($event)" (ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate">
|
||||
</ion-searchbar>
|
||||
<ion-grid no-padding>
|
||||
<ion-row no-padding>
|
||||
<ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch>
|
||||
<core-courses-course-progress [course]="course" class="core-courseoverview" showAll="true"></core-courses-course-progress>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<core-empty-box *ngIf="!courses || !courses.length" icon="ionic" [message]="'core.courses.nocourses' | translate">
|
||||
<p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p>
|
||||
</core-empty-box>
|
||||
</core-loading>
|
||||
<core-courses-my-courses></core-courses-my-courses>
|
||||
</ion-content>
|
||||
|
|
|
@ -12,15 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { IonicPage, Searchbar, NavController } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
import { CoreCoursesHelperProvider } from '../../providers/helper';
|
||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { IonicPage, NavController } from 'ionic-angular';
|
||||
import { CoreCoursesMyCoursesComponent } from '../../components/my-courses/my-courses';
|
||||
|
||||
/**
|
||||
* Page that displays the list of courses the user is enrolled in.
|
||||
|
@ -30,131 +24,10 @@ import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delega
|
|||
selector: 'page-core-courses-my-courses',
|
||||
templateUrl: 'my-courses.html',
|
||||
})
|
||||
export class CoreCoursesMyCoursesPage implements OnDestroy {
|
||||
@ViewChild('searchbar') searchbar: Searchbar;
|
||||
export class CoreCoursesMyCoursesPage {
|
||||
@ViewChild(CoreCoursesMyCoursesComponent) mcComponent: CoreCoursesMyCoursesComponent;
|
||||
|
||||
courses: any[];
|
||||
filteredCourses: any[];
|
||||
searchEnabled: boolean;
|
||||
filter = '';
|
||||
showFilter = false;
|
||||
coursesLoaded = false;
|
||||
prefetchCoursesData: any = {};
|
||||
downloadAllCoursesEnabled: boolean;
|
||||
|
||||
protected prefetchIconInitialized = false;
|
||||
protected myCoursesObserver;
|
||||
protected siteUpdatedObserver;
|
||||
protected isDestroyed = false;
|
||||
protected courseIds = '';
|
||||
|
||||
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
|
||||
private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider,
|
||||
private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider) { }
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
this.fetchCourses().finally(() => {
|
||||
this.coursesLoaded = true;
|
||||
});
|
||||
|
||||
this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => {
|
||||
this.fetchCourses();
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
// Refresh the enabled flags if site is updated.
|
||||
this.siteUpdatedObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => {
|
||||
const wasEnabled = this.downloadAllCoursesEnabled;
|
||||
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
this.downloadAllCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
if (!wasEnabled && this.downloadAllCoursesEnabled && this.coursesLoaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcon();
|
||||
}
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the user courses.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected fetchCourses(): Promise<any> {
|
||||
return this.coursesProvider.getUserCourses().then((courses) => {
|
||||
const promises = [],
|
||||
courseIds = courses.map((course) => {
|
||||
return course.id;
|
||||
});
|
||||
|
||||
this.courseIds = courseIds.join(',');
|
||||
|
||||
promises.push(this.coursesHelper.loadCoursesExtraInfo(courses));
|
||||
|
||||
if (this.coursesProvider.canGetAdminAndNavOptions()) {
|
||||
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
|
||||
courses.forEach((course) => {
|
||||
course.navOptions = options.navOptions[course.id];
|
||||
course.admOptions = options.admOptions[course.id];
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
this.courses = courses;
|
||||
this.filteredCourses = this.courses;
|
||||
this.filter = '';
|
||||
|
||||
this.initPrefetchCoursesIcon();
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the courses.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshCourses(refresher: any): void {
|
||||
const promises = [];
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses());
|
||||
promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions());
|
||||
if (this.courseIds) {
|
||||
promises.push(this.coursesProvider.invalidateCoursesByField('ids', this.courseIds));
|
||||
}
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
|
||||
this.prefetchIconInitialized = false;
|
||||
this.fetchCourses().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the filter.
|
||||
*/
|
||||
switchFilter(): void {
|
||||
this.filter = '';
|
||||
this.showFilter = !this.showFilter;
|
||||
this.filteredCourses = this.courses;
|
||||
if (this.showFilter) {
|
||||
setTimeout(() => {
|
||||
this.searchbar.setFocus();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
constructor(private navCtrl: NavController) { }
|
||||
|
||||
/**
|
||||
* Go to search courses.
|
||||
|
@ -162,89 +35,4 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
|
|||
openSearch(): void {
|
||||
this.navCtrl.push('CoreCoursesSearchPage');
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter has changed.
|
||||
*
|
||||
* @param {any} Received Event.
|
||||
*/
|
||||
filterChanged(event: any): void {
|
||||
const newValue = event.target.value && event.target.value.trim().toLowerCase();
|
||||
if (!newValue || !this.courses) {
|
||||
this.filteredCourses = this.courses;
|
||||
} else {
|
||||
// Use displayname if avalaible, or fullname if not.
|
||||
if (this.courses.length > 0 && typeof this.courses[0].displayname != 'undefined') {
|
||||
this.filteredCourses = this.courses.filter((course) => {
|
||||
return course.displayname.toLowerCase().indexOf(newValue) > -1;
|
||||
});
|
||||
} else {
|
||||
this.filteredCourses = this.courses.filter((course) => {
|
||||
return course.fullname.toLowerCase().indexOf(newValue) > -1;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the courses.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
prefetchCourses(): Promise<any> {
|
||||
const 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(() => {
|
||||
this.prefetchCoursesData.icon = 'ion-android-refresh';
|
||||
}).catch((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(): void {
|
||||
if (this.prefetchIconInitialized || !this.downloadAllCoursesEnabled) {
|
||||
// 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.getCourseStatusIconAndTitleFromStatus(status).icon;
|
||||
if (icon == 'spinner') {
|
||||
// It seems all courses are being downloaded, show a download button instead.
|
||||
icon = 'cloud-download';
|
||||
}
|
||||
this.prefetchCoursesData.icon = icon;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.myCoursesObserver && this.myCoursesObserver.off();
|
||||
this.siteUpdatedObserver && this.siteUpdatedObserver.off();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue