Merge pull request #1661 from albertgasset/MOBILE-2734

Mobile 2734
main
Juan Leyva 2018-12-14 13:17:59 +01:00 committed by GitHub
commit 377cf800ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 331 additions and 154 deletions

View File

@ -55,6 +55,8 @@ export class AddonMessagesConfirmedContactsComponent implements OnInit, OnDestro
if (index >= 0) { if (index >= 0) {
this.contacts.splice(index, 1); this.contacts.splice(index, 1);
} }
} else if (data.contactRequestConfirmed) {
this.refreshData();
} }
}, sitesProvider.getCurrentSiteId()); }, sitesProvider.getCurrentSiteId());
} }

View File

@ -53,7 +53,7 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService, constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, translate: TranslateService,
private messagesProvider: AddonMessagesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, 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) { pushNotificationsDelegate: AddonPushNotificationsDelegate) {
this.search.loading = translate.instant('core.searching'); this.search.loading = translate.instant('core.searching');
@ -91,17 +91,13 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
// A discussion has been read reset counter. // A discussion has been read reset counter.
discussion.unread = false; discussion.unread = false;
// Discussions changed, invalidate them. // Conversations changed, invalidate them and refresh unread counts.
this.messagesProvider.invalidateDiscussionsCache(); this.messagesProvider.invalidateConversations();
this.messagesProvider.refreshUnreadConversationCounts();
} }
} }
}, this.siteId); }, 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. // Refresh the view when the app is resumed.
this.appResumeSubscription = platform.resume.subscribe(() => { this.appResumeSubscription = platform.resume.subscribe(() => {
if (!this.loaded) { if (!this.loaded) {
@ -117,7 +113,8 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => { this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => {
// New message received. If it's from current site, refresh the data. // New message received. If it's from current site, refresh the data.
if (utils.isFalseOrZero(notification.notif) && notification.site == this.siteId) { 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. * Refresh the data.
* *
* @param {any} [refresher] Refresher. * @param {any} [refresher] Refresher.
* @param {boolean} [refreshUnreadCounts=true] Whteher to refresh unread counts.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
refreshData(refresher?: any): Promise<any> { refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise<any> {
return this.messagesProvider.invalidateDiscussionsCache().then(() => { const promises = [];
promises.push(this.messagesProvider.invalidateDiscussionsCache());
if (refreshUnreadCounts) {
promises.push(this.messagesProvider.invalidateUnreadConversationCounts());
}
return this.utils.allPromises(promises).finally(() => {
return this.fetchData().finally(() => { return this.fetchData().finally(() => {
if (refresher) { if (refresher) {
// Actions to take if refresh comes from the user.
this.eventsProvider.trigger(AddonMessagesProvider.READ_CHANGED_EVENT, undefined, this.siteId);
refresher.complete(); refresher.complete();
} }
}); });
@ -166,7 +169,9 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
this.loadingMessage = this.loadingMessages; this.loadingMessage = this.loadingMessages;
this.search.enabled = this.messagesProvider.isSearchMessagesEnabled(); 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. // Convert to an array for sorting.
const discussionsSorted = []; const discussionsSorted = [];
for (const userId in discussions) { for (const userId in discussions) {
@ -177,7 +182,11 @@ export class AddonMessagesDiscussionsComponent implements OnDestroy {
this.discussions = discussionsSorted.sort((a, b) => { this.discussions = discussionsSorted.sort((a, b) => {
return b.message.timecreated - a.message.timecreated; 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); this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true);
}).finally(() => { }).finally(() => {
this.loaded = true; this.loaded = true;

View File

@ -107,7 +107,11 @@ export class AddonMessagesModule {
} }
messagesProvider.invalidateDiscussionsCache(notification.site).finally(() => { 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);
}); });
}); });
}); });

View File

@ -93,7 +93,7 @@ export class AddonMessagesContactsPage implements OnDestroy {
selectUser(tab: string, userId?: number, onInit: boolean = false): void { selectUser(tab: string, userId?: number, onInit: boolean = false): void {
userId = userId || this.selectedUserId[tab]; 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. // No user conversation to open or it is already opened.
return; return;
} }

View File

@ -205,7 +205,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
// Synchronize messages if needed. // Synchronize messages if needed.
return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => { return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => {
// Ignore errors. // Ignore errors.
}).then((warnings) => { }).then((warnings): Promise<any> => {
if (warnings && warnings[0]) { if (warnings && warnings[0]) {
this.domUtils.showErrorModal(warnings[0]); this.domUtils.showErrorModal(warnings[0]);
} }
@ -213,22 +213,26 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
if (this.groupMessagingEnabled) { if (this.groupMessagingEnabled) {
// Get the conversation ID if it exists and we don't have it yet. // Get the conversation ID if it exists and we don't have it yet.
return this.getConversation(this.conversationId, this.userId).then((exists) => { return this.getConversation(this.conversationId, this.userId).then((exists) => {
const promises = [];
if (exists) { if (exists) {
// Fetch the messages for the first time. // Fetch the messages for the first time.
return this.fetchMessages(); promises.push(this.fetchMessages());
}
}).then(() => {
let promise;
if (this.userId) {
promise = this.messagesProvider.getMemberInfo(this.userId);
} else {
// Group conversation.
promise = Promise.resolve(null);
} }
return promise.then((member) => { if (this.userId) {
promises.push(this.messagesProvider.getMemberInfo(this.userId).then((member) => {
this.otherMember = member; this.otherMember = member;
}); if (!exists && member) {
this.conversationImage = member.profileimageurl;
this.title = member.fullname;
}
}));
} else {
this.otherMember = null;
}
return Promise.all(promises);
}); });
} else { } else {
this.otherMember = null; this.otherMember = null;

View File

@ -31,7 +31,7 @@
<core-icon *ngIf="!favourites.expanded" name="fa-caret-right" item-start></core-icon> <core-icon *ngIf="!favourites.expanded" name="fa-caret-right" item-start></core-icon>
<core-icon *ngIf="favourites.expanded" name="fa-caret-down" item-start></core-icon> <core-icon *ngIf="favourites.expanded" name="fa-caret-down" item-start></core-icon>
{{ 'core.favourites' | translate }} ({{ favourites.count }}) {{ 'core.favourites' | translate }} ({{ favourites.count }})
<!-- @todo: Unread total of favourites (MDL-63913). --> <ion-badge item-end *ngIf="favourites.unread">{{ favourites.unread }}</ion-badge>
</ion-item-divider> </ion-item-divider>
<div *ngIf="favourites.conversations && favourites.expanded"> <div *ngIf="favourites.conversations && favourites.expanded">
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: favourites.conversations}"></ng-container> <ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: favourites.conversations}"></ng-container>
@ -47,7 +47,7 @@
<core-icon *ngIf="!group.expanded" name="fa-caret-right" item-start></core-icon> <core-icon *ngIf="!group.expanded" name="fa-caret-right" item-start></core-icon>
<core-icon *ngIf="group.expanded" name="fa-caret-down" item-start></core-icon> <core-icon *ngIf="group.expanded" name="fa-caret-down" item-start></core-icon>
{{ 'addon.messages.groupmessages' | translate }} ({{ group.count }}) {{ 'addon.messages.groupmessages' | translate }} ({{ group.count }})
<!-- @todo: Unread total of group conversations (MDL-63913). --> <ion-badge item-end *ngIf="group.unread">{{ group.unread }}</ion-badge>
</ion-item-divider> </ion-item-divider>
<div *ngIf="group.conversations && group.expanded"> <div *ngIf="group.conversations && group.expanded">
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: group.conversations}"></ng-container> <ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: group.conversations}"></ng-container>
@ -62,7 +62,7 @@
<core-icon *ngIf="!individual.expanded" name="fa-caret-right" item-start></core-icon> <core-icon *ngIf="!individual.expanded" name="fa-caret-right" item-start></core-icon>
<core-icon *ngIf="individual.expanded" name="fa-caret-down" item-start></core-icon> <core-icon *ngIf="individual.expanded" name="fa-caret-down" item-start></core-icon>
{{ 'addon.messages.messages' | translate }} ({{ individual.count }}) {{ 'addon.messages.messages' | translate }} ({{ individual.count }})
<!-- @todo: Unread total of individual conversations (MDL-63913). --> <ion-badge item-end *ngIf="individual.unread">{{ individual.unread }}</ion-badge>
</ion-item-divider> </ion-item-divider>
<div *ngIf="individual.conversations && individual.expanded"> <div *ngIf="individual.conversations && individual.expanded">
<ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: individual.conversations}"></ng-container> <ng-container *ngTemplateOutlet="conversationsTemplate; context: {conversations: individual.conversations}"></ng-container>

View File

@ -44,15 +44,21 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
contactRequestsCount = 0; contactRequestsCount = 0;
favourites: any = { favourites: any = {
type: null, type: null,
favourites: true favourites: true,
count: 0,
unread: 0
}; };
group: any = { group: any = {
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP, type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP,
favourites: false favourites: false,
count: 0,
unread: 0
}; };
individual: any = { individual: any = {
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
favourites: false favourites: false,
count: 0,
unread: 0
}; };
typeIndividual = AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL; typeIndividual = AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL;
@ -70,9 +76,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
protected contactRequestsCountObserver: any; protected contactRequestsCountObserver: any;
protected memberInfoObserver: 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 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, pushNotificationsDelegate: AddonPushNotificationsDelegate, private messagesOffline: AddonMessagesOfflineProvider,
private userProvider: CoreUserProvider) { private userProvider: CoreUserProvider) {
@ -119,17 +125,13 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
// A conversation has been read reset counter. // A conversation has been read reset counter.
conversation.unreadcount = 0; conversation.unreadcount = 0;
// Conversations changed, invalidate them. // Conversations changed, invalidate them and refresh unread counts.
this.messagesProvider.invalidateConversations(); this.messagesProvider.invalidateConversations();
this.messagesProvider.refreshUnreadConversationCounts();
} }
} }
}, this.siteId); }, 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. // Load a discussion if we receive an event to do so.
this.openConversationObserver = eventsProvider.on(AddonMessagesProvider.OPEN_CONVERSATION_EVENT, (data) => { this.openConversationObserver = eventsProvider.on(AddonMessagesProvider.OPEN_CONVERSATION_EVENT, (data) => {
if (data.conversationId || data.userId) { if (data.conversationId || data.userId) {
@ -157,10 +159,18 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => { this.pushObserver = pushNotificationsDelegate.on('receive').subscribe((notification) => {
// New message received. If it's from current site, refresh the data. // New message received. If it's from current site, refresh the data.
if (utils.isFalseOrZero(notification.notif) && notification.site == this.siteId) { 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. // Update the contact requests badge.
this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { this.contactRequestsCountObserver = eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => {
this.contactRequestsCount = data.count; 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.favourites, false));
promises.push(this.fetchDataForOption(this.group, false)); promises.push(this.fetchDataForOption(this.group, false));
promises.push(this.fetchDataForOption(this.individual, 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) => { promises.push(this.messagesOffline.getAllMessages().then((messages) => {
offlineMessages = messages; offlineMessages = messages;
})); }));
@ -289,7 +300,6 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
if (loadingMore) { if (loadingMore) {
option.conversations = option.conversations.concat(data.conversations); option.conversations = option.conversations.concat(data.conversations);
} else { } else {
option.count = data.canLoadMore ? AddonMessagesProvider.LIMIT_MESSAGES + '+' : data.conversations.length;
option.conversations = data.conversations; option.conversations = data.conversations;
} }
@ -298,6 +308,19 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
}); });
} }
/**
* Fetch conversation counts.
*
* @return {Promise<any>} Promise resolved when done.
*/
protected fetchConversationCounts(): Promise<void> {
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. * Find a conversation in the list of loaded conversations.
* *
@ -494,15 +517,23 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
* Refresh the data. * Refresh the data.
* *
* @param {any} [refresher] Refresher. * @param {any} [refresher] Refresher.
* @param {booleam} [refreshUnreadCounts=true] Whether to refresh unread counts.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
refreshData(refresher?: any): Promise<any> { refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise<any> {
return this.messagesProvider.invalidateConversations().then(() => { 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(() => { return this.fetchData().finally(() => {
if (refresher) { 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(); refresher.complete();
} }
}); });

View File

@ -1,7 +1,10 @@
<ion-header> <ion-header>
<ion-navbar core-back-button> <ion-navbar core-back-button>
<ion-title>{{ 'addon.messages.messages' | translate }}</ion-title> <ion-title>{{ 'addon.messages.messages' | translate }}</ion-title>
<ion-buttons end></ion-buttons> <ion-buttons end>
<!-- Add an empty context menu so discussion page can add items in split view, otherwise the menu disappears in some cases. -->
<core-context-menu></core-context-menu>
</ion-buttons>
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<core-split-view> <core-split-view>

View File

@ -30,7 +30,7 @@
</ion-item> </ion-item>
<!-- List of results --> <!-- List of results -->
<a ion-item text-wrap *ngFor="let result of item.results" [title]="result.fullname" (click)="openDiscussion(result.id)" [class.core-split-item-selected]="result.id == selectedUserId" class="addon-message-discussion"> <a ion-item text-wrap *ngFor="let result of item.results" [title]="result.fullname" (click)="openConversation(result)" [class.core-split-item-selected]="result == selectedResult" class="addon-message-discussion">
<ion-avatar item-start core-user-avatar [user]="result" [checkOnline]="true" [linkProfile]="false"></ion-avatar> <ion-avatar item-start core-user-avatar [user]="result" [checkOnline]="true" [linkProfile]="false"></ion-avatar>
<h2> <h2>
<core-format-text [text]="result.fullname"></core-format-text> <core-format-text [text]="result.fullname"></core-format-text>

View File

@ -60,7 +60,7 @@ export class AddonMessagesSearchPage implements OnDestroy {
loadingMore: false, loadingMore: false,
loadMoreError: false loadMoreError: false
}; };
selectedUserId = null; selectedResult = null;
protected memberInfoObserver; protected memberInfoObserver;
@ -193,11 +193,11 @@ export class AddonMessagesSearchPage implements OnDestroy {
if (!loadMore) { if (!loadMore) {
if (this.contacts.results.length > 0) { 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) { } 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) { } else if (this.messages.results.length > 0) {
this.openDiscussion(this.messages.results[0].userid, true); this.openConversation(this.messages.results[0], true);
} }
} }
}).catch((error) => { }).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. * @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()) { if (!onInit || this.splitviewCtrl.isOn()) {
this.selectedUserId = userId; this.selectedResult = result;
this.splitviewCtrl.push('AddonMessagesDiscussionPage', { userId }); const params: any = {};
if (result.conversationid) {
params.conversationId = result.conversationid;
} else {
params.userId = result.id;
}
this.splitviewCtrl.push('AddonMessagesDiscussionPage', params);
} }
} }

View File

@ -44,8 +44,13 @@ export class AddonMessagesIndexLinkHandler extends CoreContentLinksHandlerBase {
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?): void => { 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). // 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);
} }
}]; }];
} }

