From 790dbbf7cdf2734d972bdf94f2e49a0e7c5436ba Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Wed, 12 Dec 2018 15:40:31 +0100 Subject: [PATCH 1/7] MOBILE-2734 messages: New web services for conversation counts --- .../components/discussions/discussions.ts | 39 ++-- .../group-conversations.html | 6 +- .../group-conversations.ts | 71 +++++-- src/addon/messages/pages/index/index.html | 5 +- .../messages/providers/mainmenu-handler.ts | 101 +++++----- src/addon/messages/providers/messages.ts | 190 ++++++++++++++---- 6 files changed, 284 insertions(+), 128 deletions(-) diff --git a/src/addon/messages/components/discussions/discussions.ts b/src/addon/messages/components/discussions/discussions.ts index 8e90279ea..bf3eb9f21 100644 --- a/src/addon/messages/components/discussions/discussions.ts +++ b/src/addon/messages/components/discussions/discussions.ts @@ -53,7 +53,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 appProvider: CoreAppProvider, platform: Platform, private utils: CoreUtilsProvider, pushNotificationsDelegate: AddonPushNotificationsDelegate) { this.search.loading = translate.instant('core.searching'); @@ -91,17 +91,13 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { // A discussion has been read reset counter. discussion.unread = false; - // Discussions changed, invalidate them. - this.messagesProvider.invalidateDiscussionsCache(); + // Conversations changed, invalidate them and refresh unread counts. + this.messagesProvider.invalidateConversations(); + this.messagesProvider.refreshUnreadConversationCounts(); } } }, this.siteId); - // Update discussions when cron read is executed. - this.cronObserver = eventsProvider.on(AddonMessagesProvider.READ_CRON_EVENT, (data) => { - this.refreshData(); - }, this.siteId); - // Refresh the view when the app is resumed. this.appResumeSubscription = platform.resume.subscribe(() => { if (!this.loaded) { @@ -117,7 +113,8 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { 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(); + // Don't refresh unread counts, it's refreshed from the main menu handler in this case. + this.refreshData(null, false); } }); } @@ -143,14 +140,20 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { * Refresh the data. * * @param {any} [refresher] Refresher. + * @param {boolean} [refreshUnreadCounts=true] Whteher to refresh unread counts. * @return {Promise} Promise resolved when done. */ - refreshData(refresher?: any): Promise { - return this.messagesProvider.invalidateDiscussionsCache().then(() => { + refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise { + const promises = []; + promises.push(this.messagesProvider.invalidateDiscussionsCache()); + + if (refreshUnreadCounts) { + promises.push(this.messagesProvider.invalidateUnreadConversationCounts()); + } + + return this.utils.allPromises(promises).finally(() => { return this.fetchData().finally(() => { if (refresher) { - // Actions to take if refresh comes from the user. - this.eventsProvider.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, undefined, this.siteId); refresher.complete(); } }); @@ -166,7 +169,9 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { this.loadingMessage = this.loadingMessages; this.search.enabled = this.messagesProvider.isSearchMessagesEnabled(); - return this.messagesProvider.getDiscussions().then((discussions) => { + const promises = []; + + promises.push(this.messagesProvider.getDiscussions().then((discussions) => { // Convert to an array for sorting. const discussionsSorted = []; for (const userId in discussions) { @@ -177,7 +182,11 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy { this.discussions = discussionsSorted.sort((a, b) => { return b.message.timecreated - a.message.timecreated; }); - }).catch((error) => { + })); + + promises.push(this.messagesProvider.getUnreadConversationCounts()); + + return Promise.all(promises).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); }).finally(() => { this.loaded = true; diff --git a/src/addon/messages/pages/group-conversations/group-conversations.html b/src/addon/messages/pages/group-conversations/group-conversations.html index 62ec6e659..a9d9c71a1 100644 --- a/src/addon/messages/pages/group-conversations/group-conversations.html +++ b/src/addon/messages/pages/group-conversations/group-conversations.html @@ -31,7 +31,7 @@ {{ 'core.favourites' | translate }} ({{ favourites.count }}) - + {{ favourites.unread }}
@@ -47,7 +47,7 @@ {{ 'addon.messages.groupmessages' | translate }} ({{ group.count }}) - + {{ group.unread }}
@@ -62,7 +62,7 @@ {{ 'addon.messages.messages' | translate }} ({{ individual.count }}) - + {{ individual.unread }}
diff --git a/src/addon/messages/pages/group-conversations/group-conversations.ts b/src/addon/messages/pages/group-conversations/group-conversations.ts index 333895546..4ab86a489 100644 --- a/src/addon/messages/pages/group-conversations/group-conversations.ts +++ b/src/addon/messages/pages/group-conversations/group-conversations.ts @@ -44,15 +44,21 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { contactRequestsCount = 0; favourites: any = { type: null, - favourites: true + favourites: true, + count: 0, + unread: 0 }; group: any = { type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP, - favourites: false + favourites: false, + count: 0, + unread: 0 }; individual: any = { type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, - favourites: false + favourites: false, + count: 0, + unread: 0 }; typeIndividual = AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL; @@ -70,9 +76,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { protected contactRequestsCountObserver: any; protected memberInfoObserver: any; - constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService, + constructor(eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService, private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, - private navCtrl: NavController, platform: Platform, utils: CoreUtilsProvider, + private navCtrl: NavController, platform: Platform, private utils: CoreUtilsProvider, pushNotificationsDelegate: AddonPushNotificationsDelegate, private messagesOffline: AddonMessagesOfflineProvider, private userProvider: CoreUserProvider) { @@ -119,17 +125,13 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // A conversation has been read reset counter. conversation.unreadcount = 0; - // Conversations changed, invalidate them. + // Conversations changed, invalidate them and refresh unread counts. this.messagesProvider.invalidateConversations(); + this.messagesProvider.refreshUnreadConversationCounts(); } } }, this.siteId); - // Update conversations when cron read is executed. - this.cronObserver = eventsProvider.on(AddonMessagesProvider.READ_CRON_EVENT, (data) => { - this.refreshData(); - }, this.siteId); - // Load a discussion if we receive an event to do so. this.openConversationObserver = eventsProvider.on(AddonMessagesProvider.OPEN_CONVERSATION_EVENT, (data) => { if (data.conversationId || data.userId) { @@ -157,10 +159,18 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { 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(); + // Don't refresh unread counts, it's refreshed from the main menu handler in this case. + this.refreshData(null, false); } }); + // Update unread conversation counts. + this.cronObserver = eventsProvider.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { + this.favourites.unread = data.favourites; + this.individual.unread = data.individual; + this.group.unread = data.group; + }, this.siteId); + // Update the contact requests badge. this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { this.contactRequestsCount = data.count; @@ -215,8 +225,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { } } }); - - this.messagesProvider.getContactRequestsCount(this.siteId); // Badge is updated by the observer. } /** @@ -234,6 +242,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { promises.push(this.fetchDataForOption(this.favourites, false)); promises.push(this.fetchDataForOption(this.group, false)); promises.push(this.fetchDataForOption(this.individual, false)); + promises.push(this.fetchConversationCounts()); + promises.push(this.messagesProvider.getUnreadConversationCounts()); // View updated by the event observer. + promises.push(this.messagesProvider.getContactRequestsCount()); // View updated by the event observer. promises.push(this.messagesOffline.getAllMessages().then((messages) => { offlineMessages = messages; })); @@ -289,7 +300,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { if (loadingMore) { option.conversations = option.conversations.concat(data.conversations); } else { - option.count = data.canLoadMore ? AddonMessagesProvider.LIMIT_MESSAGES + '+' : data.conversations.length; option.conversations = data.conversations; } @@ -298,6 +308,19 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { }); } + /** + * Fetch conversation counts. + * + * @return {Promise} Promise resolved when done. + */ + protected fetchConversationCounts(): Promise { + return this.messagesProvider.getConversationCounts().then((counts) => { + this.favourites.count = counts.favourites; + this.individual.count = counts.individual; + this.group.count = counts.group; + }); + } + /** * Find a conversation in the list of loaded conversations. * @@ -494,15 +517,23 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { * Refresh the data. * * @param {any} [refresher] Refresher. + * @param {booleam} [refreshUnreadCounts=true] Whether to refresh unread counts. * @return {Promise} Promise resolved when done. */ - refreshData(refresher?: any): Promise { - return this.messagesProvider.invalidateConversations().then(() => { + refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise { + const promises = [ + this.messagesProvider.invalidateConversations(), + this.messagesProvider.invalidateConversationCounts(), + this.messagesProvider.invalidateContactRequestsCountCache() + ]; + + if (refreshUnreadCounts) { + promises.push(this.messagesProvider.invalidateUnreadConversationCounts()); + } + + return this.utils.allPromises(promises).finally(() => { return this.fetchData().finally(() => { if (refresher) { - // Actions to take if refresh comes from the user. - this.eventsProvider.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, undefined, this.siteId); - this.messagesProvider.refreshContactRequestsCount(this.siteId); refresher.complete(); } }); diff --git a/src/addon/messages/pages/index/index.html b/src/addon/messages/pages/index/index.html index d20964603..29a434166 100644 --- a/src/addon/messages/pages/index/index.html +++ b/src/addon/messages/pages/index/index.html @@ -1,7 +1,10 @@ {{ 'addon.messages.messages' | translate }} - + + + + diff --git a/src/addon/messages/providers/mainmenu-handler.ts b/src/addon/messages/providers/mainmenu-handler.ts index 588177186..36df9b041 100644 --- a/src/addon/messages/providers/mainmenu-handler.ts +++ b/src/addon/messages/providers/mainmenu-handler.ts @@ -43,38 +43,41 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr loading: true }; - protected updating = false; + protected unreadCount = 0; + protected contactRequestsCount = 0; + protected orMore = false; constructor(private messagesProvider: AddonMessagesProvider, private sitesProvider: CoreSitesProvider, - private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, + eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, private localNotificationsProvider: CoreLocalNotificationsProvider, private textUtils: CoreTextUtilsProvider, private pushNotificationsProvider: AddonPushNotificationsProvider, utils: CoreUtilsProvider, pushNotificationsDelegate: AddonPushNotificationsDelegate, private emulatorHelper: CoreEmulatorHelperProvider) { - eventsProvider.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => { - this.updateBadge(data.siteId); - }); - - eventsProvider.on(AddonMessagesProvider.READ_CRON_EVENT, (data) => { + eventsProvider.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => { + this.unreadCount = data.favourites + data.individual + data.group; + this.orMore = data.orMore; this.updateBadge(data.siteId); }); eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { - this.updateBadge(data.siteId, data.count); + this.contactRequestsCount = data.count; + this.updateBadge(data.siteId); }); // Reset info on logout. eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => { + this.unreadCount = 0; + this.contactRequestsCount = 0; + this.orMore = false; this.handler.badge = ''; this.handler.loading = true; - this.updating = false; }); // If a message push notification is received, refresh the count. 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); + this.refreshBadge(notification.site); } }); @@ -101,67 +104,62 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr 'AddonMessagesGroupConversationsPage' : 'AddonMessagesIndexPage'; if (this.handler.loading) { - this.updateBadge(); + this.refreshBadge(); } return this.handler; } /** - * Triggers an update for the badge number and loading status. Mandatory if showBadge is enabled. + * Refreshes badge number. * * @param {string} [siteId] Site ID or current Site if undefined. - * @param {number} [contactRequestsCount] Number of contact requests, if known. + * @param {boolean} [unreadOnly] If true only the unread conversations count is refreshed. + * @return {Promise} Resolve when done. */ - updateBadge(siteId?: string, contactRequestsCount?: number): void { + refreshBadge(siteId?: string, unreadOnly?: boolean): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); if (!siteId) { return; } - if (this.updating) { - // An update is already in prgoress. - return; - } - - this.updating = true; - const promises = []; - let unreadCount = 0; - let unreadPlus = false; - promises.push(this.messagesProvider.getUnreadConversationsCount(undefined, siteId).then((unread) => { - unreadCount = parseInt(unread, 10); - unreadPlus = (typeof unread === 'string' && unread.slice(-1) === '+'); - }).catch(() => { - // Ignore error. + promises.push(this.messagesProvider.refreshUnreadConversationCounts(siteId).catch(() => { + this.unreadCount = 0; + this.orMore = false; })); - // Get the number of contact requests in 3.6+ sites if needed. - if (contactRequestsCount == null && this.messagesProvider.isGroupMessagingEnabled()) { - promises.push(this.messagesProvider.getContactRequestsCount(siteId).then((count) => { - contactRequestsCount = count; - }).catch(() => { - // Ignore errors + // Refresh the number of contact requests in 3.6+ sites. + if (!unreadOnly && this.messagesProvider.isGroupMessagingEnabled()) { + promises.push(this.messagesProvider.refreshContactRequestsCount(siteId).catch(() => { + this.contactRequestsCount = 0; })); } - Promise.all(promises).then(() => { - const totalCount = unreadCount + (contactRequestsCount || 0); - if (totalCount > 0) { - this.handler.badge = totalCount + (unreadPlus ? '+' : ''); - } else { - this.handler.badge = ''; - } - - // Update badge. - this.pushNotificationsProvider.updateAddonCounter('AddonMessages', totalCount, siteId); - }).finally(() => { + return Promise.all(promises).finally(() => { + this.updateBadge(siteId); this.handler.loading = false; - this.updating = false; }); } + /** + * Update badge number and push notifications counter from loaded data. + * + * @param {string} siteId Site ID. + */ + updateBadge(siteId: string): void { + const totalCount = this.unreadCount + (this.contactRequestsCount || 0); + if (totalCount > 0) { + this.handler.badge = totalCount + (this.orMore ? '+' : ''); + } else { + this.handler.badge = ''; + } + + // Update push notifications badge. + this.pushNotificationsProvider.updateAddonCounter('AddonMessages', totalCount, siteId); + } + /** * Execute the process. * Receives the ID of the site affected, undefined for all sites. @@ -171,7 +169,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr */ execute(siteId?: string): Promise { if (this.sitesProvider.isCurrentSite(siteId)) { - this.eventsProvider.trigger(AddonMessagesProvider.READ_CRON_EVENT, {}, siteId); + this.refreshBadge(); } if (this.appProvider.isDesktop() && this.localNotificationsProvider.isAvailable()) { @@ -199,8 +197,13 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr */ isSync(): boolean { // This is done to use only wifi if using the fallback function. - // In desktop it is always sync, since it fetches messages to see if there's a new one. - return !this.messagesProvider.isMessageCountEnabled() || this.appProvider.isDesktop(); + + if (this.appProvider.isDesktop()) { + // In desktop it is always sync, since it fetches messages to see if there's a new one. + return true; + } + + return !this.messagesProvider.isMessageCountEnabled() && !this.messagesProvider.isGroupMessagingEnabled(); } /** diff --git a/src/addon/messages/providers/messages.ts b/src/addon/messages/providers/messages.ts index 1ae0b3453..b2e26ccbd 100644 --- a/src/addon/messages/providers/messages.ts +++ b/src/addon/messages/providers/messages.ts @@ -33,11 +33,11 @@ export class AddonMessagesProvider { protected LIMIT_MESSAGES = AddonMessagesProvider.LIMIT_MESSAGES; 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 OPEN_CONVERSATION_EVENT = 'addon_messages_open_conversation_event'; // Notify that a conversation should be opened. static SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event'; static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event'; static MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_event'; + static UNREAD_CONVERSATION_COUNTS_EVENT = 'addon_messages_unread_conversation_counts_event'; static CONTACT_REQUESTS_COUNT_EVENT = 'addon_messages_contact_requests_count_event'; static POLL_INTERVAL = 10000; static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation'; @@ -384,6 +384,15 @@ export class AddonMessagesProvider { return this.ROOT_CACHE_KEY + 'count:' + userId; } + /** + * Get the cache key for unread conversation counts. + * + * @return {string} Cache key. + */ + protected getCacheKeyForUnreadConversationCounts(): string { + return this.ROOT_CACHE_KEY + 'unreadConversationCounts'; + } + /** * Get the cache key for the list of discussions. * @@ -449,6 +458,15 @@ export class AddonMessagesProvider { return this.getCommonCacheKeyForUserConversations(userId) + ':' + type + ':' + favourites; } + /** + * Get cache key for conversation counts. + * + * @return {string} Cache key. + */ + protected getCacheKeyForConversationCounts(): string { + return this.ROOT_CACHE_KEY + 'conversationCounts'; + } + /** * Get cache key for member info. * @@ -943,6 +961,31 @@ export class AddonMessagesProvider { }); } + /** + * Get conversation counts by type. + * + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise} Promise resolved with favourite, individual and group conversation counts. + * @since 3.6 + */ + getConversationCounts(siteId?: string): Promise<{favourites: number, individual: number, group: number}> { + return this.sitesProvider.getSite(siteId).then((site) => { + const preSets = { + cacheKey: this.getCacheKeyForConversationCounts(), + }; + + return site.read('core_message_get_conversation_counts', {}, preSets).then((result) => { + const counts = { + favourites: result.favourites, + individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], + group: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP] + }; + + return counts; + }); + }); + } + /** * Return the current user's discussion with another user. * @@ -1292,58 +1335,77 @@ export class AddonMessagesProvider { } /** - * Get unread conversations count. Do not cache calls. + * Get unread conversation counts by type. * - * @param {number} [userId] The user id who received the message. If not defined, use current user. - * @param {string} [siteId] Site ID. If not defined, use current site. - * @return {Promise} Promise resolved with the message unread count. + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise} Resolved with the unread favourite, individual and group conversation counts. */ - getUnreadConversationsCount(userId?: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + getUnreadConversationCounts(siteId?: string): + Promise<{favourites: number, individual: number, group: number, orMore?: boolean}> { - // @since 3.2 - if (site.wsAvailable('core_message_get_unread_conversations_count')) { + return this.sitesProvider.getSite(siteId).then((site) => { + let promise: Promise<{favourites: number, individual: number, group: number, orMore?: boolean}>; + + if (this.isGroupMessagingEnabled()) { + // @since 3.6 + const preSets = { + cacheKey: this.getCacheKeyForUnreadConversationCounts(), + }; + + promise = site.read('core_message_get_unread_conversation_counts', {}, preSets).then((result) => { + return { + favourites: result.favourites, + individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], + group: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP] + }; + }); + + } else if (this.isMessageCountEnabled()) { + // @since 3.2 const params = { - useridto: userId + useridto: site.getUserId(), }, preSets = { - cacheKey: this.getCacheKeyForMessageCount(userId), - getFromCache: false, - emergencyCache: true, - saveToCache: true, + cacheKey: this.getCacheKeyForMessageCount(site.getUserId()), typeExpected: 'number' }; - return site.read('core_message_get_unread_conversations_count', params, preSets).catch(() => { - // Return no messages if the call fails. - return 0; + promise = site.read('core_message_get_unread_conversations_count', params, preSets).then((count) => { + return { favourites: 0, individual: count, group: 0 }; + }); + } else { + // Fallback call. + const params = { + read: 0, + limitfrom: 0, + limitnum: this.LIMIT_MESSAGES + 1, + useridto: site.getUserId(), + useridfrom: 0, + }; + + promise = this.getMessages(params, undefined, false, siteId).then((response) => { + // Count the discussions by filtering same senders. + const discussions = {}; + response.messages.forEach((message) => { + discussions[message.useridto] = 1; + }); + + const count = Object.keys(discussions).length; + + return { + favourites: 0, + individual: count, + group: 0, + orMore: count > this.LIMIT_MESSAGES + }; }); } - // Fallback call. - const params = { - read: 0, - limitfrom: 0, - limitnum: this.LIMIT_MESSAGES + 1, - useridto: userId, - useridfrom: 0, - }; + return promise.then((counts) => { + // Notify the new counts so all views are updated. + this.eventsProvider.trigger(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, counts, site.id); - return this.getMessages(params, undefined, false, siteId).then((response) => { - // Count the discussions by filtering same senders. - const discussions = {}; - - response.messages.forEach((message) => { - discussions[message.useridto] = 1; - }); - const count = Object.keys(discussions).length; - - // Add + sign if there are more than the limit reachable. - return (count > this.LIMIT_MESSAGES) ? count + '+' : count; - }).catch(() => { - // Return no messages if the call fails. - return 0; + return counts; }); }); } @@ -1535,6 +1597,18 @@ export class AddonMessagesProvider { }); } + /** + * Invalidate conversation counts cache. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Resolved when done. + */ + invalidateConversationCounts(siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCacheKeyForConversationCounts()); + }); + } + /** * Invalidate discussion cache. * @@ -1619,6 +1693,25 @@ export class AddonMessagesProvider { ]); } + /** + * Invalidate unread conversation counts cache. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Resolved when done. + */ + invalidateUnreadConversationCounts(siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + if (this.isGroupMessagingEnabled()) { + // @since 3.6 + return site.invalidateWsCacheForKey(this.getCacheKeyForUnreadConversationCounts()); + + } else if (this.isMessageCountEnabled()) { + // @since 3.2 + return site.invalidateWsCacheForKey(this.getCacheKeyForMessageCount(site.getUserId())); + } + }); + } + /** * Checks if the a user is blocked by the current user. * @@ -1816,6 +1909,23 @@ export class AddonMessagesProvider { }); } + /** + * Refresh unread conversation counts and trigger event. + * + * @param {string} [siteId] Site ID. If not defined, use current site. + * @param {number} [conversationId] ID of the conversation that was read. + * @param {number} [userId] ID of ther other user of the conversation that was read. + * @return {Promise} Resolved with the unread favourite, individual and group conversation counts. + */ + refreshUnreadConversationCounts(siteId?: string, conversationId?: number, userId?: number): + Promise<{favourites: number, individual: number, group: number, orMore?: boolean}> { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + return this.invalidateUnreadConversationCounts(siteId).then(() => { + return this.getUnreadConversationCounts(siteId); + }); + } + /** * Remove a contact. * From f8addeb408e838d5a3c8468acc6bb188fe0d9436 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Wed, 12 Dec 2018 16:23:09 +0100 Subject: [PATCH 2/7] MOBILE-2734 messages: Fix opening group conversations in search page --- src/addon/messages/pages/search/search.html | 2 +- src/addon/messages/pages/search/search.ts | 24 +++++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/addon/messages/pages/search/search.html b/src/addon/messages/pages/search/search.html index d96fef1f8..04045f1be 100644 --- a/src/addon/messages/pages/search/search.html +++ b/src/addon/messages/pages/search/search.html @@ -30,7 +30,7 @@ - +

diff --git a/src/addon/messages/pages/search/search.ts b/src/addon/messages/pages/search/search.ts index 205d10427..6e51b1e20 100644 --- a/src/addon/messages/pages/search/search.ts +++ b/src/addon/messages/pages/search/search.ts @@ -60,7 +60,7 @@ export class AddonMessagesSearchPage implements OnDestroy { loadingMore: false, loadMoreError: false }; - selectedUserId = null; + selectedResult = null; protected memberInfoObserver; @@ -193,11 +193,11 @@ export class AddonMessagesSearchPage implements OnDestroy { if (!loadMore) { if (this.contacts.results.length > 0) { - this.openDiscussion(this.contacts.results[0].id, true); + this.openConversation(this.contacts.results[0], true); } else if (this.nonContacts.results.length > 0) { - this.openDiscussion(this.nonContacts.results[0].id, true); + this.openConversation(this.nonContacts.results[0], true); } else if (this.messages.results.length > 0) { - this.openDiscussion(this.messages.results[0].userid, true); + this.openConversation(this.messages.results[0], true); } } }).catch((error) => { @@ -223,15 +223,21 @@ export class AddonMessagesSearchPage implements OnDestroy { } /** - * Open a discussion in the split view. + * Open a conversation in the split view. * - * @param {number} userId User id. + * @param {any} result User or message. * @param {boolean} [onInit=false] Whether the tser was selected on initial load. */ - openDiscussion(userId: number, onInit: boolean = false): void { + openConversation(result: any, onInit: boolean = false): void { if (!onInit || this.splitviewCtrl.isOn()) { - this.selectedUserId = userId; - this.splitviewCtrl.push('AddonMessagesDiscussionPage', { userId }); + this.selectedResult = result; + const params: any = {}; + if (result.conversationid) { + params.conversationId = result.conversationid; + } else { + params.userId = result.id; + } + this.splitviewCtrl.push('AddonMessagesDiscussionPage', params); } } From f6145bfac607d55146c37e6a59a7ee704e93bb9b Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 13 Dec 2018 10:04:18 +0100 Subject: [PATCH 3/7] MOBILE-2734 messages: Fix empty header when conversation doesn't exist --- .../messages/pages/discussion/discussion.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index d4f6508da..79562599b 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -205,7 +205,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { // Synchronize messages if needed. return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => { // Ignore errors. - }).then((warnings) => { + }).then((warnings): Promise => { if (warnings && warnings[0]) { this.domUtils.showErrorModal(warnings[0]); } @@ -213,22 +213,26 @@ export class AddonMessagesDiscussionPage implements OnDestroy { if (this.groupMessagingEnabled) { // Get the conversation ID if it exists and we don't have it yet. return this.getConversation(this.conversationId, this.userId).then((exists) => { + const promises = []; + if (exists) { // Fetch the messages for the first time. - return this.fetchMessages(); - } - }).then(() => { - let promise; - if (this.userId) { - promise = this.messagesProvider.getMemberInfo(this.userId); - } else { - // Group conversation. - promise = Promise.resolve(null); + promises.push(this.fetchMessages()); } - return promise.then((member) => { - this.otherMember = member; - }); + if (this.userId) { + promises.push(this.messagesProvider.getMemberInfo(this.userId).then((member) => { + this.otherMember = member; + if (!exists && member) { + this.conversationImage = member.profileimageurl; + this.title = member.fullname; + } + })); + } else { + this.otherMember = null; + } + + return Promise.all(promises); }); } else { this.otherMember = null; From d40a942e78d5d130c3973b503879b52b4dbbe78c Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 13 Dec 2018 10:05:47 +0100 Subject: [PATCH 4/7] MOBILE-2734 messages: Fix navigation to conversation from contacts --- src/addon/messages/pages/contacts/contacts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addon/messages/pages/contacts/contacts.ts b/src/addon/messages/pages/contacts/contacts.ts index 5e88a8593..d27dc3662 100644 --- a/src/addon/messages/pages/contacts/contacts.ts +++ b/src/addon/messages/pages/contacts/contacts.ts @@ -93,7 +93,7 @@ export class AddonMessagesContactsPage implements OnDestroy { selectUser(tab: string, userId?: number, onInit: boolean = false): void { userId = userId || this.selectedUserId[tab]; - if (!userId || userId == this.conversationUserId) { + if (!userId || userId == this.conversationUserId && this.splitviewCtrl.isOn()) { // No user conversation to open or it is already opened. return; } From f4350e9b6a72f3f6aecc925f492d53668f69877e Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 13 Dec 2018 10:20:24 +0100 Subject: [PATCH 5/7] MOBILE-2734 messages: Refresh contacts when request is confirmed --- .../components/confirmed-contacts/confirmed-contacts.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts b/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts index 7a4a3d1da..f7efb6f76 100644 --- a/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts +++ b/src/addon/messages/components/confirmed-contacts/confirmed-contacts.ts @@ -55,6 +55,8 @@ export class AddonMessagesConfirmedContactsComponent implements OnInit, OnDestro if (index >= 0) { this.contacts.splice(index, 1); } + } else if (data.contactRequestConfirmed) { + this.refreshData(); } }, sitesProvider.getCurrentSiteId()); } From fd47d4d5fbee4fa1298aa3b70814f0f5eae39962 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 13 Dec 2018 12:14:04 +0100 Subject: [PATCH 6/7] MOBILE-2734 messages: Go to group conversations from index link handler --- src/addon/messages/providers/index-link-handler.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/addon/messages/providers/index-link-handler.ts b/src/addon/messages/providers/index-link-handler.ts index 5b162b772..974c6ddef 100644 --- a/src/addon/messages/providers/index-link-handler.ts +++ b/src/addon/messages/providers/index-link-handler.ts @@ -44,8 +44,13 @@ export class AddonMessagesIndexLinkHandler extends CoreContentLinksHandlerBase { CoreContentLinksAction[] | Promise { return [{ action: (siteId, navCtrl?): void => { + let pageName = 'AddonMessagesIndexPage'; + if (this.messagesProvider.isGroupMessagingEnabled()) { + pageName = 'AddonMessagesGroupConversationsPage'; + } + // Always use redirect to make it the new history root (to avoid "loops" in history). - this.linkHelper.goInSite(navCtrl, 'AddonMessagesIndexPage', undefined, siteId); + this.linkHelper.goInSite(navCtrl, pageName, undefined, siteId); } }]; } From f362706e81507afa8a6fd9945371eb2d47697128 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 13 Dec 2018 12:33:10 +0100 Subject: [PATCH 7/7] MOBILE-2734 messages: Go to group conversations from push notifications --- src/addon/messages/messages.module.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/addon/messages/messages.module.ts b/src/addon/messages/messages.module.ts index 58da7f789..0d3cc13fc 100644 --- a/src/addon/messages/messages.module.ts +++ b/src/addon/messages/messages.module.ts @@ -107,7 +107,11 @@ export class AddonMessagesModule { } messagesProvider.invalidateDiscussionsCache(notification.site).finally(() => { - linkHelper.goInSite(undefined, 'AddonMessagesIndexPage', undefined, notification.site); + let pageName = 'AddonMessagesIndexPage'; + if (messagesProvider.isGroupMessagingEnabled()) { + pageName = 'AddonMessagesGroupConversationsPage'; + } + linkHelper.goInSite(undefined, pageName, undefined, notification.site); }); }); });