MOBILE-2302 courses: Implement categories and available courses
parent
2b8b5b8b87
commit
cacab75855
|
@ -0,0 +1,16 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.courses.availablecourses' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="coursesLoaded" (ionRefresh)="refreshCourses($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="coursesLoaded">
|
||||
<div *ngIf="courses.length > 0">
|
||||
<core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item>
|
||||
</div>
|
||||
<core-empty-box *ngIf="!courses.length" icon="ionic" [message]="'core.courses.nocourses' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreCoursesAvailableCoursesPage } from './available-courses';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCoursesAvailableCoursesPage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreCoursesComponentsModule,
|
||||
IonicPageModule.forChild(CoreCoursesAvailableCoursesPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreCoursesAvailableCoursesPageModule {}
|
|
@ -0,0 +1,76 @@
|
|||
// (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 } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
|
||||
/**
|
||||
* Page that displays available courses in current site.
|
||||
*/
|
||||
@IonicPage()
|
||||
@Component({
|
||||
selector: 'page-core-courses-available-courses',
|
||||
templateUrl: 'available-courses.html',
|
||||
})
|
||||
export class CoreCoursesAvailableCoursesPage {
|
||||
courses: any[] = [];
|
||||
coursesLoaded: boolean;
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private sitesProvider: CoreSitesProvider) {}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad() {
|
||||
this.loadCourses().finally(() => {
|
||||
this.coursesLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the courses.
|
||||
*/
|
||||
protected loadCourses() {
|
||||
const frontpageCourseId = this.sitesProvider.getCurrentSite().getSiteHomeId();
|
||||
return this.coursesProvider.getCoursesByField().then((courses) => {
|
||||
this.courses = courses.filter((course) => {
|
||||
return course.id != frontpageCourseId;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the courses.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshCourses(refresher: any) {
|
||||
let promises = [];
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses());
|
||||
promises.push(this.coursesProvider.invalidateCoursesByField());
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.loadCourses().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="categoriesLoaded" (ionRefresh)="refreshCategories($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="categoriesLoaded">
|
||||
<ion-item *ngIf="currentCategory" text-wrap>
|
||||
<ion-icon name="folder" item-start></ion-icon>
|
||||
<h2><core-format-text [text]="currentCategory.name"></core-format-text></h2>
|
||||
</ion-item>
|
||||
<ion-item text-wrap *ngIf="currentCategory && currentCategory.description">
|
||||
<core-format-text [text]="currentCategory.description" maxHeight="60"></core-format-text>
|
||||
</ion-item>
|
||||
|
||||
<div *ngIf="categories.length > 0">
|
||||
<ion-item-divider color="light">{{ 'core.courses.categories' | translate }}</ion-item-divider>
|
||||
<section *ngFor="let category of categories">
|
||||
<a ion-item text-wrap (click)="openCategory(category.id)" [title]="category.name">
|
||||
<ion-icon name="folder" item-start></ion-icon>
|
||||
<h2><core-format-text [text]="category.name"></core-format-text></h2>
|
||||
<ion-badge item-end *ngIf="category.coursecount > 0" color="light">{{category.coursecount}}</ion-badge>
|
||||
<ion-icon item-end name="arrow-forward" md="ios-arrow-forward" class="icon-accessory"></ion-icon>
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div *ngIf="courses.length > 0">
|
||||
<ion-item-divider color="light">{{ 'core.courses.courses' | translate }}</ion-item-divider>
|
||||
<core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item>
|
||||
</div>
|
||||
<core-empty-box *ngIf="!categories.length && !courses.length" icon="ionic" [message]="'core.courses.nocoursesyet' | translate">
|
||||
<p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p>
|
||||
</core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,35 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreCoursesCategoriesPage } from './categories';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCoursesCategoriesPage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreCoursesComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
IonicPageModule.forChild(CoreCoursesCategoriesPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreCoursesCategoriesPageModule {}
|
|
@ -0,0 +1,122 @@
|
|||
// (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 } from '@angular/core';
|
||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '../../../../providers/utils/utils';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
|
||||
/**
|
||||
* Page that displays a list of categories and the courses in the current category if any.
|
||||
*/
|
||||
@IonicPage()
|
||||
@Component({
|
||||
selector: 'page-core-courses-categories',
|
||||
templateUrl: 'categories.html',
|
||||
})
|
||||
export class CoreCoursesCategoriesPage {
|
||||
title: string;
|
||||
currentCategory: any;
|
||||
categories: any[] = [];
|
||||
courses: any[] = [];
|
||||
categoriesLoaded: boolean;
|
||||
|
||||
protected categoryId: number;
|
||||
|
||||
constructor(private navCtrl: NavController, navParams: NavParams, private coursesProvider: CoreCoursesProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider, translate: TranslateService,
|
||||
private sitesProvider: CoreSitesProvider) {
|
||||
this.categoryId = navParams.get('categoryId') || 0;
|
||||
this.title = translate.instant('core.courses.categories');
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad() {
|
||||
this.fetchCategories().finally(() => {
|
||||
this.categoriesLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the categories.
|
||||
*/
|
||||
protected fetchCategories() {
|
||||
return this.coursesProvider.getCategories(this.categoryId, true).then((cats) => {
|
||||
this.currentCategory = undefined;
|
||||
|
||||
cats.forEach((cat, index) => {
|
||||
if (cat.id == this.categoryId) {
|
||||
this.currentCategory = cat;
|
||||
// Delete current Category to avoid problems with the formatTree.
|
||||
delete cats[index];
|
||||
}
|
||||
});
|
||||
|
||||
// Sort by depth and sortorder to avoid problems formatting Tree.
|
||||
cats.sort((a,b) => {
|
||||
if (a.depth == b.depth) {
|
||||
return (a.sortorder > b.sortorder) ? 1 : ((b.sortorder > a.sortorder) ? -1 : 0);
|
||||
}
|
||||
return a.depth > b.depth ? 1 : -1;
|
||||
});
|
||||
|
||||
this.categories = this.utils.formatTree(cats, 'parent', 'id', this.categoryId);
|
||||
|
||||
if (this.currentCategory) {
|
||||
this.title = this.currentCategory.name;
|
||||
|
||||
return this.coursesProvider.getCoursesByField('category', this.categoryId).then((courses) => {
|
||||
this.courses = courses;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.courses.errorloadcategories', true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the categories.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
refreshCategories(refresher: any) {
|
||||
let promises = [];
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses());
|
||||
promises.push(this.coursesProvider.invalidateCategories(this.categoryId, true));
|
||||
promises.push(this.coursesProvider.invalidateCoursesByField('category', this.categoryId));
|
||||
promises.push(this.sitesProvider.getCurrentSite().invalidateConfig());
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.fetchCategories().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Open a category.
|
||||
*
|
||||
* @param {number} categoryId The category ID.
|
||||
*/
|
||||
openCategory(categoryId: number) {
|
||||
this.navCtrl.push('CoreCoursesCategoriesPage', {categoryId: categoryId});
|
||||
}
|
||||
}
|
|
@ -151,6 +151,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
*/
|
||||
protected formatAndRenderContents() : void {
|
||||
if (!this.text) {
|
||||
this.element.innerHTML = ''; // Remove current contents.
|
||||
this.finishRender();
|
||||
return;
|
||||
}
|
||||
|
@ -158,6 +159,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
this.text = this.text.trim();
|
||||
|
||||
this.formatContents().then((div: HTMLElement) => {
|
||||
this.element.innerHTML = ''; // Remove current contents.
|
||||
|
||||
if (this.maxHeight && div.innerHTML != "") {
|
||||
// Move the children to the current element to be able to calculate the height.
|
||||
// @todo: Display the element?
|
||||
|
|
Loading…
Reference in New Issue