diff --git a/webpack.config.js b/config/webpack.config.js similarity index 100% rename from webpack.config.js rename to config/webpack.config.js diff --git a/package.json b/package.json index e006c48b2..8154175bd 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "email": "mobile@moodle.com" }, "config": { - "ionic_webpack": "./webpack.config.js" + "ionic_webpack": "./config/webpack.config.js" }, "repository": { "type": "git", diff --git a/src/addon/messages/components/contacts/contacts.html b/src/addon/messages/components/contacts/contacts.html index 67cf29fc9..a722ca171 100644 --- a/src/addon/messages/components/contacts/contacts.html +++ b/src/addon/messages/components/contacts/contacts.html @@ -3,7 +3,7 @@ - + diff --git a/src/addon/messages/components/contacts/contacts.ts b/src/addon/messages/components/contacts/contacts.ts index 781c20b29..a6cd49adf 100644 --- a/src/addon/messages/components/contacts/contacts.ts +++ b/src/addon/messages/components/contacts/contacts.ts @@ -134,8 +134,6 @@ export class AddonMessagesContactsComponent { this.clearSearch(); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); - - return Promise.reject(null); }); } @@ -202,8 +200,6 @@ export class AddonMessagesContactsComponent { this.contacts['search'] = this.sortUsers(result); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true); - - return Promise.reject(null); }); } diff --git a/src/addon/messages/components/discussions/discussions.html b/src/addon/messages/components/discussions/discussions.html index c33cb5ef0..2e7f4f993 100644 --- a/src/addon/messages/components/discussions/discussions.html +++ b/src/addon/messages/components/discussions/discussions.html @@ -3,10 +3,10 @@ - + - + @@ -33,7 +33,7 @@

-
{{discussion.message.timecreated / 1000 | coreDateDayOrTime}}
+
{{discussion.message.timecreated / 1000 | coreDateDayOrTime}}

