[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:
@@ -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;
|
||||
|
||||
+13
-13
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-7
@@ -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"/>
|
||||
|
||||
+89
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user