[FIX] mail: only highlight needaction on their origin thread

task-2391170

closes odoo/odoo#63163

X-original-commit: 2752d961922a3527f3c919d4578a33b8e87eeffb
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Sébastien Theys
2020-12-10 13:14:22 +00:00
parent 3daf67d902
commit fb7b32dbcb
6 changed files with 163 additions and 45 deletions
@@ -69,21 +69,21 @@ class NotificationList extends Component {
if (props.filter === 'all') {
// threads with needactions
threadNeedactionNotifications = this.env.models['mail.thread']
.all(t => t.model !== 'mail.box' && t.needactionMessages.length > 0)
.all(t => t.model !== 'mail.box' && t.needactionMessagesAsOriginThread.length > 0)
.sort((t1, t2) => {
if (t1.needactionMessages.length > 0 && t2.needactionMessages.length === 0) {
if (t1.needactionMessagesAsOriginThread.length > 0 && t2.needactionMessagesAsOriginThread.length === 0) {
return -1;
}
if (t1.needactionMessages.length === 0 && t2.needactionMessages.length > 0) {
if (t1.needactionMessagesAsOriginThread.length === 0 && t2.needactionMessagesAsOriginThread.length > 0) {
return 1;
}
if (t1.lastNeedactionMessage && t2.lastNeedactionMessage) {
return t1.lastNeedactionMessage.date.isBefore(t2.lastNeedactionMessage.date) ? 1 : -1;
if (t1.lastNeedactionMessageAsOriginThread && t2.lastNeedactionMessageAsOriginThread) {
return t1.lastNeedactionMessageAsOriginThread.date.isBefore(t2.lastNeedactionMessageAsOriginThread.date) ? 1 : -1;
}
if (t1.lastNeedactionMessage) {
if (t1.lastNeedactionMessageAsOriginThread) {
return -1;
}
if (t2.lastNeedactionMessage) {
if (t2.lastNeedactionMessageAsOriginThread) {
return 1;
}
return t1.id < t2.id ? -1 : 1;
@@ -21,21 +21,21 @@ class ThreadNeedactionPreview extends Component {
useStore(props => {
const thread = this.env.models['mail.thread'].get(props.threadLocalId);
const mainThreadCache = thread ? thread.mainCache : undefined;
let lastNeedactionMessageAuthor;
let lastNeedactionMessage;
let lastNeedactionMessageAsOriginThreadAuthor;
let lastNeedactionMessageAsOriginThread;
let threadCorrespondent;
if (thread) {
lastNeedactionMessage = mainThreadCache.lastNeedactionMessage;
lastNeedactionMessageAsOriginThread = mainThreadCache.lastNeedactionMessageAsOriginThread;
threadCorrespondent = thread.correspondent;
}
if (lastNeedactionMessage) {
lastNeedactionMessageAuthor = lastNeedactionMessage.author;
if (lastNeedactionMessageAsOriginThread) {
lastNeedactionMessageAsOriginThreadAuthor = lastNeedactionMessageAsOriginThread.author;
}
return {
isDeviceMobile: this.env.messaging.device.isMobile,
lastNeedactionMessage: lastNeedactionMessage ? lastNeedactionMessage.__state : undefined,
lastNeedactionMessageAuthor: lastNeedactionMessageAuthor
? lastNeedactionMessageAuthor.__state
lastNeedactionMessageAsOriginThread: lastNeedactionMessageAsOriginThread ? lastNeedactionMessageAsOriginThread.__state : undefined,
lastNeedactionMessageAsOriginThreadAuthor: lastNeedactionMessageAsOriginThreadAuthor
? lastNeedactionMessageAsOriginThreadAuthor.__state
: undefined,
thread: thread ? thread.__state : undefined,
threadCorrespondent: threadCorrespondent
@@ -77,11 +77,11 @@ class ThreadNeedactionPreview extends Component {
*
* @returns {string}
*/
get inlineLastNeedactionMessageBody() {
if (!this.thread.lastNeedactionMessage) {
get inlineLastNeedactionMessageAsOriginThreadBody() {
if (!this.thread.lastNeedactionMessageAsOriginThread) {
return '';
}
return mailUtils.htmlToTextContentInline(this.thread.lastNeedactionMessage.prettyBody);
return mailUtils.htmlToTextContentInline(this.thread.lastNeedactionMessageAsOriginThread.prettyBody);
}
/**
@@ -105,7 +105,7 @@ class ThreadNeedactionPreview extends Component {
// handled in `_onClickMarkAsRead`
return;
}
this.thread.markNeedactionMessagesAsRead();
this.thread.markNeedactionMessagesAsOriginThreadAsRead();
this.thread.open();
if (!this.env.messaging.device.isMobile) {
this.env.messaging.messagingMenu.close();
@@ -117,7 +117,7 @@ class ThreadNeedactionPreview extends Component {
* @param {MouseEvent} ev
*/
_onClickMarkAsRead(ev) {
this.thread.markNeedactionMessagesAsRead();
this.thread.markNeedactionMessagesAsOriginThreadAsRead();
}
}
@@ -28,24 +28,24 @@
<t t-esc="thread.displayName"/>
</span>
<span class="o_ThreadNeedactionPreview_counter">
(<t t-esc="thread.needactionMessages.length"/>)
(<t t-esc="thread.needactionMessagesAsOriginThread.length"/>)
</span>
<span class="o-autogrow"/>
<t t-if="thread.lastNeedactionMessage">
<t t-if="thread.lastNeedactionMessageAsOriginThread">
<span class="o_ThreadNeedactionPreview_date">
<t t-esc="thread.lastNeedactionMessage.date.fromNow()"/>
<t t-esc="thread.lastNeedactionMessageAsOriginThread.date.fromNow()"/>
</span>
</t>
</div>
<div class="o_ThreadNeedactionPreview_core">
<span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_inlineText" t-att-class="{ 'o-empty': inlineLastNeedactionMessageBody.length === 0 }">
<t t-if="thread.lastNeedactionMessage and thread.lastNeedactionMessage.author">
<span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_inlineText" t-att-class="{ 'o-empty': inlineLastNeedactionMessageAsOriginThreadBody.length === 0 }">
<t t-if="thread.lastNeedactionMessageAsOriginThread and thread.lastNeedactionMessageAsOriginThread.author">
<MessageAuthorPrefix
messageLocalId="thread.lastNeedactionMessage.localId"
messageLocalId="thread.lastNeedactionMessageAsOriginThread.localId"
threadLocalId="thread.localId"
/>
</t>
<t t-esc="inlineLastNeedactionMessageBody"/>
<t t-esc="inlineLastNeedactionMessageAsOriginThreadBody"/>
</span>
<span class="o-autogrow"/>
<span class="o_ThreadNeedactionPreview_coreItem o_ThreadNeedactionPreview_markAsRead fa fa-check" title="Mark as Read" t-on-click="_onClickMarkAsRead" t-ref="markAsRead"/>
@@ -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"
);
});
});
});
});
@@ -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',
+44 -17
View File
@@ -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