+
-
+
-
-
+
+
-
+
diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js
index fe01b8f7c7d..f3e1d49148b 100644
--- a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js
+++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js
@@ -345,6 +345,95 @@ QUnit.test('preview should display last needaction message preview even if there
);
});
+QUnit.test('needaction preview should only show on its origin thread', async function (assert) {
+ assert.expect(2);
+
+ this.data['mail.channel'].records.push({ id: 12 });
+ this.data['mail.message'].records.push({
+ channel_ids: [12],
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({ hasMessagingMenu: true });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ assert.containsOnce(
+ document.body,
+ '.o_ThreadNeedactionPreview',
+ "should have only one preview"
+ );
+ const thread = this.env.models['mail.thread'].findFromIdentifyingData({
+ id: 11,
+ model: 'res.partner',
+ });
+ assert.containsOnce(
+ document.body,
+ `.o_ThreadNeedactionPreview[data-thread-local-id="${thread.localId}"]`,
+ "preview should be on the origin thread"
+ );
+});
+
+QUnit.test('chat window header should not have unread counter for non-channel thread', async function (assert) {
+ assert.expect(2);
+
+ this.data['res.partner'].records.push({ id: 11 });
+ this.data['mail.message'].records.push({
+ author_id: 11,
+ body: 'not empty',
+ id: 21,
+ model: 'res.partner',
+ needaction: true,
+ needaction_partner_ids: [this.data.currentPartnerId],
+ res_id: 11,
+ });
+ this.data['mail.notification'].records.push({
+ mail_message_id: 21,
+ notification_status: 'sent',
+ notification_type: 'inbox',
+ res_partner_id: this.data.currentPartnerId,
+ });
+ await this.start({
+ hasChatWindow: true,
+ hasMessagingMenu: true,
+ });
+ await afterNextRender(() => this.afterEvent({
+ eventName: 'o-thread-cache-loaded-messages',
+ func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
+ message: "should wait until inbox loaded initial needaction messages",
+ predicate: ({ threadCache }) => {
+ return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox';
+ },
+ }));
+ await afterNextRender(() =>
+ document.querySelector('.o_ThreadNeedactionPreview').click()
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_ChatWindow',
+ "should have opened the chat window on clicking on the preview"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_ChatWindowHeader_counter',
+ "chat window header should not have unread counter for non-channel thread"
+ );
+});
+
});
});
});
diff --git a/addons/mail/static/src/models/message/message.js b/addons/mail/static/src/models/message/message.js
index cc8f60c2530..59e243a14fe 100644
--- a/addons/mail/static/src/models/message/message.js
+++ b/addons/mail/static/src/models/message/message.js
@@ -682,7 +682,9 @@ function factory(dependencies) {
/**
* Origin thread of this message (if any).
*/
- originThread: many2one('mail.thread'),
+ originThread: many2one('mail.thread', {
+ inverse: 'messagesAsOriginThread',
+ }),
originThreadIsModeratedByCurrentPartner: attr({
default: false,
related: 'originThread.isModeratedByCurrentPartner',
diff --git a/addons/mail/static/src/models/thread/thread.js b/addons/mail/static/src/models/thread/thread.js
index 923a156a39c..bd37b09ffd1 100644
--- a/addons/mail/static/src/models/thread/thread.js
+++ b/addons/mail/static/src/models/thread/thread.js
@@ -655,11 +655,11 @@ function factory(dependencies) {
}
/**
- * Mark all needaction messages of this thread as read.
+ * Marks as read all needaction messages with this thread as origin.
*/
- async markNeedactionMessagesAsRead() {
+ async markNeedactionMessagesAsOriginThreadAsRead() {
await this.async(() =>
- this.env.models['mail.message'].markAsRead(this.needactionMessages)
+ this.env.models['mail.message'].markAsRead(this.needactionMessagesAsOriginThread)
);
}
@@ -1223,16 +1223,16 @@ function factory(dependencies) {
* @private
* @returns {mail.message|undefined}
*/
- _computeLastNeedactionMessage() {
- const orderedNeedactionMessages = this.needactionMessages.sort(
+ _computeLastNeedactionMessageAsOriginThread() {
+ const orderedNeedactionMessagesAsOriginThread = this.needactionMessagesAsOriginThread.sort(
(m1, m2) => m1.id < m2.id ? -1 : 1
);
const {
length: l,
- [l - 1]: lastNeedactionMessage,
- } = orderedNeedactionMessages;
- if (lastNeedactionMessage) {
- return [['link', lastNeedactionMessage]];
+ [l - 1]: lastNeedactionMessageAsOriginThread,
+ } = orderedNeedactionMessagesAsOriginThread;
+ if (lastNeedactionMessageAsOriginThread) {
+ return [['link', lastNeedactionMessageAsOriginThread]];
}
return [['unlink']];
}
@@ -1250,6 +1250,10 @@ function factory(dependencies) {
* @returns {integer}
*/
_computeLocalMessageUnreadCounter() {
+ if (this.model !== 'mail.channel') {
+ // unread counter only makes sense on channels
+ return clear();
+ }
// By default trust the server up to the last message it used
// because it's not possible to do better.
let baseCounter = this.serverMessageUnreadCounter;
@@ -1289,8 +1293,8 @@ function factory(dependencies) {
* @private
* @returns {mail.message[]}
*/
- _computeNeedactionMessages() {
- return [['replace', this.messages.filter(message => message.isNeedaction)]];
+ _computeNeedactionMessagesAsOriginThread() {
+ return [['replace', this.messagesAsOriginThread.filter(message => message.isNeedaction)]];
}
/**
@@ -1805,9 +1809,14 @@ function factory(dependencies) {
compute: '_computeLastMessage',
dependencies: ['orderedMessages'],
}),
- lastNeedactionMessage: many2one('mail.message', {
- compute: '_computeLastNeedactionMessage',
- dependencies: ['needactionMessages'],
+ /**
+ * States the last known needaction message having this thread as origin.
+ */
+ lastNeedactionMessageAsOriginThread: many2one('mail.message', {
+ compute: '_computeLastNeedactionMessageAsOriginThread',
+ dependencies: [
+ 'needactionMessagesAsOriginThread',
+ ],
}),
/**
* Last non-transient message.
@@ -1882,6 +1891,18 @@ function factory(dependencies) {
inverse: 'threads',
readonly: true,
}),
+ /**
+ * All messages that have been originally posted in this thread.
+ */
+ messagesAsOriginThread: one2many('mail.message', {
+ inverse: 'originThread',
+ }),
+ /**
+ * Serves as compute dependency.
+ */
+ messagesAsOriginThreadIsNeedaction: attr({
+ related: 'messagesAsOriginThread.isNeedaction',
+ }),
/**
* All messages that are contained on this channel on the server.
* Equivalent to the inverse of python field `channel_ids`.
@@ -1918,9 +1939,15 @@ function factory(dependencies) {
}),
moduleIcon: attr(),
name: attr(),
- needactionMessages: many2many('mail.message', {
- compute: '_computeNeedactionMessages',
- dependencies: ['messages'],
+ /**
+ * States all known needaction messages having this thread as origin.
+ */
+ needactionMessagesAsOriginThread: many2many('mail.message', {
+ compute: '_computeNeedactionMessagesAsOriginThread',
+ dependencies: [
+ 'messagesAsOriginThread',
+ 'messagesAsOriginThreadIsNeedaction',
+ ],
}),
/**
* Not a real field, used to trigger `_onChangeFollowersPartner` when one of