From c1cae77bbc1bdbd1120255c578884665a3036e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 14 Jun 2022 17:55:15 +0200 Subject: [PATCH] MOBILE-4047 cron: Improve alerts on cron failures --- scripts/langindex.json | 4 ++- src/core/features/course/services/course.ts | 4 ++- .../services/pushnotifications.ts | 6 +---- src/core/features/settings/lang.json | 6 ++--- src/core/features/settings/pages/site/site.ts | 2 +- .../pages/synchronization/synchronization.ts | 2 +- .../settings/services/settings-helper.ts | 4 +++ src/core/lang.json | 14 +++++----- src/core/services/cron.ts | 26 +++++++++---------- src/core/services/utils/dom.ts | 3 +-- src/core/services/utils/text.ts | 22 ++++++++++++++++ 11 files changed, 59 insertions(+), 34 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index c61abf87b..ebdb17c76 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1705,7 +1705,9 @@ "core.erroropenfilenoextension": "local_moodlemobileapp", "core.erroropenpopup": "local_moodlemobileapp", "core.errorrenamefile": "local_moodlemobileapp", + "core.errorsitesupport": "local_moodlemobileapp", "core.errorsomedatanotdownloaded": "local_moodlemobileapp", + "core.errorsomethingwrong": "local_moodlemobileapp", "core.errorsync": "local_moodlemobileapp", "core.errorsyncblocked": "local_moodlemobileapp", "core.errorurlschemeinvalidscheme": "local_moodlemobileapp", @@ -2202,7 +2204,6 @@ "core.settings.enablerichtexteditordescription": "local_moodlemobileapp", "core.settings.enablesyncwifi": "local_moodlemobileapp", "core.settings.entriesincache": "local_moodlemobileapp", - "core.settings.errorsyncsite": "local_moodlemobileapp", "core.settings.estimatedfreespace": "local_moodlemobileapp", "core.settings.filesystemroot": "local_moodlemobileapp", "core.settings.fontsize": "local_moodlemobileapp", @@ -2233,6 +2234,7 @@ "core.settings.showdownloadoptions": "local_moodlemobileapp", "core.settings.siteinfo": "local_moodlemobileapp", "core.settings.sites": "moodle", + "core.settings.sitesyncfailed": "local_moodlemobileapp", "core.settings.spaceusage": "local_moodlemobileapp", "core.settings.synchronization": "local_moodlemobileapp", "core.settings.synchronizenow": "local_moodlemobileapp", diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts index e58699c2a..0e987dc12 100644 --- a/src/core/features/course/services/course.ts +++ b/src/core/features/course/services/course.ts @@ -177,7 +177,9 @@ export class CoreCourseProvider { CorePlatform.resume.subscribe(() => { // Run the handler the app is open to keep user in online status. setTimeout(() => { - CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name); + CoreUtils.ignoreErrors( + CoreCronDelegate.forceCronHandlerExecution(CoreCourseLogCronHandler.name), + ); }, 1000); }); diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts index e817633bf..366ae942d 100644 --- a/src/core/features/pushnotifications/services/pushnotifications.ts +++ b/src/core/features/pushnotifications/services/pushnotifications.ts @@ -746,11 +746,7 @@ export class CorePushNotificationsProvider { CoreEvents.trigger(CoreEvents.DEVICE_REGISTERED_IN_MOODLE, {}, site.getId()); // Insert the device in the local DB. - try { - await this.registeredDevicesTables[site.getId()].insert(data); - } catch (err) { - // Ignore errors. - } + await CoreUtils.ignoreErrors(this.registeredDevicesTables[site.getId()].insert(data)); } } finally { // Remove pending unregisters for this site. diff --git a/src/core/features/settings/lang.json b/src/core/features/settings/lang.json index 42114fcd0..f9388e862 100644 --- a/src/core/features/settings/lang.json +++ b/src/core/features/settings/lang.json @@ -4,8 +4,8 @@ "appsettings": "App settings", "appversion": "App version", "cannotsyncloggedout": "This site cannot be synchronised because you've logged out. Please try again when you're logged in the site again.", - "cannotsyncoffline": "Cannot synchronise offline.", - "cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.", + "cannotsyncoffline": "Site synchronisation failed because your device is not connected to the internet.", + "cannotsyncwithoutwifi": "Your device is not connected to Wi-Fi. Connect to a Wi-Fi network or turn off Data Saver in the app settings.", "changelanguage": "Change to {{$a}}", "changelanguagealert": "Changing the language will restart the app.", "colorscheme-dark": "Dark", @@ -37,7 +37,6 @@ "enablerichtexteditordescription": "If enabled, a text editor will be available when entering content.", "enablesyncwifi": "Allow sync only when on Wi-Fi", "entriesincache": "{{$a}} entries in cache", - "errorsyncsite": "Error synchronising site data. Please check your Internet connection and try again.", "estimatedfreespace": "Estimated free space", "filesystemroot": "File system root", "fontsize": "Text size", @@ -68,6 +67,7 @@ "showdownloadoptions": "Show download options", "siteinfo": "Site info", "sites": "Sites", + "sitesyncfailed": "Site synchronisation failed", "spaceusage": "Space usage", "synchronization": "Synchronisation", "synchronizenow": "Synchronise now", diff --git a/src/core/features/settings/pages/site/site.ts b/src/core/features/settings/pages/site/site.ts index 57826c5ea..a88532235 100644 --- a/src/core/features/settings/pages/site/site.ts +++ b/src/core/features/settings/pages/site/site.ts @@ -94,7 +94,7 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy { if (this.isDestroyed) { return; } - CoreDomUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true); + CoreDomUtils.showErrorModalDefault(error, 'core.settings.sitesyncfailed', true); } } diff --git a/src/core/features/settings/pages/synchronization/synchronization.ts b/src/core/features/settings/pages/synchronization/synchronization.ts index 5b5b664c3..b96806ab2 100644 --- a/src/core/features/settings/pages/synchronization/synchronization.ts +++ b/src/core/features/settings/pages/synchronization/synchronization.ts @@ -95,7 +95,7 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy { return; } - CoreDomUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true); + CoreDomUtils.showErrorModalDefault(error, 'core.settings.sitesyncfailed', true); } } diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts index 1d1ee82ea..18e754265 100644 --- a/src/core/features/settings/services/settings-helper.ts +++ b/src/core/features/settings/services/settings-helper.ts @@ -29,6 +29,7 @@ import { CoreCourse } from '@features/course/services/course'; import { makeSingleton, Translate } from '@singletons'; import { CoreError } from '@classes/errors/error'; import { Observable, Subject } from 'rxjs'; +import { CoreTextUtils } from '@services/utils/text'; /** * Object with space usage and cache entries that can be erased. @@ -260,6 +261,7 @@ export class CoreSettingsHelperProvider { const site = await CoreSites.getSite(siteId); const hasSyncHandlers = CoreCronDelegate.hasManualSyncHandlers(); + // All these errors should not happen on manual sync because are prevented on UI. if (site.isLoggedOut()) { // Cannot sync logged out sites. throw new CoreError(Translate.instant('core.settings.cannotsyncloggedout')); @@ -286,6 +288,8 @@ export class CoreSettingsHelperProvider { try { await syncPromise; + } catch (error) { + throw CoreTextUtils.addTitleToError(error, Translate.instant('core.settings.sitesyncfailed')); } finally { delete this.syncPromises[siteId]; } diff --git a/src/core/lang.json b/src/core/lang.json index a3e73bb03..c83d0aed7 100644 --- a/src/core/lang.json +++ b/src/core/lang.json @@ -96,6 +96,7 @@ "downloading": "Downloading", "edit": "Edit", "emptysplit": "This page will appear blank if the left panel is empty or is loading.", + "endonesteptour": "Got it", "error": "Error", "errorchangecompletion": "An error occurred while changing the completion status. Please try again.", "errordeletefile": "Error deleting the file. Please try again.", @@ -111,7 +112,9 @@ "erroropenfilenoextension": "Error opening file: the file doesn't have an extension.", "erroropenpopup": "This activity is trying to open a popup. This is not supported in the app.", "errorrenamefile": "Error renaming file. Please try again.", + "errorsitesupport": "If the problem persists, contact site support.", "errorsomedatanotdownloaded": "If you downloaded this activity, please notice that some data isn't downloaded during the download process for performance and data usage reasons.", + "errorsomethingwrong": "Something went wrong. Please try again.", "errorsync": "An error occurred while synchronising. Please try again.", "errorsyncblocked": "This {{$a}} cannot be synchronised right now because of an ongoing process. Please try again later. If the problem persists, try restarting the app.", "errorurlschemeinvalidscheme": "This URL is meant to be used in another app: {{$a}}.", @@ -248,8 +251,8 @@ "resources": "Resources", "restore": "Restore", "restricted": "Restricted", - "retry": "Retry", "resume": "Resume", + "retry": "Retry", "save": "Save", "savechanges": "Save changes", "scanqr": "Scan QR code", @@ -328,11 +331,10 @@ "user": "User", "userdeleted": "This user account has been deleted", "userdetails": "User details", - "usernotfullysetup": "User not fully set-up", "usernologin": "Authentication has been revoked for this account", - "usersuspended": "Registration suspended", - "endonesteptour": "Got it", + "usernotfullysetup": "User not fully set-up", "users": "Users", + "usersuspended": "Registration suspended", "view": "View", "viewcode": "View code", "vieweditor": "View editor", @@ -351,8 +353,8 @@ "year": "year", "years": "years", "yes": "Yes", - "youreoffline": "You are offline", - "youreonline": "You are back online", + "youreoffline": "Your device is offline", + "youreonline": "Your device is back online", "zoomin": "Zoom In", "zoomout": "Zoom Out" } diff --git a/src/core/services/cron.ts b/src/core/services/cron.ts index df6ec5e33..32ec54823 100644 --- a/src/core/services/cron.ts +++ b/src/core/services/cron.ts @@ -21,7 +21,7 @@ import { CoreUtils } from '@services/utils/utils'; import { CoreConstants } from '@/core/constants'; import { CoreError } from '@classes/errors/error'; -import { makeSingleton } from '@singletons'; +import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { APP_SCHEMA, CRON_TABLE_NAME, CronDBEntry } from '@services/database/cron'; import { asyncInstance } from '../utils/async-instance'; @@ -81,10 +81,11 @@ export class CoreCronDelegateService { protected async checkAndExecuteHandler(name: string, force?: boolean, siteId?: string): Promise { if (!this.handlers[name] || !this.handlers[name].execute) { // Invalid handler. - const message = `Cannot execute handler because is invalid: ${name}`; - this.logger.debug(message); + this.logger.debug(`Cannot execute cron job because is invalid: ${name}`); - throw new CoreError(message); + throw new CoreError( + Translate.instant('core.errorsomethingwrong') + '
' + Translate.instant('core.errorsitesupport'), + ); } const usesNetwork = this.handlerUsesNetwork(name); @@ -92,11 +93,10 @@ export class CoreCronDelegateService { if (usesNetwork && !CoreNetwork.isOnline()) { // Offline, stop executing. - const message = `Cannot execute handler because device is offline: ${name}`; - this.logger.debug(message); + this.logger.debug(`Cron job failed because your device is not connected to the internet: ${name}`); this.stopHandler(name); - throw new CoreError(message); + throw new CoreError(Translate.instant('core.settings.cannotsyncoffline')); } if (isSync) { @@ -105,11 +105,10 @@ export class CoreCronDelegateService { if (syncOnlyOnWifi && !CoreNetwork.isWifi()) { // Cannot execute in this network connection, retry soon. - const message = `Cannot execute handler because device is using limited connection: ${name}`; - this.logger.debug(message); + this.logger.debug(`Cron job failed because your device has a limited internet connection: ${name}`); this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL); - throw new CoreError(message); + throw new CoreError(Translate.instant('core.settings.cannotsyncwithoutwifi')); } } @@ -118,7 +117,7 @@ export class CoreCronDelegateService { try { await this.executeHandler(name, force, siteId); - this.logger.debug(`Execution of handler '${name}' was a success.`); + this.logger.debug(`Cron job '${name}' was successfully executed.`); await CoreUtils.ignoreErrors(this.setHandlerLastExecutionTime(name, Date.now())); @@ -127,11 +126,10 @@ export class CoreCronDelegateService { return; } catch (error) { // Handler call failed. Retry soon. - const message = `Execution of handler '${name}' failed.`; - this.logger.error(message, error); + this.logger.error(`Cron job '${name}' failed.`, error); this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL); - throw new CoreError(message); + throw error; } }); diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index 70cd4c47a..a6218fd05 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -46,7 +46,6 @@ import { CoreViewerImageComponent } from '@features/viewer/components/image/imag import { CoreFormFields, CoreForms } from '../../singletons/form'; import { CoreModalLateralTransitionEnter, CoreModalLateralTransitionLeave } from '@classes/modal-lateral-transition'; import { CoreZoomLevel } from '@features/settings/services/settings-helper'; -import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle'; import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang'; import { CoreSites } from '@services/sites'; import { NavigationStart } from '@angular/router'; @@ -1350,7 +1349,7 @@ export class CoreDomUtilsProvider { if (this.isNetworkError(message, error)) { alertOptions.cssClass = 'core-alert-network-error'; - } else if (error instanceof CoreErrorWithTitle) { + } else if (typeof error !== 'string' && 'title' in error) { alertOptions.header = error.title || undefined; } else { alertOptions.header = Translate.instant('core.error'); diff --git a/src/core/services/utils/text.ts b/src/core/services/utils/text.ts index 723b3574c..20d98c515 100644 --- a/src/core/services/utils/text.ts +++ b/src/core/services/utils/text.ts @@ -37,6 +37,7 @@ export type CoreTextErrorObject = { body?: string; debuginfo?: string; backtrace?: string; + title?: string; }; /* @@ -149,6 +150,27 @@ export class CoreTextUtilsProvider { return error; } + /** + * Add some title to an error message. + * + * @param error Error message or object. + * @param title Title to add. + * @return Modified error. + */ + addTitleToError(error: string | CoreError | CoreTextErrorObject | undefined | null, title: string): CoreTextErrorObject { + let improvedError: CoreTextErrorObject = {}; + + if (typeof error === 'string') { + improvedError.message = error; + } else if (error && 'message' in error) { + improvedError = error; + } + + improvedError.title = title; + + return improvedError; + } + /** * Given an address as a string, return a URL to open the address in maps. *