View File

@ -43,38 +43,41 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
loading: true loading: true
}; };
protected updating = false; protected unreadCount = 0;
protected contactRequestsCount = 0;
protected orMore = false;
constructor(private messagesProvider: AddonMessagesProvider, private sitesProvider: CoreSitesProvider, 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 localNotificationsProvider: CoreLocalNotificationsProvider, private textUtils: CoreTextUtilsProvider,
private pushNotificationsProvider: AddonPushNotificationsProvider, utils: CoreUtilsProvider, private pushNotificationsProvider: AddonPushNotificationsProvider, utils: CoreUtilsProvider,
pushNotificationsDelegate: AddonPushNotificationsDelegate, private emulatorHelper: CoreEmulatorHelperProvider) { pushNotificationsDelegate: AddonPushNotificationsDelegate, private emulatorHelper: CoreEmulatorHelperProvider) {
eventsProvider.on(AddonMessagesProvider.READ_CHANGED_EVENT, (data) => { eventsProvider.on(AddonMessagesProvider.UNREAD_CONVERSATION_COUNTS_EVENT, (data) => {
this.updateBadge(data.siteId); this.unreadCount = data.favourites + data.individual + data.group;
}); this.orMore = data.orMore;
eventsProvider.on(AddonMessagesProvider.READ_CRON_EVENT, (data) => {
this.updateBadge(data.siteId); this.updateBadge(data.siteId);
}); });
eventsProvider.on(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, (data) => { 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. // Reset info on logout.
eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => { eventsProvider.on(CoreEventsProvider.LOGOUT, (data) => {
this.unreadCount = 0;
this.contactRequestsCount = 0;
this.orMore = false;
this.handler.badge = ''; this.handler.badge = '';
this.handler.loading = true; this.handler.loading = true;
this.updating = false;
}); });
// If a message push notification is received, refresh the count. // If a message push notification is received, refresh the count.
pushNotificationsDelegate.on('receive').subscribe((notification) => { pushNotificationsDelegate.on('receive').subscribe((notification) => {
// New message received. If it's from current site, refresh the data. // New message received. If it's from current site, refresh the data.
if (utils.isFalseOrZero(notification.notif) && this.sitesProvider.isCurrentSite(notification.site)) { if (utils.isFalseOrZero(notification.notif) && this.sitesProvider.isCurrentSite(notification.site)) {
this.updateBadge(notification.site); this.refreshBadge(notification.site);
} }
}); });
@ -101,65 +104,60 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
'AddonMessagesGroupConversationsPage' : 'AddonMessagesIndexPage'; 'AddonMessagesGroupConversationsPage' : 'AddonMessagesIndexPage';
if (this.handler.loading) { if (this.handler.loading) {
this.updateBadge(); this.refreshBadge();
} }
return this.handler; 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 {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<any>} Resolve when done.
*/ */
updateBadge(siteId?: string, contactRequestsCount?: number): void { refreshBadge(siteId?: string, unreadOnly?: boolean): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (!siteId) { if (!siteId) {
return; return;
} }
if (this.updating) {
// An update is already in prgoress.
return;
}
this.updating = true;
const promises = []; const promises = [];
let unreadCount = 0;
let unreadPlus = false;
promises.push(this.messagesProvider.getUnreadConversationsCount(undefined, siteId).then((unread) => { promises.push(this.messagesProvider.refreshUnreadConversationCounts(siteId).catch(() => {
unreadCount = parseInt(unread, 10); this.unreadCount = 0;
unreadPlus = (typeof unread === 'string' && unread.slice(-1) === '+'); this.orMore = false;
}).catch(() => {
// Ignore error.
})); }));
// Get the number of contact requests in 3.6+ sites if needed. // Refresh the number of contact requests in 3.6+ sites.
if (contactRequestsCount == null && this.messagesProvider.isGroupMessagingEnabled()) { if (!unreadOnly && this.messagesProvider.isGroupMessagingEnabled()) {
promises.push(this.messagesProvider.getContactRequestsCount(siteId).then((count) => { promises.push(this.messagesProvider.refreshContactRequestsCount(siteId).catch(() => {
contactRequestsCount = count; this.contactRequestsCount = 0;
}).catch(() => {
// Ignore errors
})); }));
} }
Promise.all(promises).then(() => { return Promise.all(promises).finally(() => {
const totalCount = unreadCount + (contactRequestsCount || 0); this.updateBadge(siteId);
this.handler.loading = 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) { if (totalCount > 0) {
this.handler.badge = totalCount + (unreadPlus ? '+' : ''); this.handler.badge = totalCount + (this.orMore ? '+' : '');
} else { } else {
this.handler.badge = ''; this.handler.badge = '';
} }
// Update badge. // Update push notifications badge.
this.pushNotificationsProvider.updateAddonCounter('AddonMessages', totalCount, siteId); this.pushNotificationsProvider.updateAddonCounter('AddonMessages', totalCount, siteId);
}).finally(() => {
this.handler.loading = false;
this.updating = false;
});
} }
/** /**
@ -171,7 +169,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
*/ */
execute(siteId?: string): Promise<any> { execute(siteId?: string): Promise<any> {
if (this.sitesProvider.isCurrentSite(siteId)) { if (this.sitesProvider.isCurrentSite(siteId)) {
this.eventsProvider.trigger(AddonMessagesProvider.READ_CRON_EVENT, {}, siteId); this.refreshBadge();
} }
if (this.appProvider.isDesktop() && this.localNotificationsProvider.isAvailable()) { if (this.appProvider.isDesktop() && this.localNotificationsProvider.isAvailable()) {
@ -199,8 +197,13 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
*/ */
isSync(): boolean { isSync(): boolean {
// This is done to use only wifi if using the fallback function. // This is done to use only wifi if using the fallback function.
if (this.appProvider.isDesktop()) {
// In desktop it is always sync, since it fetches messages to see if there's a new one. // 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(); return true;
}
return !this.messagesProvider.isMessageCountEnabled() && !this.messagesProvider.isGroupMessagingEnabled();
} }
/** /**

View File

@ -33,11 +33,11 @@ export class AddonMessagesProvider {
protected LIMIT_MESSAGES = AddonMessagesProvider.LIMIT_MESSAGES; protected LIMIT_MESSAGES = AddonMessagesProvider.LIMIT_MESSAGES;
static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event'; static NEW_MESSAGE_EVENT = 'addon_messages_new_message_event';
static READ_CHANGED_EVENT = 'addon_messages_read_changed_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 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 SPLIT_VIEW_LOAD_EVENT = 'addon_messages_split_view_load_event';
static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event'; static UPDATE_CONVERSATION_LIST_EVENT = 'addon_messages_update_conversation_list_event';
static MEMBER_INFO_CHANGED_EVENT = 'addon_messages_member_changed_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 CONTACT_REQUESTS_COUNT_EVENT = 'addon_messages_contact_requests_count_event';
static POLL_INTERVAL = 10000; static POLL_INTERVAL = 10000;
static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation'; static PUSH_SIMULATION_COMPONENT = 'AddonMessagesPushSimulation';
@ -384,6 +384,15 @@ export class AddonMessagesProvider {
return this.ROOT_CACHE_KEY + 'count:' + userId; 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. * Get the cache key for the list of discussions.
* *
@ -449,6 +458,15 @@ export class AddonMessagesProvider {
return this.getCommonCacheKeyForUserConversations(userId) + ':' + type + ':' + favourites; 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. * 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<any>} 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. * 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. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any>} Promise resolved with the message unread count. * @return {Promise<any>} Resolved with the unread favourite, individual and group conversation counts.
*/ */
getUnreadConversationsCount(userId?: number, siteId?: string): Promise<any> { getUnreadConversationCounts(siteId?: string):
return this.sitesProvider.getSite(siteId).then((site) => { Promise<{favourites: number, individual: number, group: number, orMore?: boolean}> {
userId = userId || site.getUserId();
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 // @since 3.2
if (site.wsAvailable('core_message_get_unread_conversations_count')) {
const params = { const params = {
useridto: userId useridto: site.getUserId(),
}, },
preSets = { preSets = {
cacheKey: this.getCacheKeyForMessageCount(userId), cacheKey: this.getCacheKeyForMessageCount(site.getUserId()),
getFromCache: false,
emergencyCache: true,
saveToCache: true,
typeExpected: 'number' typeExpected: 'number'
}; };
return site.read('core_message_get_unread_conversations_count', params, preSets).catch(() => { promise = site.read('core_message_get_unread_conversations_count', params, preSets).then((count) => {
// Return no messages if the call fails. return { favourites: 0, individual: count, group: 0 };
return 0;
}); });
} } else {
// Fallback call. // Fallback call.
const params = { const params = {
read: 0, read: 0,
limitfrom: 0, limitfrom: 0,
limitnum: this.LIMIT_MESSAGES + 1, limitnum: this.LIMIT_MESSAGES + 1,
useridto: userId, useridto: site.getUserId(),
useridfrom: 0, useridfrom: 0,
}; };
return this.getMessages(params, undefined, false, siteId).then((response) => { promise = this.getMessages(params, undefined, false, siteId).then((response) => {
// Count the discussions by filtering same senders. // Count the discussions by filtering same senders.
const discussions = {}; const discussions = {};
response.messages.forEach((message) => { response.messages.forEach((message) => {
discussions[message.useridto] = 1; discussions[message.useridto] = 1;
}); });
const count = Object.keys(discussions).length; const count = Object.keys(discussions).length;
// Add + sign if there are more than the limit reachable. return {
return (count > this.LIMIT_MESSAGES) ? count + '+' : count; favourites: 0,
}).catch(() => { individual: count,
// Return no messages if the call fails. group: 0,
return 0; orMore: count > this.LIMIT_MESSAGES
};
});
}
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 counts;
}); });
}); });
} }
@ -1535,6 +1597,18 @@ export class AddonMessagesProvider {
}); });
} }
/**
* Invalidate conversation counts cache.
*
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Resolved when done.
*/
invalidateConversationCounts(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getCacheKeyForConversationCounts());
});
}
/** /**
* Invalidate discussion cache. * 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<any>} Resolved when done.
*/
invalidateUnreadConversationCounts(siteId?: string): Promise<any> {
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. * 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<any>} 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. * Remove a contact.
* *