diff --git a/src/addon/messages/components/discussions/discussions.ts b/src/addon/messages/components/discussions/discussions.ts index 0d5a1fb4e..8738cf76b 100644 --- a/src/addon/messages/components/discussions/discussions.ts +++ b/src/addon/messages/components/discussions/discussions.ts @@ -42,7 +42,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { loadingMessage: string; discussions: any; discussionUserId: number; - messageId: number; + pushObserver: any; search = { enabled: false, showResults: false, @@ -54,7 +54,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService, private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, private appProvider: CoreAppProvider, platform: Platform, utils: CoreUtilsProvider, - private pushNotificationsDelegate: AddonPushNotificationsDelegate) { + pushNotificationsDelegate: AddonPushNotificationsDelegate) { this.search.loading = translate.instant('core.searching'); this.loadingMessages = translate.instant('core.loading'); @@ -114,7 +114,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { this.discussionUserId = navParams.get('discussionUserId') || false; // If a message push notification is received, refresh the view. - pushNotificationsDelegate.registerReceiveHandler('AddonMessagesDiscussionsComponent', (notification) => { + this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => { // New message received. If it's from current site, refresh the data. if (utils.isFalseOrZero(notification.notif) && notification.site == this.siteId) { this.refreshData(); @@ -211,7 +211,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { this.search.showResults = true; this.search.results = searchResults; }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'mma.messages.errorwhileretrievingmessages', true); + this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); }).finally(() => { this.loaded = true; }); @@ -233,7 +233,6 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { }; if (messageId) { params['message'] = messageId; - this.messageId = messageId; } this.eventsProvider.trigger(AddonMessagesProvider.SPLIT_VIEW_LOAD_EVENT, params, this.siteId); } @@ -246,6 +245,6 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { this.readChangedObserver && this.readChangedObserver.off(); this.cronObserver && this.cronObserver.off(); this.appResumeSubscription && this.appResumeSubscription.unsubscribe(); - this.pushNotificationsDelegate.unregisterReceiveHandler('AddonMessagesDiscussionsComponent'); + this.pushObserver && this.pushObserver.unsubscribe(); } } diff --git a/src/addon/messages/messages.module.ts b/src/addon/messages/messages.module.ts index 3c82420cd..2878f5fed 100644 --- a/src/addon/messages/messages.module.ts +++ b/src/addon/messages/messages.module.ts @@ -105,7 +105,7 @@ export class AddonMessagesModule { } // Register push notification clicks. - pushNotificationsDelegate.registerHandler('mmaMessages', (notification) => { + pushNotificationsDelegate.on('click').subscribe((notification) => { if (utils.isFalseOrZero(notification.notif)) { notificationClicked(notification); diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index 290eeb8d1..49c2125c6 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -172,7 +172,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); }).finally(() => { - this.triggerDiscussionLoadedEvent(); + this.checkCanDelete(); this.resizeContent(); this.loaded = true; }); @@ -201,9 +201,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy { protected fetchData(): Promise { this.logger.debug(`Polling new messages for discussion with user '${this.userId}'`); if (this.messagesBeingSent > 0) { - // We do not poll while a message is being sent or we could confuse the user - // as his message would disappear from the list, and he'd have to wait for the - // interval to check for new messages. + // We do not poll while a message is being sent or we could confuse the user. + // Otherwise, his message would disappear from the list, and he'd have to wait for the interval to check for messages. return Promise.reject(null); } else if (this.fetching) { // Already fetching. @@ -384,7 +383,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { // Update navBar links and buttons. const newCanDelete = (last && last.id && this.messages.length == 1) || this.messages.length > 1; if (this.canDelete != newCanDelete) { - this.triggerDiscussionLoadedEvent(); + this.checkCanDelete(); } } } @@ -425,7 +424,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { /** * Check if there's any message in the list that can be deleted. */ - protected triggerDiscussionLoadedEvent(): void { + protected checkCanDelete(): void { // All messages being sent should be at the end of the list. const first = this.messages[0]; this.canDelete = first && !first.sending; @@ -503,7 +502,6 @@ export class AddonMessagesDiscussionPage implements OnDestroy { this.utils.copyToClipboard(text); } - /** * Function to delete a message. * @@ -627,8 +625,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy { } promise.catch(() => { - // Fetch failed or is offline message, mark the message as sent. If fetch is successful there's no need - // to mark it because the fetch will already show the message received from the server. + // Fetch failed or is offline message, mark the message as sent. + // If fetch is successful there's no need to mark it because the fetch will already show the message received. message.sending = false; if (data.sent) { // Message sent to server, not pending anymore. @@ -642,8 +640,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy { }).catch((error) => { this.messagesBeingSent--; - // Only close the keyboard if an error happens, we want the user to be able to send multiple - // messages without the keyboard being closed. + // Only close the keyboard if an error happens. + // We want the user to be able to send multiple messages without the keyboard being closed. this.appProvider.closeKeyboard(); this.domUtils.showErrorModalDefault(error, 'addon.messages.messagenotsent', true); diff --git a/src/addon/messages/pages/index/index.module.ts b/src/addon/messages/pages/index/index.module.ts index a896440b7..169c8c7df 100644 --- a/src/addon/messages/pages/index/index.module.ts +++ b/src/addon/messages/pages/index/index.module.ts @@ -17,8 +17,6 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { AddonMessagesIndexPage } from './index'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; import { AddonMessagesComponentsModule } from '../../components/components.module'; @NgModule({ @@ -27,8 +25,6 @@ import { AddonMessagesComponentsModule } from '../../components/components.modul ], imports: [ CoreComponentsModule, - CoreDirectivesModule, - CorePipesModule, AddonMessagesComponentsModule, IonicPageModule.forChild(AddonMessagesIndexPage), TranslateModule.forChild() diff --git a/src/addon/messages/pages/settings/settings.ts b/src/addon/messages/pages/settings/settings.ts index ea4ec02c9..72a4e0b26 100644 --- a/src/addon/messages/pages/settings/settings.ts +++ b/src/addon/messages/pages/settings/settings.ts @@ -48,6 +48,7 @@ export class AddonMessagesSettingsPage implements OnDestroy { /** * Fetches preference data. + * * @return {Promise} Resolved when done. */ protected fetchPreferences(): Promise { @@ -85,6 +86,7 @@ export class AddonMessagesSettingsPage implements OnDestroy { /** * Block non contacts. + * * @param {boolean} block If it should be blocked or not. */ blockNonContacts(block: boolean): void { @@ -103,6 +105,7 @@ export class AddonMessagesSettingsPage implements OnDestroy { /** * Change the value of a certain preference. + * * @param {any} notification Notification object. * @param {string} state State name, ['loggedin', 'loggedoff']. * @param {any} processor Notification processor. diff --git a/src/addon/messages/providers/mainmenu-handler.ts b/src/addon/messages/providers/mainmenu-handler.ts index 82dec12b0..f49660b56 100644 --- a/src/addon/messages/providers/mainmenu-handler.ts +++ b/src/addon/messages/providers/mainmenu-handler.ts @@ -56,7 +56,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr }); // If a message push notification is received, refresh the count. - pushNotificationsDelegate.registerReceiveHandler('AddonMessagesMainMenuHandler', (notification) => { + pushNotificationsDelegate.on('receive').subscribe((notification) => { // New message received. If it's from current site, refresh the data. if (utils.isFalseOrZero(notification.notif) && this.sitesProvider.isCurrentSite(notification.site)) { this.updateBadge(notification.site); @@ -64,7 +64,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr }); // Register Badge counter. - pushNotificationsDelegate.registerCounterHandler('mmaMessages'); + pushNotificationsDelegate.registerCounterHandler('AddonMessages'); } /** @@ -91,7 +91,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr title: 'addon.messages.messages', page: 'AddonMessagesIndexPage', class: 'addon-messages-handler', - showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled., + showBadge: true, // Do not check isMessageCountEnabled because we'll use fallback it not enabled. badge: this.badge, loading: this.loading }; @@ -112,7 +112,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr // Leave badge enter if there is a 0+ or a 0. this.badge = parseInt(unread, 10) > 0 ? unread : ''; // Update badge. - this.pushNotificationsProvider.updateAddonCounter('mmaMessages', unread, siteId); + this.pushNotificationsProvider.updateAddonCounter('AddonMessages', unread, siteId); }).catch(() => { this.badge = ''; }).finally(() => { diff --git a/src/addon/messages/providers/messages-offline.ts b/src/addon/messages/providers/messages-offline.ts index 29799fbde..ee8c5a7dd 100644 --- a/src/addon/messages/providers/messages-offline.ts +++ b/src/addon/messages/providers/messages-offline.ts @@ -26,7 +26,7 @@ export class AddonMessagesOfflineProvider { protected logger; // Variables for database. - protected MESSAGES_TABLE = 'mma_messages_offline_messages'; + protected MESSAGES_TABLE = 'addon_messages_offline_messages'; protected tablesSchema = [ { name: this.MESSAGES_TABLE, @@ -140,7 +140,7 @@ export class AddonMessagesOfflineProvider { */ saveMessage(toUserId: number, message: string, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { - const entry = { + const entry = { touserid: toUserId, useridfrom: site.getUserId(), smallmessage: message, diff --git a/src/addon/messages/providers/messages.ts b/src/addon/messages/providers/messages.ts index 42145faf3..da898e64b 100644 --- a/src/addon/messages/providers/messages.ts +++ b/src/addon/messages/providers/messages.ts @@ -29,10 +29,10 @@ export class AddonMessagesProvider { protected ROOT_CACHE_KEY = 'mmaMessages:'; protected LIMIT_MESSAGES = 50; protected LIMIT_SEARCH_MESSAGES = 50; - static NEW_MESSAGE_EVENT = 'new_message_event'; - static READ_CHANGED_EVENT = 'read_changed_event'; - static READ_CRON_EVENT = 'read_cron_event'; - static SPLIT_VIEW_LOAD_EVENT = 'split_view_load_event'; + static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event'; + static READ_CHANGED_EVENT = 'addon_messages_read_changed_event'; + static READ_CRON_EVENT = 'addon_messages_read_cron_event'; + static SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event'; static POLL_INTERVAL = 10000; static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation'; @@ -148,6 +148,16 @@ export class AddonMessagesProvider { return this.ROOT_CACHE_KEY + 'discussion:' + userId; } + /** + * Get the cache key for the message count. + * + * @param {number} userId User ID. + * @return {string} Cache key. + */ + protected getCacheKeyForMessageCount(userId: number): string { + return this.ROOT_CACHE_KEY + 'count:' + userId; + } + /** * Get the cache key for the list of discussions. * @@ -267,8 +277,8 @@ export class AddonMessagesProvider { hasSent; if (lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0) { - // Do not use cache when retrieving older messages. This is to prevent storing too much data - // and to prevent inconsistencies between "pages" loaded. + // Do not use cache when retrieving older messages. + // This is to prevent storing too much data and to prevent inconsistencies between "pages" loaded. preSets['getFromCache'] = false; preSets['saveToCache'] = false; preSets['emergencyCache'] = false; @@ -566,9 +576,10 @@ export class AddonMessagesProvider { useridto: userId }, preSets = { + cacheKey: this.getCacheKeyForMessageCount(userId), getFromCache: false, - emergencyCache: false, - saveToCache: false, + emergencyCache: true, + saveToCache: true, typeExpected: 'number' }; @@ -590,11 +601,11 @@ export class AddonMessagesProvider { return this.getMessages(params, undefined, false, siteId).then((response) => { // Count the discussions by filtering same senders. const discussions = {}; - let count; + response.messages.forEach((message) => { discussions[message.useridto] = 1; }); - count = Object.keys(discussions).length; + const count = Object.keys(discussions).length; // Add + sign if there are more than the limit reachable. return (count > this.LIMIT_MESSAGES) ? count + '+' : count; @@ -700,9 +711,11 @@ export class AddonMessagesProvider { */ invalidateDiscussionsCache(siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCacheKeyForDiscussions()).then(() => { - return this.invalidateContactsCache(site.getId()); - }); + const promises = []; + promises.push(site.invalidateWsCacheForKey(this.getCacheKeyForDiscussions())); + promises.push(this.invalidateContactsCache(site.getId())); + + return Promise.all(promises); }); } @@ -797,12 +810,10 @@ export class AddonMessagesProvider { * @return {Promise} Resolved when enabled, otherwise rejected. */ isMessagingEnabledForSite(siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - if (!site.canUseAdvancedFeature('messaging')) { + return this.isPluginEnabled(siteId).then((enabled) => { + if (!enabled) { return Promise.reject(null); } - - return Promise.resolve(true); }); } @@ -986,14 +997,14 @@ export class AddonMessagesProvider { // Online and no messages stored. Send it to server. return this.sendMessageOnline(toUserId, message).then(() => { return { sent: true }; - }).catch((data) => { - if (data.wserror) { + }).catch((error) => { + if (this.utils.isWebServiceError(error)) { // It's a WebService error, the user cannot send the message so don't store it. - return Promise.reject(data.error); - } else { - // Error sending message, store it to retry later. - return storeOffline(); + return Promise.reject(error); } + + // Error sending message, store it to retry later. + return storeOffline(); }); }); } @@ -1004,9 +1015,7 @@ export class AddonMessagesProvider { * @param {number} toUserId User ID to send the message to. * @param {string} message The message to send * @param {string} [siteId] Site ID. If not defined, current site. - * @return {Promise} Promise resolved if success, rejected if failure. Reject param is an object with: - * - error: The error message. - * - wserror: True if it's an error returned by the WebService, false otherwise. + * @return {Promise} Promise resolved if success, rejected if failure. */ sendMessageOnline(toUserId: number, message: string, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); @@ -1019,18 +1028,10 @@ export class AddonMessagesProvider { } ]; - return this.sendMessagesOnline(messages, siteId).catch((error) => { - return Promise.reject({ - error: error, - wserror: this.utils.isWebServiceError(error) - }); - }).then((response) => { + return this.sendMessagesOnline(messages, siteId).then((response) => { if (response && response[0] && response[0].msgid === -1) { // There was an error, and it should be translated already. - return Promise.reject({ - error: response[0].errormessage, - wserror: true - }); + return this.utils.createFakeWSError(response[0].errormessage); } return this.invalidateDiscussionCache(toUserId, siteId).catch(() => { diff --git a/src/addon/messages/providers/settings-handler.ts b/src/addon/messages/providers/settings-handler.ts index 436eab857..4f479f018 100644 --- a/src/addon/messages/providers/settings-handler.ts +++ b/src/addon/messages/providers/settings-handler.ts @@ -31,7 +31,7 @@ export class AddonMessagesSettingsHandler implements CoreSettingsHandler { /** * Check if the handler is enabled on a site level. * - * @return {boolean} Whether or not the handler is enabled on a site level. + * @return {boolean | Promise} Whether or not the handler is enabled on a site level. */ isEnabled(): boolean | Promise { return this.messagesProvider.isMessagePreferencesEnabled(); diff --git a/src/addon/messages/providers/sync.ts b/src/addon/messages/providers/sync.ts index c270ea0d9..45dcfbb24 100644 --- a/src/addon/messages/providers/sync.ts +++ b/src/addon/messages/providers/sync.ts @@ -21,7 +21,9 @@ import { AddonMessagesOfflineProvider } from './messages-offline'; import { AddonMessagesProvider } from './messages'; import { CoreUserProvider } from '@core/user/providers/user'; import { CoreEventsProvider } from '@providers/events'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { TranslateService } from '@ngx-translate/core'; +import { CoreSyncProvider } from '@providers/sync'; /** * Service to sync messages. @@ -34,8 +36,9 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider, protected appProvider: CoreAppProvider, private messagesOffline: AddonMessagesOfflineProvider, private eventsProvider: CoreEventsProvider, private messagesProvider: AddonMessagesProvider, - private userProvider: CoreUserProvider, private translate: TranslateService) { - super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider); + private userProvider: CoreUserProvider, private translate: TranslateService, private utils: CoreUtilsProvider, + syncProvider: CoreSyncProvider) { + super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider, syncProvider); } /** @@ -49,7 +52,7 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { syncAllDiscussions(siteId?: string, onlyDeviceOffline: boolean = false): Promise { const syncFunctionLog = 'all discussions' + (onlyDeviceOffline ? ' (Only offline)' : ''); - return this.syncOnSites(syncFunctionLog, 'syncAllDiscussionsFunc', {onlyDeviceOffline: onlyDeviceOffline}, siteId); + return this.syncOnSites(syncFunctionLog, this.syncAllDiscussionsFunc.bind(this), [onlyDeviceOffline], siteId); } /** @@ -106,13 +109,12 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { return this.getOngoingSync(userId, siteId); } - let syncPromise; const warnings = []; this.logger.debug(`Try to sync discussion with user '${userId}'`); // Get offline messages to be sent. - syncPromise = this.messagesOffline.getMessages(userId, siteId).then((messages) => { + const syncPromise = this.messagesOffline.getMessages(userId, siteId).then((messages) => { if (!messages.length) { // Nothing to sync. return []; @@ -123,33 +125,35 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { return Promise.reject(null); } - let promise: Promise; - promise = Promise.resolve(); + let promise: Promise = Promise.resolve(); const errors = []; // Order message by timecreated. messages = this.messagesProvider.sortMessages(messages); - // Send the messages. We don't use $mmaMessages#sendMessagesOnline because there's a problem with display order. - // @todo Use $mmaMessages#sendMessagesOnline once the display order is fixed. + // Send the messages. + // We don't use AddonMessagesProvider#sendMessagesOnline because there's a problem with display order. + // @todo Use AddonMessagesProvider#sendMessagesOnline once the display order is fixed. messages.forEach((message, index) => { // Chain message sending. If 1 message fails to be sent we'll stop sending. promise = promise.then(() => { - return this.messagesProvider.sendMessageOnline(userId, message.smallmessage, siteId).catch((data) => { - if (data.wserror) { + return this.messagesProvider.sendMessageOnline(userId, message.smallmessage, siteId).catch((error) => { + if (this.utils.isWebServiceError(error)) { // Error returned by WS. Store the error to show a warning but keep sending messages. - if (errors.indexOf(data.error) == -1) { - errors.push(data.error); - } - } else { - // Error sending, stop execution. - if (this.appProvider.isOnline()) { - // App is online, unmark deviceoffline if marked. - this.messagesOffline.setMessagesDeviceOffline(messages, false); + if (errors.indexOf(error) == -1) { + errors.push(error); } - return Promise.reject(data.error); + return; } + + // Error sending, stop execution. + if (this.appProvider.isOnline()) { + // App is online, unmark deviceoffline if marked. + this.messagesOffline.setMessagesDeviceOffline(messages, false); + } + + return Promise.reject(error); }).then(() => { // Message was sent, delete it from local DB. return this.messagesOffline.deleteMessage(userId, message.smallmessage, message.timecreated, siteId); diff --git a/src/addon/messages/providers/user-add-contact-handler.ts b/src/addon/messages/providers/user-add-contact-handler.ts index f8cbb5dcc..bae34daff 100644 --- a/src/addon/messages/providers/user-add-contact-handler.ts +++ b/src/addon/messages/providers/user-add-contact-handler.ts @@ -33,8 +33,8 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle */ static UPDATED_EVENT = 'AddonMessagesAddContactUserHandler_updated_event'; - name = 'mmaMessages:blockContact'; - priority = 600; + name = 'AddonMessages:addContact'; + priority = 800; type = CoreUserDelegate.TYPE_ACTION; protected disabled = false; @@ -52,9 +52,9 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle /** * Check if handler is enabled. * - * @return {Promise} Promise resolved with true if enabled, rejected or resolved with false otherwise. + * @return {Promise} Promise resolved with true if enabled, rejected or resolved with false otherwise. */ - isEnabled(): Promise { + isEnabled(): Promise { return this.messagesProvider.isPluginEnabled(); } @@ -101,14 +101,14 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle return this.domUtils.showConfirm(template, title, title).then(() => { return this.messagesProvider.removeContact(user.id); - }).catch(() => { + }, () => { // Ignore on cancel. }); } else { return this.messagesProvider.addContact(user.id); } }).catch((error) => { - this.domUtils.showErrorModal(error); + this.domUtils.showErrorModalDefault(error, 'core.error', true); }).finally(() => { this.eventsProvider.trigger(AddonMessagesAddContactUserHandler.UPDATED_EVENT, {userId: user.id}); this.checkButton(user.id).finally(() => { @@ -132,7 +132,7 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle if (isContact) { this.updateButton({ title: 'addon.messages.removecontact', - class: 'mma-messages-removecontact-handler', + class: 'addon-messages-removecontact-handler', icon: 'remove', hidden: false, spinner: false @@ -140,7 +140,7 @@ export class AddonMessagesAddContactUserHandler implements CoreUserProfileHandle } else { this.updateButton({ title: 'addon.messages.addcontact', - class: 'mma-messages-addcontact-handler', + class: 'addon-messages-addcontact-handler', icon: 'add', hidden: false, spinner: false diff --git a/src/addon/messages/providers/user-block-contact-handler.ts b/src/addon/messages/providers/user-block-contact-handler.ts index 1015fe580..ead588acd 100644 --- a/src/addon/messages/providers/user-block-contact-handler.ts +++ b/src/addon/messages/providers/user-block-contact-handler.ts @@ -33,8 +33,8 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand */ static UPDATED_EVENT = 'AddonMessagesBlockContactUserHandler_updated_event'; - name = 'mmaMessages:addContact'; - priority = 800; + name = 'AddonMessages:blockContact'; + priority = 600; type = CoreUserDelegate.TYPE_ACTION; protected disabled = false; @@ -52,9 +52,9 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand /** * Check if handler is enabled. * - * @return {Promise} Promise resolved with true if enabled, rejected or resolved with false otherwise. + * @return {Promise} Promise resolved with true if enabled, rejected or resolved with false otherwise. */ - isEnabled(): Promise { + isEnabled(): Promise { return this.messagesProvider.isPluginEnabled(); } @@ -104,12 +104,12 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand return this.domUtils.showConfirm(template, title, title).then(() => { return this.messagesProvider.blockContact(user.id); - }).catch(() => { + }, () => { // Ignore on cancel. }); } }).catch((error) => { - this.domUtils.showErrorModal(error); + this.domUtils.showErrorModalDefault(error, 'core.error', true); }).finally(() => { this.eventsProvider.trigger(AddonMessagesBlockContactUserHandler.UPDATED_EVENT, {userId: user.id}); this.checkButton(user.id).finally(() => { @@ -133,7 +133,7 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand if (isBlocked) { this.updateButton({ title: 'addon.messages.unblockcontact', - class: 'mma-messages-unblockcontact-handler', + class: 'addon-messages-unblockcontact-handler', icon: 'checkmark-circle', hidden: false, spinner: false @@ -141,7 +141,7 @@ export class AddonMessagesBlockContactUserHandler implements CoreUserProfileHand } else { this.updateButton({ title: 'addon.messages.blockcontact', - class: 'mma-messages-blockcontact-handler', + class: 'addon-messages-blockcontact-handler', icon: 'close-circle', hidden: false, spinner: false diff --git a/src/addon/messages/providers/user-send-message-handler.ts b/src/addon/messages/providers/user-send-message-handler.ts index 4ba84dbd1..1d1838c50 100644 --- a/src/addon/messages/providers/user-send-message-handler.ts +++ b/src/addon/messages/providers/user-send-message-handler.ts @@ -23,7 +23,7 @@ import { AddonMessagesProvider } from './messages'; */ @Injectable() export class AddonMessagesSendMessageUserHandler implements CoreUserProfileHandler { - name = 'mmaMessages:sendMessage'; + name = 'AddonMessages:sendMessage'; priority = 1000; type = CoreUserDelegate.TYPE_COMMUNICATION; diff --git a/src/addon/pushnotifications/providers/delegate.ts b/src/addon/pushnotifications/providers/delegate.ts index 6ecfaaa48..61ca92ce5 100644 --- a/src/addon/pushnotifications/providers/delegate.ts +++ b/src/addon/pushnotifications/providers/delegate.ts @@ -14,20 +14,22 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; +import { Subject } from 'rxjs'; /** - * Service to handle push notifications clicks. + * Service to handle push notifications actions to perform when clicked and received. */ @Injectable() export class AddonPushNotificationsDelegate { protected logger; - protected clickHandlers: { [s: string]: Function } = {}; - protected receiveHandlers: { [s: string]: Function } = {}; + protected observables: { [s: string]: Subject } = {}; protected counterHandlers: { [s: string]: string } = {}; constructor(loggerProvider: CoreLoggerProvider) { this.logger = loggerProvider.getInstance('AddonPushNotificationsDelegate'); + this.observables['click'] = new Subject(); + this.observables['receive'] = new Subject(); } /** @@ -36,15 +38,7 @@ export class AddonPushNotificationsDelegate { * @param {any} notification Notification clicked. */ clicked(notification: any): void { - for (const name in this.clickHandlers) { - const callback = this.clickHandlers[name]; - if (typeof callback == 'function') { - const treated = callback(notification); - if (treated) { - return; // Stop execution when notification is treated. - } - } - } + this.observables['click'].next(notification); } /** @@ -54,60 +48,42 @@ export class AddonPushNotificationsDelegate { * @param {any} notification Notification received. */ received(notification: any): void { - for (const name in this.receiveHandlers) { - const callback = this.receiveHandlers[name]; - if (typeof callback == 'function') { - callback(notification); - } + this.observables['receive'].next(notification); + } + + /** + * Register a push notifications observable for click and receive notification event. + * When a notification is clicked or received, the observable will receive a notification to treat. + * let observer = pushNotificationsDelegate.on('click').subscribe((notification) => { + * ... + * observer.unsuscribe(); + * + * @param {string} eventName Only click and receive are permitted. + * @return {Subject} Observer to subscribe. + */ + on(eventName: string): Subject { + if (typeof this.observables[eventName] == 'undefined') { + const eventNames = Object.keys(this.observables).join(', '); + this.logger.warn(`'${eventName}' event name is not allowed. Use one of the following: '${eventNames}'.`); + + return new Subject(); } - } - /** - * Register a push notifications handler for CLICKS. - * When a notification is clicked, the handler will receive a notification to treat. - * - * @param {string} name Handler's name. - * @param {Function} callback The callback function. Will get as parameter the clicked notification. - * @description - * The handler should return true if the notification is the one expected, false otherwise. - * @see {@link AddonPushNotificationsDelegate#clicked} - */ - registerHandler(name: string, callback: Function): void { - this.logger.debug(`Registered handler '${name}' as CLICK push notification handler.`); - this.clickHandlers[name] = callback; - } - - /** - * Register a push notifications handler for RECEIVE notifications in foreground (cannot tell when it's received in background). - * When a notification is received, the handler will receive a notification to treat. - * - * @param {string} name Handler's name. - * @param {Function} callback The callback function. Will get as parameter the clicked notification. - * @see {@link AddonPushNotificationsDelegate#received} - */ - registerReceiveHandler(name: string, callback: Function): void { - this.logger.debug(`Registered handler '${name}' as RECEIVE push notification handler.`); - this.receiveHandlers[name] = callback; - } - - /** - * Unregister a push notifications handler for RECEIVE notifications. - * - * @param {string} name Handler's name. - */ - unregisterReceiveHandler(name: string): void { - this.logger.debug(`Unregister handler '${name}' from RECEIVE push notification handlers.`); - delete this.receiveHandlers[name]; + return this.observables[eventName]; } /** * Register a push notifications handler for update badge counter. * - * @param {string} name Handler's name. + * @param {string} name Handler's name. */ registerCounterHandler(name: string): void { - this.logger.debug(`Registered handler '${name}' as badge counter handler.`); - this.counterHandlers[name] = name; + if (typeof this.counterHandlers[name] == 'undefined') { + this.logger.debug(`Registered handler '${name}' as badge counter handler.`); + this.counterHandlers[name] = name; + } else { + this.logger.log(`Handler '${name}' as badge counter handler already registered.`); + } } /** diff --git a/src/addon/pushnotifications/providers/pushnotifications.ts b/src/addon/pushnotifications/providers/pushnotifications.ts index 94607105a..75bbee3d4 100644 --- a/src/addon/pushnotifications/providers/pushnotifications.ts +++ b/src/addon/pushnotifications/providers/pushnotifications.ts @@ -36,17 +36,17 @@ export class AddonPushNotificationsProvider { protected logger; protected pushID: string; protected appDB: any; - static COMPONENT = 'mmaPushNotifications'; + static COMPONENT = 'AddonPushNotificationsProvider'; // Variables for database. - protected BADGE_TABLE = 'mma_pushnotifications_badge'; + protected BADGE_TABLE = 'addon_pushnotifications_badge'; protected tablesSchema = [ { name: this.BADGE_TABLE, columns: [ { name: 'siteid', - type: 'INTEGER' + type: 'TEXT' }, { name: 'addon', @@ -84,25 +84,26 @@ export class AddonPushNotificationsProvider { } /** - * Returns options for push notifications based on - * @return {Promise} [description] + * Returns options for push notifications based on device. + * + * @return {Promise} Promise with the push options resolved when done. */ protected getOptions(): Promise { // @todo: CoreSettingsProvider.NOTIFICATION_SOUND return this.configProvider.get('CoreSettingsProvider.NOTIFICATION_SOUND', true).then((soundEnabled) => { return { - android: { - senderID: CoreConfigConstants.gcmpn, - sound: !!soundEnabled - }, - ios: { - alert: 'true', - badge: true, - sound: !!soundEnabled - }, - windows: { - sound: !!soundEnabled - } + android: { + senderID: CoreConfigConstants.gcmpn, + sound: !!soundEnabled + }, + ios: { + alert: 'true', + badge: true, + sound: !!soundEnabled + }, + windows: { + sound: !!soundEnabled + } }; }); } @@ -259,9 +260,9 @@ export class AddonPushNotificationsProvider { return Promise.all(promises).then((counters) => { const total = counters.reduce((previous, counter) => { - // The app badge counter does not support strings, so parse to int before. - return previous + parseInt(counter, 10); - }, 0); + // The app badge counter does not support strings, so parse to int before. + return previous + parseInt(counter, 10); + }, 0); // Set the app badge. return this.badge.set(total).then(() => { diff --git a/src/classes/base-sync.ts b/src/classes/base-sync.ts index ee01ffe4d..e1e70a2c5 100644 --- a/src/classes/base-sync.ts +++ b/src/classes/base-sync.ts @@ -44,7 +44,7 @@ export class CoreSyncBaseProvider { protected syncPromises: { [siteId: string]: { [uniqueId: string]: Promise } } = {}; constructor(component: string, protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider, - protected appProvider: CoreAppProvider) { + protected appProvider: CoreAppProvider, protected syncProvider: CoreSyncProvider) { this.logger = this.loggerProvider.getInstance(component); this.component = component; } @@ -52,12 +52,12 @@ export class CoreSyncBaseProvider { /** * Add an ongoing sync to the syncPromises list. On finish the promise will be removed. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {Promise} promise The promise of the sync to add. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} The sync promise. */ - addOngoingSync(id: number, promise: Promise, siteId?: string): Promise { + addOngoingSync(id: string | number, promise: Promise, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); const uniqueId = this.getUniqueSyncId(id); @@ -76,11 +76,11 @@ export class CoreSyncBaseProvider { /** * If there's an ongoing sync for a certain identifier return it. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise of the current sync or undefined if there isn't any. */ - getOngoingSync(id: number, siteId?: string): Promise { + getOngoingSync(id: string | number, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); if (this.isSyncing(id, siteId)) { @@ -94,59 +94,55 @@ export class CoreSyncBaseProvider { /** * Get the synchronization time. Returns 0 if no time stored. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved with the time. */ - getSyncTime(id: number, siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getRecord(CoreSyncProvider.SYNC_TABLE, { component: this.component, id: id }).then((entry) => { - return entry.time; - }).catch(() => { - return 0; - }); + getSyncTime(id: string | number, siteId?: string): Promise { + return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => { + return entry.time; + }).catch(() => { + return 0; }); } /** * Get the synchronization warnings of an instance. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved with the warnings. */ - getSyncWarnings(id: number, siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - return db.getRecord(CoreSyncProvider.SYNC_TABLE, { component: this.component, id: id }).then((entry) => { - try { - return JSON.parse(entry.warnings); - } catch (ex) { - return []; - } - }).catch(() => { + getSyncWarnings(id: string | number, siteId?: string): Promise { + return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => { + try { + return JSON.parse(entry.warnings); + } catch (ex) { return []; - }); + } + }).catch(() => { + return []; }); } /** * Create a unique identifier from component and id. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @return {string} Unique identifier from component and id. */ - protected getUniqueSyncId(id: number): string { + protected getUniqueSyncId(id: string | number): string { return this.component + '#' + id; } /** * Check if a there's an ongoing syncronization for the given id. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @return {boolean} Whether it's synchronizing. */ - isSyncing(id: number, siteId?: string): boolean { + isSyncing(id: string | number, siteId?: string): boolean { siteId = siteId || this.sitesProvider.getCurrentSiteId(); const uniqueId = this.getUniqueSyncId(id); @@ -157,11 +153,11 @@ export class CoreSyncBaseProvider { /** * Check if a sync is needed: if a certain time has passed since the last time. * - * @param {number} id Unique sync identifier per component. + * @param {string} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved with boolean: whether sync is needed. */ - isSyncNeeded(id: number, siteId?: string): Promise { + isSyncNeeded(id: string, siteId?: string): Promise { return this.getSyncTime(id, siteId).then((time) => { return Date.now() - this.syncInterval >= time; }); @@ -170,59 +166,47 @@ export class CoreSyncBaseProvider { /** * Set the synchronization time. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @param {number} [time] Time to set. If not defined, current time. * @return {Promise} Promise resolved when the time is set. */ - setSyncTime(id: number, siteId?: string, time?: number): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - time = typeof time != 'undefined' ? time : Date.now(); + setSyncTime(id: string | number, siteId?: string, time?: number): Promise { + time = typeof time != 'undefined' ? time : Date.now(); - return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, { time: time }, { component: this.component, id: id }); - }); + return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { time: time }, siteId); } /** * Set the synchronization warnings. * - * @param {number} id Unique sync identifier per component. + * @param {string} id Unique sync identifier per component. * @param {string[]} warnings Warnings to set. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when done. */ - setSyncWarnings(id: number, warnings: string[], siteId?: string): Promise { - return this.sitesProvider.getSiteDb(siteId).then((db) => { - warnings = warnings || []; + setSyncWarnings(id: string, warnings: string[], siteId?: string): Promise { + const warningsText = JSON.stringify(warnings || []); - return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, { warnings: JSON.stringify(warnings) }, - { component: this.component, id: id }); - }); + return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { warnings: warningsText }, siteId); } /** * Execute a sync function on selected sites. * * @param {string} syncFunctionLog Log message to explain the sync function purpose. - * @param {string} syncFunction Sync function to execute. - * @param {any} [params] Object that defines the params that admit the funcion. + * @param {Function} syncFunction Sync function to execute. + * @param {any[]} [params] Array that defines the params that admit the funcion. * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. * @return {Promise} Resolved with siteIds selected. Rejected if offline. */ - syncOnSites(syncFunctionLog: string, syncFunction: string, params?: any, siteId?: string): Promise { + syncOnSites(syncFunctionLog: string, syncFunction: Function, params?: any[], siteId?: string): Promise { if (!this.appProvider.isOnline()) { this.logger.debug(`Cannot sync '${syncFunctionLog}' because device is offline.`); return Promise.reject(null); } - if (!this[syncFunction]) { - this.logger.debug(`Cannot sync '${syncFunctionLog}' function '${syncFunction}' does not exist.`); - - return Promise.reject(null); - } - params = params || {}; - let promise; if (!siteId) { // No site ID defined, sync all sites. @@ -233,12 +217,13 @@ export class CoreSyncBaseProvider { promise = Promise.resolve([siteId]); } + params = params || []; + return promise.then((siteIds) => { const sitePromises = []; siteIds.forEach((siteId) => { - params['siteId'] = siteId; // Execute function for every site selected. - sitePromises.push(this[syncFunction].apply(this, params)); + sitePromises.push(syncFunction.apply(syncFunction, [siteId].concat(params))); }); return Promise.all(sitePromises); @@ -249,11 +234,11 @@ export class CoreSyncBaseProvider { * If there's an ongoing sync for a certain identifier, wait for it to end. * If there's no sync ongoing the promise will be resolved right away. * - * @param {number} id Unique sync identifier per component. + * @param {string | number} id Unique sync identifier per component. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when there's no sync going on for the identifier. */ - waitForSync(id: number, siteId?: string): Promise { + waitForSync(id: string | number, siteId?: string): Promise { const promise = this.getOngoingSync(id, siteId); if (promise) { return promise.catch(() => { @@ -262,5 +247,5 @@ export class CoreSyncBaseProvider { } return Promise.resolve(); - } +} } diff --git a/src/classes/site.ts b/src/classes/site.ts index 276a1a0e7..3c3480f25 100644 --- a/src/classes/site.ts +++ b/src/classes/site.ts @@ -531,7 +531,7 @@ export class CoreSite { } else { this.logger.error(`WS function '${method}' is not available, even in compatibility mode.`); - return Promise.reject(this.wsProvider.createFakeWSError('core.wsfunctionnotavailable', true)); + return Promise.reject(this.utils.createFakeWSError('core.wsfunctionnotavailable', true)); } } @@ -560,7 +560,7 @@ export class CoreSite { data = this.wsProvider.convertValuesToString(data, wsPreSets.cleanUnicode); } catch (e) { // Empty cleaned text found. - return Promise.reject(this.wsProvider.createFakeWSError('core.unicodenotsupportedcleanerror', true)); + return Promise.reject(this.utils.createFakeWSError('core.unicodenotsupportedcleanerror', true)); } return this.getFromCache(method, data, preSets).catch(() => { diff --git a/src/components/search-box/search-box.html b/src/components/search-box/search-box.html index dfac40da9..127de77e6 100644 --- a/src/components/search-box/search-box.html +++ b/src/components/search-box/search-box.html @@ -1,11 +1,11 @@
- - - diff --git a/src/components/search-box/search-box.ts b/src/components/search-box/search-box.ts index 6100ea1d6..2c5110b65 100644 --- a/src/components/search-box/search-box.ts +++ b/src/components/search-box/search-box.ts @@ -38,6 +38,7 @@ export class CoreSearchBoxComponent implements OnInit { @Input() autoFocus?: string | boolean; // Enables/disable Autofocus when entering view. @Input() lengthCheck? = 3; // Check value length before submit. If 0, any string will be submitted. @Input() showClear? = true; // Show/hide clear button. + @Input() disabled? = false; // Disables the input text. @Output() onSubmit: EventEmitter; // Send data when submitting the search form. @Output() onClear?: EventEmitter; // Send event when clearing the search form. diff --git a/src/components/tabs/tabs.scss b/src/components/tabs/tabs.scss index 3872a5055..a7e779cba 100644 --- a/src/components/tabs/tabs.scss +++ b/src/components/tabs/tabs.scss @@ -83,4 +83,8 @@ core-tabs { top: 0; height: 100%; } + ion-content core-tabs core-tab .core-avoid-header ion-content { + top: 0; + height: 100%; + } } diff --git a/src/core/emulator/classes/sqlitedb.ts b/src/core/emulator/classes/sqlitedb.ts index 33f5f3029..90d5654d2 100644 --- a/src/core/emulator/classes/sqlitedb.ts +++ b/src/core/emulator/classes/sqlitedb.ts @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +/* tslint:disable:no-console */ + import { SQLiteDB } from '@classes/sqlitedb'; /** diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index b4406407b..acd3a99eb 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -91,7 +91,6 @@ export class CoreMainMenuPage implements OnDestroy { }); if (tab) { tab.badge = data.badge; - tab.loading = false; } }, site.getId()); diff --git a/src/core/mainmenu/pages/more/more.ts b/src/core/mainmenu/pages/more/more.ts index be6199ffd..0c396bfd9 100644 --- a/src/core/mainmenu/pages/more/more.ts +++ b/src/core/mainmenu/pages/more/more.ts @@ -68,7 +68,6 @@ export class CoreMainMenuMorePage implements OnDestroy { }); if (handler) { handler.badge = data.badge; - handler.loading = false; } }, this.sitesProvider.getCurrentSiteId()); } diff --git a/src/core/settings/pages/list/list.html b/src/core/settings/pages/list/list.html index 724c5f9ee..b26591fe8 100644 --- a/src/core/settings/pages/list/list.html +++ b/src/core/settings/pages/list/list.html @@ -6,32 +6,32 @@ - +

{{ 'core.settings.general' | translate }}

-
- + +

{{ 'core.settings.spaceusage' | translate }}

-
- + +

{{ 'core.settings.synchronization' | translate }}

-
- + +

{{ 'core.sharedfiles.sharedfiles' | translate }}

-
+

{{ handler.title | translate}}

- +

{{ 'core.settings.about' | translate }}

-
+
diff --git a/src/core/settings/pages/list/list.ts b/src/core/settings/pages/list/list.ts index 6133e8e26..28803a351 100644 --- a/src/core/settings/pages/list/list.ts +++ b/src/core/settings/pages/list/list.ts @@ -29,7 +29,6 @@ export class CoreSettingsListPage { @ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent; handlers: CoreSettingsHandlerData[]; - areHandlersLoaded: Function; isIOS: boolean; selectedPage: string; @@ -61,7 +60,7 @@ export class CoreSettingsListPage { */ openHandler(page: string, params?: any): void { this.selectedPage = page; - this.navCtrl.push(page, params); + this.splitviewCtrl.push(page, params); } } diff --git a/src/core/user/pages/profile/profile.html b/src/core/user/pages/profile/profile.html index 9c0609bf7..b7d1306c3 100644 --- a/src/core/user/pages/profile/profile.html +++ b/src/core/user/pages/profile/profile.html @@ -52,9 +52,9 @@ - diff --git a/src/core/user/pages/profile/profile.scss b/src/core/user/pages/profile/profile.scss index c83a39afe..cb0594deb 100644 --- a/src/core/user/pages/profile/profile.scss +++ b/src/core/user/pages/profile/profile.scss @@ -31,4 +31,10 @@ page-core-user-profile { } } } + + .core-user-profile-handler { + ion-spinner { + margin-left: 0.3em; + } + } } \ No newline at end of file diff --git a/src/core/user/pages/profile/profile.ts b/src/core/user/pages/profile/profile.ts index 5da56ec3d..08dd1faf7 100644 --- a/src/core/user/pages/profile/profile.ts +++ b/src/core/user/pages/profile/profile.ts @@ -18,11 +18,11 @@ import { CoreUserProvider } from '../../providers/user'; import { CoreUserHelperProvider } from '../../providers/helper'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { TranslateService } from '@ngx-translate/core'; -import { CoreCoursesProvider } from '../../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; -import { CoreFileUploaderHelperProvider } from '../../../fileuploader/providers/helper'; +import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper'; import { CoreUserDelegate, CoreUserProfileHandlerData } from '../../providers/user-delegate'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; diff --git a/src/core/user/user.module.ts b/src/core/user/user.module.ts index 38adb10f6..bfec54d89 100644 --- a/src/core/user/user.module.ts +++ b/src/core/user/user.module.ts @@ -20,11 +20,11 @@ import { CoreUserHelperProvider } from './providers/helper'; import { CoreUserProfileMailHandler } from './providers/user-handler'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; import { CoreUserProfileLinkHandler } from './providers/user-link-handler'; import { CoreUserParticipantsCourseOptionHandler } from './providers/course-option-handler'; import { CoreUserParticipantsLinkHandler } from './providers/participants-link-handler'; -import { CoreCourseOptionsDelegate } from '../course/providers/options-delegate'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; import { CoreUserComponentsModule } from './components/components.module'; @NgModule({ diff --git a/src/pipes/date-day-or-time.ts b/src/pipes/date-day-or-time.ts index 5a77f0f7b..ff9607410 100644 --- a/src/pipes/date-day-or-time.ts +++ b/src/pipes/date-day-or-time.ts @@ -59,7 +59,7 @@ export class CoreDateDayOrTimePipe implements PipeTransform { } return moment(timestamp * 1000).calendar(null, { - sameDay: 'LT', //this.translate.instant('core.dftimedate'), + sameDay: 'LT', lastDay: this.translate.instant('core.dflastweekdate'), lastWeek: this.translate.instant('core.dflastweekdate'), sameElse: 'L' diff --git a/src/providers/local-notifications.ts b/src/providers/local-notifications.ts index 5d4bdd647..4bd55cfac 100644 --- a/src/providers/local-notifications.ts +++ b/src/providers/local-notifications.ts @@ -42,10 +42,13 @@ export interface CoreILocalNotification extends ILocalNotification { } /* - Generated class for the LocalNotificationsProvider provider. - - See https://angular.io/guide/dependency-injection for more info on providers - and Angular DI. + * Generated class for the LocalNotificationsProvider provider. + * + * See https://angular.io/guide/dependency-injection for more info on providers + * and Angular DI. + * + * @todo We might have to translate the old component name to the new one. + * Otherwise the unique ID of local notifications could change. */ @Injectable() export class CoreLocalNotificationsProvider { diff --git a/src/providers/sync.ts b/src/providers/sync.ts index ea8d6ee75..2003b488d 100644 --- a/src/providers/sync.ts +++ b/src/providers/sync.ts @@ -23,9 +23,9 @@ import { CoreSitesProvider } from './sites'; export class CoreSyncProvider { // Variables for the database. - static SYNC_TABLE = 'sync'; + protected SYNC_TABLE = 'sync'; protected tableSchema = { - name: CoreSyncProvider.SYNC_TABLE, + name: this.SYNC_TABLE, columns: [ { name: 'component', @@ -34,7 +34,7 @@ export class CoreSyncProvider { }, { name: 'id', - type: 'INTEGER', + type: 'TEXT', notNull: true }, { @@ -116,6 +116,36 @@ export class CoreSyncProvider { } } + /** + * Returns a sync record. + * @param {string} component Component name. + * @param {string | number} id Unique ID per component. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Record if found or reject. + */ + getSyncRecord(component: string, id: string | number, siteId?: string): Promise { + return this.sitesProvider.getSiteDb(siteId).then((db) => { + return db.getRecord(this.SYNC_TABLE, { component: component, id: id }); + }); + } + + /** + * Inserts or Updates info of a sync record. + * @param {string} component Component name. + * @param {string | number} id Unique ID per component. + * @param {any} data Data that updates the record. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with done. + */ + insertOrUpdateSyncRecord(component: string, id: string | number, data: any, siteId?: string): Promise { + return this.sitesProvider.getSiteDb(siteId).then((db) => { + data.component = component; + data.id = id; + + return db.insertOrUpdateRecord(this.SYNC_TABLE, data, { component: component, id: id }); + }); + } + /** * Convenience function to create unique identifiers for a component and id. * diff --git a/src/providers/utils/utils.ts b/src/providers/utils/utils.ts index e1bd4a08c..ec57e20d4 100644 --- a/src/providers/utils/utils.ts +++ b/src/providers/utils/utils.ts @@ -23,6 +23,7 @@ import { CoreEventsProvider } from '../events'; import { CoreLoggerProvider } from '../logger'; import { TranslateService } from '@ngx-translate/core'; import { CoreLangProvider } from '../lang'; +import { CoreWSError } from '../ws'; /** * Deferred promise. It's similar to the result of $q.defer() in AngularJS. @@ -601,28 +602,31 @@ export class CoreUtilsProvider { return typeof value != 'undefined' && (value === true || value === 'true' || parseInt(value, 10) === 1); } + /** + * Create a "fake" WS error for local errors. + * + * @param {string} message The message to include in the error. + * @param {boolean} [needsTranslate] If the message needs to be translated. + * @return {CoreWSError} Fake WS error. + */ + createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError { + if (needsTranslate) { + message = this.translate.instant(message); + } + + return { + message: message + }; + } + /** * Given an error returned by a WS call, check if the error is generated by the app or it has been returned by the WebSwervice. * - * @param {string} error Error to check. + * @param {any} error Error to check. * @return {boolean} Whether the error was returned by the WebService. */ - isWebServiceError(error: string): boolean { - const localErrors = [ - this.translate.instant('core.wsfunctionnotavailable'), - this.translate.instant('core.lostconnection'), - this.translate.instant('core.userdeleted'), - this.translate.instant('core.unexpectederror'), - this.translate.instant('core.networkerrormsg'), - this.translate.instant('core.serverconnection'), - this.translate.instant('core.errorinvalidresponse'), - this.translate.instant('core.sitemaintenance'), - this.translate.instant('core.upgraderunning'), - this.translate.instant('core.nopasswordchangeforced'), - this.translate.instant('core.unicodenotsupported') - ]; - - return error && localErrors.indexOf(error) == -1; + isWebServiceError(error: any): boolean { + return typeof error.errorcode == 'undefined'; } /** diff --git a/src/providers/ws.ts b/src/providers/ws.ts index 6e9a3a516..462678683 100644 --- a/src/providers/ws.ts +++ b/src/providers/ws.ts @@ -174,9 +174,9 @@ export class CoreWSProvider { let siteUrl; if (!preSets) { - return Promise.reject(this.createFakeWSError('core.unexpectederror', true)); + return Promise.reject(this.utils.createFakeWSError('core.unexpectederror', true)); } else if (!this.appProvider.isOnline()) { - return Promise.reject(this.createFakeWSError('core.networkerrormsg', true)); + return Promise.reject(this.utils.createFakeWSError('core.networkerrormsg', true)); } preSets.typeExpected = preSets.typeExpected || 'object'; @@ -320,23 +320,6 @@ export class CoreWSProvider { return result; } - /** - * Create a "fake" WS error for local errors. - * - * @param {string} message The message to include in the error. - * @param {boolean} [needsTranslate] If the message needs to be translated. - * @return {CoreWSError} Fake WS error. - */ - createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError { - if (needsTranslate) { - message = this.translate.instant(message); - } - - return { - message: message - }; - } - /** * Downloads a file from Moodle using Cordova File API. * @@ -532,11 +515,11 @@ export class CoreWSProvider { } if (!data) { - return Promise.reject(this.createFakeWSError('core.serverconnection', true)); + return Promise.reject(this.utils.createFakeWSError('core.serverconnection', true)); } else if (typeof data != preSets.typeExpected) { this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`); - return Promise.reject(this.createFakeWSError('core.errorinvalidresponse', true)); + return Promise.reject(this.utils.createFakeWSError('core.errorinvalidresponse', true)); } if (typeof data.exception !== 'undefined') { @@ -544,7 +527,7 @@ export class CoreWSProvider { } if (typeof data.debuginfo != 'undefined') { - return Promise.reject(this.createFakeWSError('Error. ' + data.message)); + return Promise.reject(this.utils.createFakeWSError('Error. ' + data.message)); } return data; @@ -572,7 +555,7 @@ export class CoreWSProvider { return retryPromise; } - return Promise.reject(this.createFakeWSError('core.serverconnection', true)); + return Promise.reject(this.utils.createFakeWSError('core.serverconnection', true)); }); promise = this.setPromiseHttp(promise, 'post', preSets.siteUrl, ajaxData);