MOBILE-2302 courses: Implement My Courses
parent
e778448824
commit
2d29cc2da6
102
src/app/app.scss
102
src/app/app.scss
|
@ -113,3 +113,105 @@ ion-avatar ion-img, ion-avatar img {
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
/** Format Text */
|
||||
core-format-text[maxHeight], *[core-format-text][maxHeight] {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
/* Force display inline */
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// This is to allow clicks in radio/checkbox content.
|
||||
&.mm-text-formatted {
|
||||
cursor: pointer;
|
||||
|
||||
.mm-show-more {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:not(.mm-shortened) {
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
&.mm-shortened {
|
||||
color: $gray-darker;
|
||||
overflow: hidden;
|
||||
min-height: 50px;
|
||||
|
||||
.mm-show-more {
|
||||
color: color($colors, dark);
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1001;
|
||||
background-color: $white;
|
||||
padding-left: 10px;
|
||||
|
||||
/* @todo
|
||||
&:after {
|
||||
@extend .ion;
|
||||
content: $ionicon-var-chevron-down;
|
||||
margin-left: 10px;
|
||||
color: $item-icon-accessory-color;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
&.mm-expand-in-fullview .mm-show-more:after {
|
||||
// content: $ionicon-var-chevron-right; @todo
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px));
|
||||
background: -webkit-gradient(left top, left bottom, color-stop(calc(100% - 50px), rgba(255, 255, 255, 0)), color-stop(calc(100% - 15px), white));
|
||||
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px));
|
||||
background: -o-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px));
|
||||
background: -ms-linear-gradient(top, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px));
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) calc(100% - 50px), white calc(100% - 15px));
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core-format-text, *[core-format-text] {
|
||||
audio, video, a, iframe {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
// Fix lists styles in core-format-text.
|
||||
ul, ol {
|
||||
-webkit-padding-start: 40px;
|
||||
}
|
||||
ul {
|
||||
list-style: disc;
|
||||
}
|
||||
ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: initial !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Message item.
|
||||
.item-message {
|
||||
core-format-text > p:only-child {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -482,7 +482,14 @@ export class CoreSite {
|
|||
|
||||
// We pass back a clone of the original object, this may
|
||||
// prevent errors if in the callback the object is modified.
|
||||
return Object.assign({}, response);
|
||||
if (typeof response == 'object') {
|
||||
if (Array.isArray(response)) {
|
||||
return Array.from(response);
|
||||
} else {
|
||||
return Object.assign({}, response);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}).catch((error) => {
|
||||
if (error.errorcode == 'invalidtoken' ||
|
||||
(error.errorcode == 'accessexception' && error.message.indexOf('Invalid token - token expired') > -1)) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import { CoreInputErrorsComponent } from './input-errors/input-errors';
|
|||
import { CoreShowPasswordComponent } from './show-password/show-password';
|
||||
import { CoreIframeComponent } from './iframe/iframe';
|
||||
import { CoreProgressBarComponent } from './progress-bar/progress-bar';
|
||||
import { CoreEmptyBoxComponent } from './empty-box/empty-box';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -30,7 +31,8 @@ import { CoreProgressBarComponent } from './progress-bar/progress-bar';
|
|||
CoreInputErrorsComponent,
|
||||
CoreShowPasswordComponent,
|
||||
CoreIframeComponent,
|
||||
CoreProgressBarComponent
|
||||
CoreProgressBarComponent,
|
||||
CoreEmptyBoxComponent
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
|
@ -43,7 +45,8 @@ import { CoreProgressBarComponent } from './progress-bar/progress-bar';
|
|||
CoreInputErrorsComponent,
|
||||
CoreShowPasswordComponent,
|
||||
CoreIframeComponent,
|
||||
CoreProgressBarComponent
|
||||
CoreProgressBarComponent,
|
||||
CoreEmptyBoxComponent
|
||||
]
|
||||
})
|
||||
export class CoreComponentsModule {}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<div class="mm-empty-box" [class.mm-empty-box-inline]="!image && !icon">
|
||||
<div class="mm-empty-box-content">
|
||||
<img *ngIf="image && !icon" [src]="image" role="presentation">
|
||||
<ion-icon *ngIf="icon" name="icon"></ion-icon>
|
||||
<p *ngIf="message">{{ message }}</p>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
core-empty-box {
|
||||
|
||||
}
|
|
@ -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 { Component, Input } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component to show an empty box message. It will show an optional icon or image and a text centered on page.
|
||||
*
|
||||
* Usage:
|
||||
* <core-empty-box *ngIf="empty" icon="bell" [message]="'core.emptymessage' | translate"></core-empty-box>
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-empty-box',
|
||||
templateUrl: 'empty-box.html'
|
||||
})
|
||||
export class CoreEmptyBoxComponent {
|
||||
@Input() message: string; // Message to display.
|
||||
@Input() icon?: string; // Name of the icon to use.
|
||||
@Input() image?: string; // Image source. If an icon is provided, image won't be used.
|
||||
|
||||
constructor() {}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// (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 { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../directives/directives.module';
|
||||
import { CoreCoursesCourseProgressComponent } from '../components/course-progress/course-progress';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCoursesCourseProgressComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
CoreCoursesCourseProgressComponent
|
||||
]
|
||||
})
|
||||
export class CoreCoursesComponentsModule {}
|
|
@ -14,19 +14,21 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreCoursesProvider } from './providers/courses';
|
||||
import { CoreCoursesCourseProgressComponent } from './components/course-progress/course-progress';
|
||||
import { CoreCoursesMainMenuHandler } from './providers/handlers';
|
||||
import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCoursesCourseProgressComponent
|
||||
],
|
||||
declarations: [],
|
||||
imports: [
|
||||
],
|
||||
providers: [
|
||||
CoreCoursesProvider
|
||||
CoreCoursesProvider,
|
||||
CoreCoursesMainMenuHandler
|
||||
],
|
||||
exports: [
|
||||
CoreCoursesCourseProgressComponent
|
||||
]
|
||||
exports: []
|
||||
})
|
||||
export class CoreCoursesModule {}
|
||||
export class CoreCoursesModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: CoreCoursesMainMenuHandler) {
|
||||
mainMenuDelegate.registerHandler(mainMenuHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<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">
|
||||
<ion-icon name="search"></ion-icon>
|
||||
</button>
|
||||
<!-- @todo: Context menu. -->
|
||||
</ion-buttons>
|
||||
</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">
|
||||
<ion-item *ngIf="showFilter" class="item-transparent">
|
||||
<ion-icon name="funnel" class="placeholder-icon" item-start></ion-icon>
|
||||
<ion-input type="text" name="filter" placeholder="{{ 'core.courses.filtermycourses' | translate }}" [(ngModel)]="filter" (ngModelChange)="filterChanged($event)"></ion-input>
|
||||
</ion-item>
|
||||
<core-courses-course-progress *ngFor="let course of filteredCourses" [course]="course" showSummary="true"></core-courses-course-progress>
|
||||
<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>
|
||||
</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 { CoreCoursesMyCoursesPage } from './my-courses';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreCoursesMyCoursesPage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreCoursesComponentsModule,
|
||||
IonicPageModule.forChild(CoreCoursesMyCoursesPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreCoursesMyCoursesPageModule {}
|
|
@ -0,0 +1,3 @@
|
|||
page-core-courses-my-courses {
|
||||
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
// (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 } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '../../../../providers/events';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
|
||||
/**
|
||||
* Page that displays the list of courses the user is enrolled in.
|
||||
*/
|
||||
@IonicPage()
|
||||
@Component({
|
||||
selector: 'page-core-courses-my-courses',
|
||||
templateUrl: 'my-courses.html',
|
||||
})
|
||||
export class CoreCoursesMyCoursesPage {
|
||||
courses: any[];
|
||||
filteredCourses: any[];
|
||||
searchEnabled: boolean;
|
||||
filter = '';
|
||||
showFilter = false;
|
||||
coursesLoaded = false;
|
||||
|
||||
protected prefetchIconInitialized = false;
|
||||
protected myCoursesObserver;
|
||||
protected siteUpdatedObserver;
|
||||
|
||||
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
|
||||
private sitesProvider: CoreSitesProvider) {}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad() {
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
|
||||
this.fetchCourses().finally(() => {
|
||||
this.coursesLoaded = true;
|
||||
});
|
||||
|
||||
this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, (data) => {
|
||||
if (data.siteId == this.sitesProvider.getCurrentSiteId()) {
|
||||
this.fetchCourses();
|
||||
}
|
||||
});
|
||||
|
||||
this.siteUpdatedObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, (data) => {
|
||||
if (data.siteId == this.sitesProvider.getCurrentSiteId()) {
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the user courses.
|
||||
*/
|
||||
protected fetchCourses() {
|
||||
return this.coursesProvider.getUserCourses().then((courses) => {
|
||||
|
||||
const courseIds = courses.map((course) => {
|
||||
return course.id;
|
||||
});
|
||||
|
||||
return this.coursesProvider.getCoursesOptions(courseIds).then((options) => {
|
||||
courses.forEach((course) => {
|
||||
course.progress = isNaN(parseInt(course.progress, 10)) ? false : parseInt(course.progress, 10);
|
||||
course.navOptions = options.navOptions[course.id];
|
||||
course.admOptions = options.admOptions[course.id];
|
||||
});
|
||||
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) {
|
||||
let promises = [];
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses());
|
||||
// promises.push($mmCoursesDelegate.clearAndInvalidateCoursesOptions());
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
|
||||
this.prefetchIconInitialized = false;
|
||||
this.fetchCourses().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the filter.
|
||||
*/
|
||||
switchFilter() {
|
||||
this.filter = '';
|
||||
this.showFilter = !this.showFilter;
|
||||
this.filteredCourses = this.courses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to search courses.
|
||||
*/
|
||||
openSearch() {
|
||||
this.navCtrl.push('CoreCoursesSearchPage');
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter has changed.
|
||||
*
|
||||
* @param {string} newValue New filter value.
|
||||
*/
|
||||
filterChanged(newValue: string) {
|
||||
if (!newValue || !this.courses) {
|
||||
this.filteredCourses = this.courses;
|
||||
} else {
|
||||
this.filteredCourses = this.courses.filter((course) => {
|
||||
return course.fullname.indexOf(newValue) > -1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this.myCoursesObserver && this.myCoursesObserver.off();
|
||||
this.siteUpdatedObserver && this.siteUpdatedObserver.off();
|
||||
}
|
||||
}
|
|
@ -144,7 +144,7 @@ export class CoreCoursesProvider {
|
|||
* @param {CoreSite} [site] Site. If not defined, use current site.
|
||||
* @return {boolean} Whether it's disabled.
|
||||
*/
|
||||
isMyCoursesDisabledInSite(site: CoreSite) : boolean {
|
||||
isMyCoursesDisabledInSite(site?: CoreSite) : boolean {
|
||||
site = site || this.sitesProvider.getCurrentSite();
|
||||
return site.isFeatureDisabled('$mmSideMenuDelegate_mmCourses');
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ export class CoreCoursesProvider {
|
|||
* @param {CoreSite} [site] Site. If not defined, use current site.
|
||||
* @return {boolean} Whether it's disabled.
|
||||
*/
|
||||
isSearchCoursesDisabledInSite(site: CoreSite) : boolean {
|
||||
isSearchCoursesDisabledInSite(site?: CoreSite) : boolean {
|
||||
site = site || this.sitesProvider.getCurrentSite();
|
||||
return site.isFeatureDisabled('$mmCoursesDelegate_search');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { CoreCoursesProvider } from './courses';
|
||||
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreCoursesMainMenuHandler implements CoreMainMenuHandler {
|
||||
name = 'mmCourses';
|
||||
priority = 1100;
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider) {}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean|Promise<boolean> {
|
||||
let myCoursesDisabled = this.coursesProvider.isMyCoursesDisabledInSite();
|
||||
|
||||
// Check if overview side menu is available, so it won't show My courses.
|
||||
// var $mmaMyOverview = $mmAddonManager.get('$mmaMyOverview');
|
||||
// if ($mmaMyOverview) {
|
||||
// return $mmaMyOverview.isSideMenuAvailable().then(function(enabled) {
|
||||
// if (enabled) {
|
||||
// return false;
|
||||
// }
|
||||
// // Addon not enabled, check my courses.
|
||||
// return !myCoursesDisabled;
|
||||
// });
|
||||
// }
|
||||
// Addon not present, check my courses.
|
||||
return !myCoursesDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return {CoreMainMenuHandlerData} Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHandlerData {
|
||||
return {
|
||||
icon: 'ionic',
|
||||
title: 'core.courses.mycourses',
|
||||
page: 'CoreCoursesMyCoursesPage',
|
||||
class: 'mm-mycourses-handler'
|
||||
};
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/d
|
|||
templateUrl: 'menu.html',
|
||||
})
|
||||
export class CoreMainMenuPage implements OnDestroy {
|
||||
tabs: CoreMainMenuHandlerData[];
|
||||
tabs: CoreMainMenuHandlerData[] = [];
|
||||
loaded: boolean;
|
||||
protected subscription;
|
||||
protected moreTabData = {
|
||||
|
@ -36,6 +36,7 @@ export class CoreMainMenuPage implements OnDestroy {
|
|||
title: 'core.more',
|
||||
icon: 'more'
|
||||
};
|
||||
protected moreTabAdded = false;
|
||||
protected logoutObserver;
|
||||
|
||||
constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider,
|
||||
|
@ -58,7 +59,31 @@ export class CoreMainMenuPage implements OnDestroy {
|
|||
|
||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||
this.tabs = handlers.slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS); // Get main handlers.
|
||||
this.tabs.push(this.moreTabData); // Add "More" tab.
|
||||
|
||||
// Check if handlers are already in tabs. Add the ones that aren't.
|
||||
// @todo: https://github.com/ionic-team/ionic/issues/13633
|
||||
for (let i in handlers) {
|
||||
let handler = handlers[i],
|
||||
found = false;
|
||||
|
||||
for (let j in this.tabs) {
|
||||
let tab = this.tabs[j];
|
||||
if (tab.title == handler.title && tab.icon == handler.icon) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
this.tabs.push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.moreTabAdded) {
|
||||
this.moreTabAdded = true;
|
||||
this.tabs.push(this.moreTabData); // Add "More" tab.
|
||||
}
|
||||
|
||||
this.loaded = this.menuDelegate.areHandlersLoaded();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ siteInfo.sitename }}</ion-title>
|
||||
<ion-title><core-format-text [text]="siteInfo.sitename"></core-format-text></ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
|
|
|
@ -177,7 +177,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
if (expandInFullview) {
|
||||
this.element.classList.add('mm-expand-in-fullview');
|
||||
}
|
||||
this.element.classList.add('mm-text-formatted mm-shortened');
|
||||
this.element.classList.add('mm-text-formatted');
|
||||
this.element.classList.add('mm-shortened');
|
||||
this.element.style.maxHeight = this.maxHeight + 'px';
|
||||
|
||||
this.element.addEventListener('click', (e) => {
|
||||
|
|
|
@ -726,6 +726,9 @@ export class CoreDomUtilsProvider {
|
|||
*/
|
||||
showErrorModalDefault(error: any, defaultError: any, needsTranslate?: boolean, autocloseTime?: number) : Alert {
|
||||
if (error != CoreConstants.dontShowError) {
|
||||
if (error && typeof error != 'string') {
|
||||
error = error.message || error.error;
|
||||
}
|
||||
error = typeof error == 'string' ? error : defaultError;
|
||||
return this.showErrorModal(error, needsTranslate, autocloseTime);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue