From 01848965d8c357bc54b1c232fa68f8d79647a76f Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 6 Apr 2018 08:58:23 +0200 Subject: [PATCH] MOBILE-2348 core: Return a 'canceled' error in showConfirm --- .../messages/pages/discussion/discussion.ts | 6 +- .../providers/user-add-contact-handler.ts | 2 - .../providers/user-block-contact-handler.ts | 2 - src/addon/mod/quiz/pages/player/player.ts | 16 ++-- src/addon/mod/quiz/providers/helper.ts | 2 +- .../mod/survey/components/index/index.ts | 6 +- src/components/file/file.ts | 5 +- src/components/local-file/local-file.ts | 7 +- src/core/contentlinks/providers/helper.ts | 2 + src/core/course/pages/section/section.ts | 24 +++--- src/core/course/providers/helper.ts | 83 ++++++++----------- .../courses/pages/my-courses/my-courses.ts | 6 +- .../courses/pages/my-overview/my-overview.ts | 6 +- src/core/login/providers/helper.ts | 2 +- src/providers/utils/dom.ts | 41 ++++++--- 15 files changed, 107 insertions(+), 103 deletions(-) diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index d29d7b0ea..ebea96e97 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -518,18 +518,18 @@ export class AddonMessagesDiscussionPage implements OnDestroy { this.domUtils.showConfirm(this.translate.instant(langKey)).then(() => { const modal = this.domUtils.showModalLoading('core.deleting', true); - this.messagesProvider.deleteMessage(message).then(() => { + return this.messagesProvider.deleteMessage(message).then(() => { // Remove message from the list without having to wait for re-fetch. this.messages.splice(index, 1); this.removeMessage(message.hash); this.notifyNewMessage(); this.fetchData(); // Re-fetch messages to update cached data. - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.messages.errordeletemessage', true); }).finally(() => { modal.dismiss(); }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'addon.messages.errordeletemessage', true); }); } diff --git a/src/addon/messages/providers/user-add-contact-handler.ts b/src/addon/messages/providers/user-add-contact-handler.ts index 8c1e8241e..0dac7a96d 100644 --- a/src/addon/messages/providers/user-add-contact-handler.ts +++ b/src/addon/messages/providers/user-add-contact-handler.ts @@ -100,8 +100,6 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle return this.domUtils.showConfirm(template, title, title).then(() => { return this.messagesProvider.removeContact(user.id); - }, () => { - // Ignore on cancel. }); } else { return this.messagesProvider.addContact(user.id); diff --git a/src/addon/messages/providers/user-block-contact-handler.ts b/src/addon/messages/providers/user-block-contact-handler.ts index 1693dd538..a329c06d2 100644 --- a/src/addon/messages/providers/user-block-contact-handler.ts +++ b/src/addon/messages/providers/user-block-contact-handler.ts @@ -103,8 +103,6 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand return this.domUtils.showConfirm(template, title, title).then(() => { return this.messagesProvider.blockContact(user.id); - }, () => { - // Ignore on cancel. }); } }).catch((error) => { diff --git a/src/addon/mod/quiz/pages/player/player.ts b/src/addon/mod/quiz/pages/player/player.ts index 341d1ec8f..fddcbfa3f 100644 --- a/src/addon/mod/quiz/pages/player/player.ts +++ b/src/addon/mod/quiz/pages/player/player.ts @@ -172,7 +172,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { answers[button.name] = button.value; // Behaviour checks are always in online. - this.quizProvider.processAttempt(this.quiz, this.attempt, answers, this.preflightData).then(() => { + return this.quizProvider.processAttempt(this.quiz, this.attempt, answers, this.preflightData).then(() => { // Reload the current page. const scrollElement = this.content.getScrollElement(), scrollTop = scrollElement.scrollTop || 0, @@ -185,11 +185,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { this.loaded = true; this.content.scrollTo(scrollLeft, scrollTop); }); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'Error performing action.'); }).finally(() => { modal.dismiss(); }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'Error performing action.'); }); } @@ -354,11 +354,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { // Leave the player. this.forceLeave = true; this.navCtrl.pop(); - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorsaveattempt', true); }).finally(() => { modal.dismiss(); }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorsaveattempt', true); }); } @@ -560,13 +560,9 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { // Attempt is overdue or finished in offline, we can only load the summary. return this.loadSummary(); } - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); }); }).catch((error) => { - if (error) { - this.domUtils.showErrorModal(error); - } + this.domUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true); }); } diff --git a/src/addon/mod/quiz/providers/helper.ts b/src/addon/mod/quiz/providers/helper.ts index 3eb2b003a..3093b3994 100644 --- a/src/addon/mod/quiz/providers/helper.ts +++ b/src/addon/mod/quiz/providers/helper.ts @@ -139,7 +139,7 @@ export class AddonModQuizHelperProvider { if (typeof data != 'undefined') { resolve(data); } else { - reject(null); + reject(this.domUtils.createCanceledError()); } }); }); diff --git a/src/addon/mod/survey/components/index/index.ts b/src/addon/mod/survey/components/index/index.ts index a5e8e234b..731ce8b66 100644 --- a/src/addon/mod/survey/components/index/index.ts +++ b/src/addon/mod/survey/components/index/index.ts @@ -188,15 +188,15 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo }); } - this.surveyProvider.submitAnswers(this.survey.id, this.survey.name, this.courseId, answers).then(() => { + return this.surveyProvider.submitAnswers(this.survey.id, this.survey.name, this.courseId, answers).then(() => { this.content.scrollToTop(); return this.refreshContent(false); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'addon.mod_survey.cannotsubmitsurvey', true); }).finally(() => { modal.dismiss(); }); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'addon.mod_survey.cannotsubmitsurvey', true); }); } diff --git a/src/components/file/file.ts b/src/components/file/file.ts index 7ed4d61c6..ab0585190 100644 --- a/src/components/file/file.ts +++ b/src/components/file/file.ts @@ -158,14 +158,17 @@ export class CoreFileComponent implements OnInit, OnDestroy { promise = this.fileSize ? this.domUtils.confirmDownloadSize({ size: this.fileSize, total: true }) : Promise.resolve(); promise.then(() => { // User confirmed, add the file to queue. - this.filepoolProvider.invalidateFileByUrl(this.siteId, this.fileUrl).finally(() => { + return this.filepoolProvider.invalidateFileByUrl(this.siteId, this.fileUrl).finally(() => { this.isDownloading = true; + this.filepoolProvider.addToQueueByUrl(this.siteId, this.fileUrl, this.component, this.componentId, this.timemodified, undefined, undefined, 0, this.file).catch((error) => { this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); this.calculateState(); }); }); + }).catch(() => { + // Ignore error. }); } } diff --git a/src/components/local-file/local-file.ts b/src/components/local-file/local-file.ts index 6f7dbb871..1acdcbd5d 100644 --- a/src/components/local-file/local-file.ts +++ b/src/components/local-file/local-file.ts @@ -171,15 +171,14 @@ export class CoreLocalFileComponent implements OnInit { // Ask confirmation. this.domUtils.showConfirm(this.translate.instant('core.confirmdeletefile')).then(() => { const modal = this.domUtils.showModalLoading(); - this.fileProvider.removeFile(this.relativePath).then(() => { + + return this.fileProvider.removeFile(this.relativePath).then(() => { this.onDelete.emit(); - }).catch(() => { - this.domUtils.showErrorModal('core.errordeletefile', true); }).finally(() => { modal.dismiss(); }); }).catch(() => { - // User cancelled. + this.domUtils.showErrorModal('core.errordeletefile', true); }); } } diff --git a/src/core/contentlinks/providers/helper.ts b/src/core/contentlinks/providers/helper.ts index 2eb8be01f..8cd2660d8 100644 --- a/src/core/contentlinks/providers/helper.ts +++ b/src/core/contentlinks/providers/helper.ts @@ -233,6 +233,8 @@ export class CoreContentLinksHelperProvider { } else { this.goToChooseSite(url); } + }).catch(() => { + // User canceled. }); } diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index d0325a3ca..33d30e128 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -283,18 +283,18 @@ export class CoreCourseSectionPage implements OnDestroy { */ prefetchCourse(): void { this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, this.sections, this.courseHandlers) - .then((downloaded) => { - if (downloaded && this.downloadEnabled) { - // Recalculate the status. - this.courseHelper.calculateSectionsStatus(this.sections, this.course.id).catch(() => { - // Ignore errors (shouldn't happen). - }); - } - }).catch((error) => { - if (!this.isDestroyed) { - this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); - } - }); + .then(() => { + if (this.downloadEnabled) { + // Recalculate the status. + this.courseHelper.calculateSectionsStatus(this.sections, this.course.id).catch(() => { + // Ignore errors (shouldn't happen). + }); + } + }).catch((error) => { + if (!this.isDestroyed) { + this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); + } + }); } /** diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index 8bc95c52b..02502ef0c 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -249,8 +249,7 @@ export class CoreCourseHelperProvider { * @param {any} course Course to prefetch. * @param {any[]} [sections] List of course sections. * @param {CoreCourseOptionsHandlerToDisplay[]} courseHandlers List of course handlers. - * @return {Promise} Promise resolved with true when the download finishes, resolved with false if user doesn't - * confirm, rejected if an error occurs. + * @return {Promise} Promise resolved when the download finishes, rejected if an error occurs or the user cancels. */ confirmAndPrefetchCourse(iconData: any, course: any, sections?: any[], courseHandlers?: CoreCourseOptionsHandlerToDisplay[]) : Promise { @@ -288,11 +287,8 @@ export class CoreCourseHelperProvider { }, (error): any => { // User cancelled or there was an error calculating the size. iconData.prefetchCourseIcon = initialIcon; - if (error) { - return Promise.reject(error); - } - return false; + return Promise.reject(error); }); }); } @@ -302,9 +298,9 @@ export class CoreCourseHelperProvider { * * @param {any[]} courses List of courses to download. * @param {Function} [onProgress] Function to call everytime a course is downloaded. - * @return {Promise} Resolved with true when downloaded, resolved with false if user cancels, rejected if error. + * @return {Promise} Resolved when downloaded, rejected if error or canceled. */ - confirmAndPrefetchCourses(courses: any[], onProgress?: (data: CoreCourseCoursesProgress) => void): Promise { + confirmAndPrefetchCourses(courses: any[], onProgress?: (data: CoreCourseCoursesProgress) => void): Promise { const siteId = this.sitesProvider.getCurrentSiteId(); // Confirm the download without checking size because it could take a while. @@ -347,12 +343,7 @@ export class CoreCourseHelperProvider { onProgress({ count: 0, total: total, success: true }); } - return this.utils.allPromises(promises).then(() => { - return true; - }); - }, () => { - // User cancelled. - return false; + return this.utils.allPromises(promises); }); } @@ -426,23 +417,18 @@ export class CoreCourseHelperProvider { */ contextMenuPrefetch(instance: any, module: any, courseId: number): Promise { const initialIcon = instance.prefetchStatusIcon; - let cancelled = false; instance.prefetchStatusIcon = 'spinner'; // Show spinner since this operation might take a while. // We need to call getDownloadSize, the package might have been updated. return this.prefetchDelegate.getModuleDownloadSize(module, courseId, true).then((size) => { - return this.domUtils.confirmDownloadSize(size).catch(() => { - // User hasn't confirmed, stop. - cancelled = true; - - return Promise.reject(null); - }).then(() => { + return this.domUtils.confirmDownloadSize(size).then(() => { return this.prefetchDelegate.prefetchModule(module, courseId, true); }); }).catch((error) => { instance.prefetchStatusIcon = initialIcon; - if (!instance.isDestroyed && !cancelled) { + + if (!instance.isDestroyed) { this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); } }); @@ -979,36 +965,37 @@ export class CoreCourseHelperProvider { // First of all, mark the course as being downloaded. this.courseDwnPromises[siteId][course.id] = this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADING, - siteId).then(() => { - const promises = []; - let allSectionsSection = sections[0]; + siteId).then(() => { - // Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections". - if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) { - allSectionsSection = { id: CoreCourseProvider.ALL_SECTIONS_ID }; + const promises = []; + let allSectionsSection = sections[0]; + + // Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections". + if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) { + allSectionsSection = { id: CoreCourseProvider.ALL_SECTIONS_ID }; + } + promises.push(this.prefetchSection(allSectionsSection, course.id, sections)); + + // Prefetch course options. + courseHandlers.forEach((handler) => { + if (handler.prefetch) { + promises.push(handler.prefetch(course)); } - promises.push(this.prefetchSection(allSectionsSection, course.id, sections)); - - // Prefetch course options. - courseHandlers.forEach((handler) => { - if (handler.prefetch) { - promises.push(handler.prefetch(course)); - } - }); - - return this.utils.allPromises(promises); - }).then(() => { - // Download success, mark the course as downloaded. - return this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADED, siteId); - }).catch((error) => { - // Error, restore previous status. - return this.courseProvider.setCoursePreviousStatus(course.id, siteId).then(() => { - return Promise.reject(error); - }); - }).finally(() => { - delete this.courseDwnPromises[siteId][course.id]; }); + return this.utils.allPromises(promises); + }).then(() => { + // Download success, mark the course as downloaded. + return this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADED, siteId); + }).catch((error) => { + // Error, restore previous status. + return this.courseProvider.setCoursePreviousStatus(course.id, siteId).then(() => { + return Promise.reject(error); + }); + }).finally(() => { + delete this.courseDwnPromises[siteId][course.id]; + }); + return this.courseDwnPromises[siteId][course.id]; } diff --git a/src/core/courses/pages/my-courses/my-courses.ts b/src/core/courses/pages/my-courses/my-courses.ts index 1508052d4..98ec7007d 100644 --- a/src/core/courses/pages/my-courses/my-courses.ts +++ b/src/core/courses/pages/my-courses/my-courses.ts @@ -160,9 +160,9 @@ export class CoreCoursesMyCoursesPage implements OnDestroy { return this.courseHelper.confirmAndPrefetchCourses(this.courses, (progress) => { this.prefetchCoursesData.badge = progress.count + ' / ' + progress.total; - }).then((downloaded) => { - this.prefetchCoursesData.icon = downloaded ? 'ion-android-refresh' : initialIcon; - }, (error) => { + }).then(() => { + this.prefetchCoursesData.icon = 'ion-android-refresh'; + }).catch((error) => { if (!this.isDestroyed) { this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); this.prefetchCoursesData.icon = initialIcon; diff --git a/src/core/courses/pages/my-overview/my-overview.ts b/src/core/courses/pages/my-overview/my-overview.ts index 552208dfa..8d609ed4e 100644 --- a/src/core/courses/pages/my-overview/my-overview.ts +++ b/src/core/courses/pages/my-overview/my-overview.ts @@ -362,9 +362,9 @@ export class CoreCoursesMyOverviewPage implements OnDestroy { return this.courseHelper.confirmAndPrefetchCourses(this.courses[selected], (progress) => { selectedData.badge = progress.count + ' / ' + progress.total; - }).then((downloaded) => { - selectedData.icon = downloaded ? 'refresh' : initialIcon; - }, (error) => { + }).then(() => { + selectedData.icon = 'refresh'; + }).catch((error) => { if (!this.isDestroyed) { this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); selectedData.icon = initialIcon; diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts index 6b45c3571..8f20a9b08 100644 --- a/src/core/login/providers/helper.ts +++ b/src/core/login/providers/helper.ts @@ -235,7 +235,7 @@ export class CoreLoginHelperProvider { promise.then(() => { this.openBrowserForSSOLogin(siteUrl, typeOfLogin, service, launchUrl); - }, () => { + }).catch(() => { // User cancelled, ignore. }); } diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index f20fca0ed..494e2bd6f 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -139,6 +139,15 @@ export class CoreDomUtilsProvider { return Promise.resolve(); } + /** + * Create a "cancelled" error. These errors won't display an error message in showErrorModal functions. + * + * @return {any} The error object. + */ + createCanceledError(): any { + return {coreCanceled: true}; + } + /** * Extract the downloadable URLs from an HTML code. * @@ -771,7 +780,7 @@ export class CoreDomUtilsProvider { * @param {string} [okText] Text of the OK button. * @param {string} [cancelText] Text of the Cancel button. * @param {any} [options] More options. See https://ionicframework.com/docs/api/components/alert/AlertController/ - * @return {Promise} Promise resolved if the user confirms and rejected if he cancels. + * @return {Promise} Promise resolved if the user confirms and rejected with a canceled error if he cancels. */ showConfirm(message: string, title?: string, okText?: string, cancelText?: string, options?: any): Promise { return new Promise((resolve, reject): void => { @@ -787,7 +796,7 @@ export class CoreDomUtilsProvider { text: cancelText || this.translate.instant('core.cancel'), role: 'cancel', handler: (): void => { - reject(); + reject(this.createCanceledError()); } }, { @@ -813,7 +822,10 @@ export class CoreDomUtilsProvider { showErrorModal(error: any, needsTranslate?: boolean, autocloseTime?: number): Alert { if (typeof error == 'object') { // We received an object instead of a string. Search for common properties. - if (typeof error.content != 'undefined') { + if (error.coreCanceled) { + // It's a canceled error, don't display an error. + return; + } else if (typeof error.content != 'undefined') { error = error.content; } else if (typeof error.body != 'undefined') { error = error.body; @@ -833,6 +845,11 @@ export class CoreDomUtilsProvider { } } + if (error == CoreConstants.DONT_SHOW_ERROR) { + // The error shouldn't be shown, stop. + return; + } + const message = this.textUtils.decodeHTML(needsTranslate ? this.translate.instant(error) : error); return this.showAlert(this.getErrorTitle(message), message, undefined, autocloseTime); @@ -848,14 +865,18 @@ export class CoreDomUtilsProvider { * @return {Alert} The alert modal. */ showErrorModalDefault(error: any, defaultError: any, needsTranslate?: boolean, autocloseTime?: number): Alert { - if (error != CoreConstants.DONT_SHOW_ERROR) { - if (error && typeof error != 'string') { - error = error.message || error.error; - } - error = typeof error == 'string' ? error : defaultError; - - return this.showErrorModal(error, needsTranslate, autocloseTime); + if (error && error.coreCanceled) { + // It's a canceled error, don't display an error. + return; } + + if (error && typeof error != 'string') { + error = error.message || error.error || error.content || error.body; + } + + error = typeof error == 'string' ? error : defaultError; + + return this.showErrorModal(error, needsTranslate, autocloseTime); } /**