From 2d29cc2da693584e2a574d569bb82fb03f70451a Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 12 Dec 2017 10:49:13 +0100 Subject: [PATCH] MOBILE-2302 courses: Implement My Courses --- src/app/app.scss | 102 ++++++++++++ src/classes/site.ts | 9 +- src/components/components.module.ts | 7 +- src/components/empty-box/empty-box.html | 8 + src/components/empty-box/empty-box.scss | 3 + src/components/empty-box/empty-box.ts | 33 ++++ .../courses/components/components.module.ts | 40 +++++ src/core/courses/courses.module.ts | 20 ++- .../courses/pages/my-courses/my-courses.html | 28 ++++ .../pages/my-courses/my-courses.module.ts | 33 ++++ .../courses/pages/my-courses/my-courses.scss | 3 + .../courses/pages/my-courses/my-courses.ts | 154 ++++++++++++++++++ src/core/courses/providers/courses.ts | 4 +- src/core/courses/providers/handlers.ts | 65 ++++++++ src/core/mainmenu/pages/menu/menu.ts | 29 +++- src/core/mainmenu/pages/more/more.html | 2 +- src/directives/format-text.ts | 3 +- src/providers/utils/dom.ts | 3 + 18 files changed, 528 insertions(+), 18 deletions(-) create mode 100644 src/components/empty-box/empty-box.html create mode 100644 src/components/empty-box/empty-box.scss create mode 100644 src/components/empty-box/empty-box.ts create mode 100644 src/core/courses/components/components.module.ts create mode 100644 src/core/courses/pages/my-courses/my-courses.html create mode 100644 src/core/courses/pages/my-courses/my-courses.module.ts create mode 100644 src/core/courses/pages/my-courses/my-courses.scss create mode 100644 src/core/courses/pages/my-courses/my-courses.ts create mode 100644 src/core/courses/providers/handlers.ts diff --git a/src/app/app.scss b/src/app/app.scss index 1ef2a9457..215cb71e7 100644 --- a/src/app/app.scss +++ b/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; + } +} diff --git a/src/classes/site.ts b/src/classes/site.ts index 901e9d7e7..2b46c8e86 100644 --- a/src/classes/site.ts +++ b/src/classes/site.ts @@ -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)) { diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 30ee33409..ab0b08f84 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -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 {} diff --git a/src/components/empty-box/empty-box.html b/src/components/empty-box/empty-box.html new file mode 100644 index 000000000..1c43cb9bf --- /dev/null +++ b/src/components/empty-box/empty-box.html @@ -0,0 +1,8 @@ +
+
+ + +

{{ message }}

+ +
+
\ No newline at end of file diff --git a/src/components/empty-box/empty-box.scss b/src/components/empty-box/empty-box.scss new file mode 100644 index 000000000..ec407edf6 --- /dev/null +++ b/src/components/empty-box/empty-box.scss @@ -0,0 +1,3 @@ +core-empty-box { + +} diff --git a/src/components/empty-box/empty-box.ts b/src/components/empty-box/empty-box.ts new file mode 100644 index 000000000..340445940 --- /dev/null +++ b/src/components/empty-box/empty-box.ts @@ -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: + * + */ +@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() {} +} diff --git a/src/core/courses/components/components.module.ts b/src/core/courses/components/components.module.ts new file mode 100644 index 000000000..f6a66ff29 --- /dev/null +++ b/src/core/courses/components/components.module.ts @@ -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 {} diff --git a/src/core/courses/courses.module.ts b/src/core/courses/courses.module.ts index 19beec0fe..6cc2b1056 100644 --- a/src/core/courses/courses.module.ts +++ b/src/core/courses/courses.module.ts @@ -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); + } +} diff --git a/src/core/courses/pages/my-courses/my-courses.html b/src/core/courses/pages/my-courses/my-courses.html new file mode 100644 index 000000000..720c27686 --- /dev/null +++ b/src/core/courses/pages/my-courses/my-courses.html @@ -0,0 +1,28 @@ + + + {{ 'core.courses.mycourses' | translate }} + + + + + + + + + + + + + + + + + + + +

{{ 'core.courses.searchcoursesadvice' | translate }}

+
+
+
diff --git a/src/core/courses/pages/my-courses/my-courses.module.ts b/src/core/courses/pages/my-courses/my-courses.module.ts new file mode 100644 index 000000000..45c404a78 --- /dev/null +++ b/src/core/courses/pages/my-courses/my-courses.module.ts @@ -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 {} diff --git a/src/core/courses/pages/my-courses/my-courses.scss b/src/core/courses/pages/my-courses/my-courses.scss new file mode 100644 index 000000000..89812f8f7 --- /dev/null +++ b/src/core/courses/pages/my-courses/my-courses.scss @@ -0,0 +1,3 @@ +page-core-courses-my-courses { + +} diff --git a/src/core/courses/pages/my-courses/my-courses.ts b/src/core/courses/pages/my-courses/my-courses.ts new file mode 100644 index 000000000..2b87258e8 --- /dev/null +++ b/src/core/courses/pages/my-courses/my-courses.ts @@ -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(); + } +} diff --git a/src/core/courses/providers/courses.ts b/src/core/courses/providers/courses.ts index fb2cd80ee..655f292b7 100644 --- a/src/core/courses/providers/courses.ts +++ b/src/core/courses/providers/courses.ts @@ -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'); } diff --git a/src/core/courses/providers/handlers.ts b/src/core/courses/providers/handlers.ts new file mode 100644 index 000000000..cece22a00 --- /dev/null +++ b/src/core/courses/providers/handlers.ts @@ -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 { + 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' + }; + } +} diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index e9bab31e5..57932bbaa 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -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(); }); } diff --git a/src/core/mainmenu/pages/more/more.html b/src/core/mainmenu/pages/more/more.html index d1c02dc11..56b29de6d 100644 --- a/src/core/mainmenu/pages/more/more.html +++ b/src/core/mainmenu/pages/more/more.html @@ -1,6 +1,6 @@ - {{ siteInfo.sitename }} + diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index 64b3485b0..96097e12f 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -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) => { diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 1feaff433..59d48c641 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -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); }