diff --git a/src/addon/messages/pages/discussion/discussion.html b/src/addon/messages/pages/discussion/discussion.html index ce5d58787..ac8de345d 100644 --- a/src/addon/messages/pages/discussion/discussion.html +++ b/src/addon/messages/pages/discussion/discussion.html @@ -9,11 +9,15 @@ - - - - - + + + + + + + + + @@ -21,7 +25,7 @@ - + {{ message.timecreated | coreFormatDate: "LL" }} @@ -59,7 +63,25 @@ - - +

{{ 'addon.messages.unabletomessage' | translate }}

+
+

{{ 'addon.messages.youhaveblockeduser' | translate }}

+ +
+
+

{{ 'addon.messages.isnotinyourcontacts' | translate: {$a: otherMember.fullname} }}

+

{{ 'addon.messages.requirecontacttomessage' | translate: {$a: otherMember.fullname} }}

+ +
+
+

{{ 'addon.messages.userwouldliketocontactyou' | translate: {$a: otherMember.fullname} }}

+ + +
+
+

{{ 'addon.messages.contactrequestsent' | translate }}

+

{{ 'addon.messages.yourcontactrequestpending' | translate: {$a: otherMember.fullname} }}

+
+
diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index cd1d1d967..d4f6508da 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -57,6 +57,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy { protected keyboardObserver: any; protected scrollBottom = true; protected viewDestroyed = false; + protected memberInfoObserver: any; + protected showLoadingModal = false; // Whether to show a loading modal while fetching data. conversationId: number; // Conversation ID. Undefined if it's a new individual conversation. conversation: any; // The conversation object (if it exists). @@ -77,6 +79,10 @@ export class AddonMessagesDiscussionPage implements OnDestroy { members: any = {}; // Members that wrote a message, indexed by ID. favouriteIcon = 'fa-star'; deleteIcon = 'trash'; + otherMember: any; // Other member information (individual conversations only). + footerType: 'message' | 'blocked' | 'requiresContact' | 'requestSent' | 'requestReceived' | 'unable'; + requestContactSent = false; + requestContactReceived = false; constructor(private eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, navParams: NavParams, private userProvider: CoreUserProvider, private navCtrl: NavController, private messagesSync: AddonMessagesSyncProvider, @@ -108,6 +114,13 @@ export class AddonMessagesDiscussionPage implements OnDestroy { } } }, this.siteId); + + // Refresh data if info of a mamber of the conversation have changed. + this.memberInfoObserver = eventsProvider.on(AddonMessagesProvider.MEMBER_INFO_CHANGED_EVENT, (data) => { + if (data.userId && (this.members[data.userId] || this.otherMember && data.userId == this.otherMember.id)) { + this.fetchData(); + } + }, this.siteId); } /** @@ -160,6 +173,25 @@ export class AddonMessagesDiscussionPage implements OnDestroy { const backViewPage = this.navCtrl.getPrevious() && this.navCtrl.getPrevious().component.name; this.showInfo = !backViewPage || backViewPage !== 'CoreUserProfilePage'; + // Recalculate footer position when keyboard is shown or hidden. + this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => { + this.content.resize(); + }); + + this.fetchData(); + } + + /** + * Convenience function to fetch the conversation data. + * + * @return {Promise} Resolved when done. + */ + protected fetchData(): Promise { + let loader; + if (this.showLoadingModal) { + loader = this.domUtils.showModalLoading(); + } + if (!this.groupMessagingEnabled && this.userId) { // Get the user profile to retrieve the user fullname and image. this.userProvider.getProfile(this.userId, undefined, true).then((user) => { @@ -171,7 +203,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { } // Synchronize messages if needed. - this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => { + return this.messagesSync.syncDiscussion(this.conversationId, this.userId).catch(() => { // Ignore errors. }).then((warnings) => { if (warnings && warnings[0]) { @@ -185,8 +217,22 @@ export class AddonMessagesDiscussionPage implements OnDestroy { // 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); + } + + return promise.then((member) => { + this.otherMember = member; + }); }); } else { + this.otherMember = null; + // Fetch the messages for the first time. return this.fetchMessages().then(() => { if (!this.title && this.messages.length) { @@ -207,11 +253,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy { this.resizeContent(); this.loaded = true; this.setPolling(); // Make sure we're polling messages. - }); - - // Recalculate footer position when keyboard is shown or hidden. - this.keyboardObserver = this.eventsProvider.on(CoreEventsProvider.KEYBOARD_CHANGE, (kbHeight) => { - this.content.resize(); + this.setContactRequestInfo(); + this.setFooterType(); + loader && loader.dismiss(); }); } @@ -985,6 +1029,71 @@ export class AddonMessagesDiscussionPage implements OnDestroy { }); } + /** + * Calculate whether there are pending contact requests. + */ + protected setContactRequestInfo(): void { + this.requestContactSent = false; + this.requestContactReceived = false; + if (this.otherMember && !this.otherMember.iscontact) { + this.requestContactSent = this.otherMember.contactrequests.some((request) => { + return request.userid == this.currentUserId && request.requesteduserid == this.otherMember.id; + }); + this.requestContactReceived = this.otherMember.contactrequests.some((request) => { + return request.userid == this.otherMember.id && request.requesteduserid == this.currentUserId; + }); + } + } + + /** + * Calculate what to display in the footer. + */ + protected setFooterType(): void { + if (!this.otherMember) { + // Group conversation or group messaging not available. + this.footerType = 'message'; + } else if (this.otherMember.isblocked) { + this.footerType = 'blocked'; + } else if (this.requestContactReceived) { + this.footerType = 'requestReceived'; + } else if (this.otherMember.canmessage) { + this.footerType = 'message'; + } else if (this.requestContactSent) { + this.footerType = 'requestSent'; + } else if (this.otherMember.requirescontact) { + this.footerType = 'requiresContact'; + } else { + this.footerType = 'unable'; + } + } + + /** + * Displays a confirmation modal to block the user of the individual conversation. + * + * @return {Promise} Promise resolved when user is blocked or dialog is cancelled. + */ + blockUser(): Promise { + if (!this.otherMember) { + // Should never happen. + return Promise.reject(null); + } + + const template = this.translate.instant('addon.messages.blockuserconfirm', {$a: this.otherMember.fullname}); + const okText = this.translate.instant('addon.messages.blockuser'); + + return this.domUtils.showConfirm(template, undefined, okText).then(() => { + const modal = this.domUtils.showModalLoading('core.sending', true); + this.showLoadingModal = true; + + return this.messagesProvider.blockContact(this.otherMember.id).finally(() => { + modal.dismiss(); + this.showLoadingModal = false; + }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.error', true); + }); + } + /** * Delete the conversation. * @@ -1012,6 +1121,131 @@ export class AddonMessagesDiscussionPage implements OnDestroy { }); } + /** + * Displays a confirmation modal to unblock the user of the individual conversation. + * + * @return {Promise} Promise resolved when user is unblocked or dialog is cancelled. + */ + unblockUser(): Promise { + if (!this.otherMember) { + // Should never happen. + return Promise.reject(null); + } + + const template = this.translate.instant('addon.messages.unblockuserconfirm', {$a: this.otherMember.fullname}); + const okText = this.translate.instant('addon.messages.unblockuser'); + + return this.domUtils.showConfirm(template, undefined, okText).then(() => { + const modal = this.domUtils.showModalLoading('core.sending', true); + this.showLoadingModal = true; + + return this.messagesProvider.unblockContact(this.otherMember.id).finally(() => { + modal.dismiss(); + this.showLoadingModal = false; + }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.error', true); + }); + } + + /** + * Displays a confirmation modal to send a contact request to the other user of the individual conversation. + * + * @return {Promise} Promise resolved when the request is sent or the dialog is cancelled. + */ + createContactRequest(): Promise { + if (!this.otherMember) { + // Should never happen. + return Promise.reject(null); + } + + const template = this.translate.instant('addon.messages.addcontactconfirm', { $a: this.otherMember.fullname }); + const okText = this.translate.instant('core.add'); + + return this.domUtils.showConfirm(template, undefined, okText).then(() => { + const modal = this.domUtils.showModalLoading('core.sending', true); + this.showLoadingModal = true; + + return this.messagesProvider.createContactRequest(this.otherMember.id).finally(() => { + modal.dismiss(); + this.showLoadingModal = false; + }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.error', true); + }); + } + + /** + * Confirms the contact request of the other user of the individual conversation. + * + * @return {Promise} Promise resolved when the request is confirmed. + */ + confirmContactRequest(): Promise { + if (!this.otherMember) { + // Should never happen. + return Promise.reject(null); + } + + const modal = this.domUtils.showModalLoading('core.sending', true); + this.showLoadingModal = true; + + return this.messagesProvider.confirmContactRequest(this.otherMember.id).finally(() => { + modal.dismiss(); + this.showLoadingModal = false; + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.error', true); + }); + } + + /** + * Declines the contact request of the other user of the individual conversation. + * + * @return {Promise} Promise resolved when the request is confirmed. + */ + declineContactRequest(): Promise { + if (!this.otherMember) { + // Should never happen. + return Promise.reject(null); + } + + const modal = this.domUtils.showModalLoading('core.sending', true); + this.showLoadingModal = true; + + return this.messagesProvider.declineContactRequest(this.otherMember.id).finally(() => { + modal.dismiss(); + this.showLoadingModal = false; + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.error', true); + }); + } + + /** + * Displays a confirmation modal to remove the other user of the conversation from contacts. + * + * @return {Promise} Promise resolved when the request is sent or the dialog is cancelled. + */ + removeContact(): Promise { + if (!this.otherMember) { + // Should never happen. + return Promise.reject(null); + } + + const template = this.translate.instant('addon.messages.removecontactconfirm', { $a: this.otherMember.fullname }); + const okText = this.translate.instant('core.remove'); + + return this.domUtils.showConfirm(template, undefined, okText).then(() => { + const modal = this.domUtils.showModalLoading('core.sending', true); + this.showLoadingModal = true; + + return this.messagesProvider.removeContact(this.otherMember.id).finally(() => { + modal.dismiss(); + this.showLoadingModal = false; + }); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.error', true); + }); + } + /** * Page destroyed. */ @@ -1020,6 +1254,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { this.unsetPolling(); this.syncObserver && this.syncObserver.off(); this.keyboardObserver && this.keyboardObserver.off(); + this.memberInfoObserver && this.memberInfoObserver.off(); this.viewDestroyed = true; } }