[IMP] mail, sms, snailmail: remove default date on message
We should not assume that the default date of the message is "now". A message should only have a date when it's coming from the database that supply it. closes odoo/odoo#70069 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -37,9 +37,11 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="o_Message_date o_Message_sidebarItem o-message-squashed" t-att-class="{ 'o-message-selected': isSelected }">
|
||||
<t t-esc="shortTime"/>
|
||||
</div>
|
||||
<t t-if="message.date">
|
||||
<div class="o_Message_date o_Message_sidebarItem o-message-squashed" t-att-class="{ 'o-message-selected': isSelected }">
|
||||
<t t-esc="shortTime"/>
|
||||
</div>
|
||||
</t>
|
||||
<div class="o_Message_commands o_Message_sidebarCommands o_Message_sidebarItem o-message-squashed" t-att-class="{ 'o-message-selected': isSelected, 'o-mobile': env.messaging.device.isMobile }">
|
||||
<t t-if="message.message_type !== 'notification'">
|
||||
<div class="o_Message_command o_Message_commandStar fa"
|
||||
@@ -76,9 +78,11 @@
|
||||
Anonymous
|
||||
</div>
|
||||
</t>
|
||||
<div class="o_Message_date o_Message_headerDate" t-att-class="{ 'o-message-selected': isSelected }" t-att-title="datetime">
|
||||
- <t t-esc="message.dateFromNow"/>
|
||||
</div>
|
||||
<t t-if="message.date">
|
||||
<div class="o_Message_date o_Message_headerDate" t-att-class="{ 'o-message-selected': isSelected }" t-att-title="datetime">
|
||||
- <t t-esc="message.dateFromNow"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="message.isCurrentPartnerAuthor and threadView and threadView.thread and threadView.thread.hasSeenIndicators">
|
||||
<MessageSeenIndicator class="o_Message_seenIndicator" messageLocalId="message.localId" threadLocalId="threadView.thread.localId"/>
|
||||
</t>
|
||||
|
||||
@@ -56,6 +56,7 @@ QUnit.test('basic rendering', async function (assert) {
|
||||
const message = this.env.models['mail.message'].create({
|
||||
author: insert({ id: 7, display_name: "Demo User" }),
|
||||
body: "<p>Test</p>",
|
||||
date: moment(),
|
||||
id: 100,
|
||||
});
|
||||
await this.createMessageComponent(message);
|
||||
|
||||
@@ -172,6 +172,11 @@ class MessageList extends Component {
|
||||
* @returns {string}
|
||||
*/
|
||||
getDateDay(message) {
|
||||
if (!message.date) {
|
||||
// Without a date, we assume that it's a today message. This is
|
||||
// mainly done to avoid flicker inside the UI.
|
||||
return this.env._t("Today");
|
||||
}
|
||||
const date = message.date.format('YYYY-MM-DD');
|
||||
if (date === moment().format('YYYY-MM-DD')) {
|
||||
return this.env._t("Today");
|
||||
@@ -293,7 +298,10 @@ class MessageList extends Component {
|
||||
if (!this.props.hasSquashCloseMessages) {
|
||||
return false;
|
||||
}
|
||||
if (Math.abs(message.date.diff(prevMessage.date)) > 60000) {
|
||||
if (!prevMessage.date && message.date) {
|
||||
return false;
|
||||
}
|
||||
if (message.date && prevMessage.date && Math.abs(message.date.diff(prevMessage.date)) > 60000) {
|
||||
// more than 1 min. elasped
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
(<t t-esc="group.notifications.length"/>)
|
||||
</span>
|
||||
<span class="o-autogrow"/>
|
||||
<span class="o_NotificationGroup_date">
|
||||
<t t-esc="group.date.fromNow()"/>
|
||||
</span>
|
||||
<t t-if="group.date">
|
||||
<span class="o_NotificationGroup_date">
|
||||
<t t-esc="group.date.fromNow()"/>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_NotificationGroup_core">
|
||||
<span class="o_NotificationGroup_coreItem o_NotificationGroup_inlineText">
|
||||
|
||||
@@ -84,7 +84,7 @@ class NotificationList extends Component {
|
||||
return 1;
|
||||
}
|
||||
if (t1.lastNeedactionMessageAsOriginThread && t2.lastNeedactionMessageAsOriginThread) {
|
||||
return t1.lastNeedactionMessageAsOriginThread.date.isBefore(t2.lastNeedactionMessageAsOriginThread.date) ? 1 : -1;
|
||||
return t1.lastNeedactionMessageAsOriginThread.id < t2.lastNeedactionMessageAsOriginThread.id ? 1 : -1;
|
||||
}
|
||||
if (t1.lastNeedactionMessageAsOriginThread) {
|
||||
return -1;
|
||||
@@ -112,7 +112,7 @@ class NotificationList extends Component {
|
||||
return 1;
|
||||
}
|
||||
if (t1.lastMessage && t2.lastMessage) {
|
||||
return t1.lastMessage.date.isBefore(t2.lastMessage.date) ? 1 : -1;
|
||||
return t1.lastMessage.id < t2.lastMessage.id ? 1 : -1;
|
||||
}
|
||||
if (t1.lastMessage) {
|
||||
return -1;
|
||||
@@ -133,9 +133,8 @@ class NotificationList extends Component {
|
||||
if (props.filter === 'all') {
|
||||
const notificationGroups = this.env.messaging.notificationGroupManager.groups;
|
||||
notifications = Object.values(notificationGroups)
|
||||
.sort((group1, group2) =>
|
||||
group1.date.isAfter(group2.date) ? -1 : 1
|
||||
).map(notificationGroup => {
|
||||
.sort((group1, group2) => group1.sequence - group2.sequence)
|
||||
.map(notificationGroup => {
|
||||
return {
|
||||
notificationGroup,
|
||||
uniqueId: notificationGroup.localId,
|
||||
|
||||
+1
-4
@@ -419,13 +419,12 @@ QUnit.test('different mail.channel are not grouped', async function (assert) {
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('multiple grouped notifications by document model, sorted by date desc', async function (assert) {
|
||||
QUnit.test('multiple grouped notifications by document model, sorted by the most recent message of each group', async function (assert) {
|
||||
assert.expect(9);
|
||||
|
||||
this.data['mail.message'].records.push(
|
||||
// first message that is expected to have a failure
|
||||
{
|
||||
date: moment.utc().format("YYYY-MM-DD HH:mm:ss"), // random date
|
||||
id: 11, // random unique id, will be used to link failure to message
|
||||
message_type: 'email', // message must be email (goal of the test)
|
||||
model: 'res.partner', // different model from second message
|
||||
@@ -434,8 +433,6 @@ QUnit.test('multiple grouped notifications by document model, sorted by date des
|
||||
},
|
||||
// second message that is expected to have a failure
|
||||
{
|
||||
// random date, later than first message
|
||||
date: moment.utc().add(1, 'days').format("YYYY-MM-DD HH:mm:ss"),
|
||||
id: 12, // random unique id, will be used to link failure to message
|
||||
message_type: 'email', // message must be email (goal of the test)
|
||||
model: 'res.company', // different model from first message
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
(<t t-esc="thread.needactionMessagesAsOriginThread.length"/>)
|
||||
</span>
|
||||
<span class="o-autogrow"/>
|
||||
<t t-if="thread.lastNeedactionMessageAsOriginThread">
|
||||
<t t-if="thread.lastNeedactionMessageAsOriginThread and thread.lastNeedactionMessageAsOriginThread.date">
|
||||
<span class="o_ThreadNeedactionPreview_date">
|
||||
<t t-esc="thread.lastNeedactionMessageAsOriginThread.date.fromNow()"/>
|
||||
</span>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</span>
|
||||
</t>
|
||||
<span class="o-autogrow"/>
|
||||
<t t-if="thread.lastMessage">
|
||||
<t t-if="thread.lastMessage and thread.lastMessage.date">
|
||||
<span class="o_ThreadPreview_date" t-att-class="{ 'o-muted': thread.localMessageUnreadCounter === 0 }">
|
||||
<t t-esc="thread.lastMessage.date.fromNow()"/>
|
||||
</span>
|
||||
|
||||
@@ -586,9 +586,10 @@ function factory(dependencies) {
|
||||
inverse: 'checkedMessages',
|
||||
readonly: true,
|
||||
}),
|
||||
date: attr({
|
||||
default: moment(),
|
||||
}),
|
||||
/**
|
||||
* Determines the date of the message as a moment object.
|
||||
*/
|
||||
date: attr(),
|
||||
/**
|
||||
* States the time elapsed since date up to now.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { registerNewModel } from '@mail/model/model_core';
|
||||
import { attr, many2one, one2many } from '@mail/model/model_field';
|
||||
import { insert, unlink } from '@mail/model/model_field_command';
|
||||
import { clear, insert, unlink } from '@mail/model/model_field_command';
|
||||
|
||||
function factory(dependencies) {
|
||||
|
||||
@@ -51,13 +51,17 @@ function factory(dependencies) {
|
||||
* @returns {mail.thread|undefined}
|
||||
*/
|
||||
_computeThread() {
|
||||
if (this.res_id) {
|
||||
return insert({
|
||||
id: this.res_id,
|
||||
model: this.res_model,
|
||||
});
|
||||
const notificationsThreadIds = this.notifications
|
||||
.filter(notification => notification.message && notification.message.originThread)
|
||||
.map(notification => notification.message.originThread.id);
|
||||
const threadIds = new Set(notificationsThreadIds);
|
||||
if (threadIds.size !== 1) {
|
||||
return unlink();
|
||||
}
|
||||
return unlink();
|
||||
return insert({
|
||||
id: notificationsThreadIds[0],
|
||||
model: this.res_model,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +71,32 @@ function factory(dependencies) {
|
||||
return `${this.modelName}_${data.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the most recent date inside the notification messages.
|
||||
*
|
||||
* @private
|
||||
* @returns {moment|undefined}
|
||||
*/
|
||||
_computeDate() {
|
||||
const dates = this.notifications
|
||||
.filter(notification => notification.message && notification.message.date)
|
||||
.map(notification => notification.message.date);
|
||||
if (dates.length === 0) {
|
||||
return clear();
|
||||
}
|
||||
return moment.max(dates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the position of the group inside the notification list.
|
||||
*
|
||||
* @private
|
||||
* @returns {number}
|
||||
*/
|
||||
_computeSequence() {
|
||||
return -Math.max(...this.notifications.map(notification => notification.message.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the view that displays all the records of the group.
|
||||
*
|
||||
@@ -97,23 +127,63 @@ function factory(dependencies) {
|
||||
}
|
||||
|
||||
NotificationGroup.fields = {
|
||||
date: attr(),
|
||||
/**
|
||||
* States the most recent date of all the notification message.
|
||||
*/
|
||||
date: attr({
|
||||
compute: '_computeDate',
|
||||
dependencies: [
|
||||
'notifications',
|
||||
'notificationsMessage',
|
||||
'notificationsMessageDate',
|
||||
],
|
||||
}),
|
||||
id: attr({
|
||||
required: true,
|
||||
}),
|
||||
notification_type: attr(),
|
||||
notifications: one2many('mail.notification'),
|
||||
res_id: attr(),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
notificationsMessage: one2many('mail.message', {
|
||||
related: 'notifications.message',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
notificationsMessageDate: attr({
|
||||
related: 'notificationsMessage.date',
|
||||
}),
|
||||
/**
|
||||
* Serves as compute dependency.
|
||||
*/
|
||||
notificationsMessageOriginThread: one2many('mail.thread', {
|
||||
related: 'notificationsMessage.originThread',
|
||||
}),
|
||||
res_model: attr(),
|
||||
res_model_name: attr(),
|
||||
/**
|
||||
* States the position of the group inside the notification list.
|
||||
*/
|
||||
sequence: attr({
|
||||
compute: '_computeSequence',
|
||||
default: 0,
|
||||
dependencies: [
|
||||
'notifications',
|
||||
'notificationsMessage',
|
||||
],
|
||||
}),
|
||||
/**
|
||||
* Related thread when the notification group concerns a single thread.
|
||||
*/
|
||||
thread: many2one('mail.thread', {
|
||||
compute: '_computeThread',
|
||||
dependencies: [
|
||||
'res_id',
|
||||
'res_model',
|
||||
'notifications',
|
||||
'notificationsMessage',
|
||||
'notificationsMessageOriginThread',
|
||||
],
|
||||
})
|
||||
};
|
||||
|
||||
-19
@@ -33,25 +33,6 @@ function factory(dependencies) {
|
||||
res_model_name: thread.model_name,
|
||||
});
|
||||
group.update({ notifications: link(notification) });
|
||||
// keep res_id only if all notifications are for the same record
|
||||
// set null if multiple records are present in the group
|
||||
let res_id = group.res_id;
|
||||
if (group.res_id === undefined) {
|
||||
res_id = thread.id;
|
||||
} else if (group.res_id !== thread.id) {
|
||||
res_id = null;
|
||||
}
|
||||
// keep only the most recent date from all notification messages
|
||||
let date = group.date;
|
||||
if (!date) {
|
||||
date = notification.message.date;
|
||||
} else {
|
||||
date = moment.max(group.date, notification.message.date);
|
||||
}
|
||||
group.update({
|
||||
date,
|
||||
res_id,
|
||||
});
|
||||
// avoid linking the same group twice when adding a notification
|
||||
// to an existing group
|
||||
if (!groups.includes(group)) {
|
||||
|
||||
@@ -124,7 +124,7 @@ class MockModels {
|
||||
attachment_ids: { string: "Attachments", type: 'many2many', relation: 'ir.attachment', default: [] },
|
||||
author_id: { string: "Author", type: 'many2one', relation: 'res.partner', default() { return this.currentPartnerId; } },
|
||||
body: { string: "Contents", type: 'html', default: "<p></p>" },
|
||||
date: { string: "Date", type: 'datetime' },
|
||||
date: { string: "Date", type: 'datetime', default() { return moment.utc().format("YYYY-MM-DD HH:mm:ss"); } },
|
||||
email_from: { string: "From", type: 'char' },
|
||||
history_partner_ids: { string: "Partners with History", type: 'many2many', relation: 'res.partner' },
|
||||
id: { string: "Id", type: 'integer' },
|
||||
|
||||
+4
-4
@@ -108,7 +108,7 @@ QUnit.test('notifications grouped by notification_type', async function (assert)
|
||||
// first message that is expected to have a failure
|
||||
{
|
||||
id: 11, // random unique id, will be used to link failure to message
|
||||
message_type: 'email', // different type from second message
|
||||
message_type: 'sms', // different type from second message
|
||||
model: 'res.partner', // same model as second message (and not `mail.channel`)
|
||||
res_id: 31, // same res_id as second message
|
||||
res_model_name: "Partner", // random related model name
|
||||
@@ -116,7 +116,7 @@ QUnit.test('notifications grouped by notification_type', async function (assert)
|
||||
// second message that is expected to have a failure
|
||||
{
|
||||
id: 12, // random unique id, will be used to link failure to message
|
||||
message_type: 'sms', // different type from first message
|
||||
message_type: 'email', // different type from first message
|
||||
model: 'res.partner', // same model as first message (and not `mail.channel`)
|
||||
res_id: 31, // same res_id as first message
|
||||
res_model_name: "Partner", // same related model name for consistency
|
||||
@@ -127,13 +127,13 @@ QUnit.test('notifications grouped by notification_type', async function (assert)
|
||||
{
|
||||
mail_message_id: 11, // id of the related first message
|
||||
notification_status: 'exception', // necessary value to have a failure
|
||||
notification_type: 'email', // different type from second failure
|
||||
notification_type: 'sms', // different type from second failure
|
||||
},
|
||||
// second failure that is expected to be used in the test
|
||||
{
|
||||
mail_message_id: 12, // id of the related second message
|
||||
notification_status: 'exception', // necessary value to have a failure
|
||||
notification_type: 'sms', // different type from first failure
|
||||
notification_type: 'email', // different type from first failure
|
||||
}
|
||||
);
|
||||
await this.start();
|
||||
|
||||
+4
-4
@@ -103,7 +103,7 @@ QUnit.test('notifications grouped by notification_type', async function (assert)
|
||||
// first message that is expected to have a failure
|
||||
{
|
||||
id: 11, // random unique id, will be used to link failure to message
|
||||
message_type: 'email', // different type from second message
|
||||
message_type: 'snailmail', // different type from second message
|
||||
model: 'res.partner', // same model as second message (and not `mail.channel`)
|
||||
res_id: 31, // same res_id as second message
|
||||
res_model_name: "Partner", // random related model name
|
||||
@@ -111,7 +111,7 @@ QUnit.test('notifications grouped by notification_type', async function (assert)
|
||||
// second message that is expected to have a failure
|
||||
{
|
||||
id: 12, // random unique id, will be used to link failure to message
|
||||
message_type: 'snailmail', // different type from first message
|
||||
message_type: 'email', // different type from first message
|
||||
model: 'res.partner', // same model as first message (and not `mail.channel`)
|
||||
res_id: 31, // same res_id as first message
|
||||
res_model_name: "Partner", // same related model name for consistency
|
||||
@@ -122,13 +122,13 @@ QUnit.test('notifications grouped by notification_type', async function (assert)
|
||||
{
|
||||
mail_message_id: 11, // id of the related first message
|
||||
notification_status: 'exception', // necessary value to have a failure
|
||||
notification_type: 'email', // different type from second failure
|
||||
notification_type: 'snail', // different type from second failure
|
||||
},
|
||||
// second failure that is expected to be used in the test
|
||||
{
|
||||
mail_message_id: 12, // id of the related second message
|
||||
notification_status: 'exception', // necessary value to have a failure
|
||||
notification_type: 'snail', // different type from first failure
|
||||
notification_type: 'email', // different type from first failure
|
||||
}
|
||||
);
|
||||
await this.start();
|
||||
|
||||
Reference in New Issue
Block a user