diff --git a/addons/mail/static/src/core/common/discuss_app_category_model.js b/addons/mail/static/src/core/common/discuss_app_category_model.js
index 0ec6ca23532..8bc371d38c4 100644
--- a/addons/mail/static/src/core/common/discuss_app_category_model.js
+++ b/addons/mail/static/src/core/common/discuss_app_category_model.js
@@ -1,6 +1,5 @@
/* @odoo-module */
-import { assignDefined } from "@mail/utils/common/misc";
import { Record } from "./record";
export class DiscussAppCategory extends Record {
@@ -11,10 +10,7 @@ export class DiscussAppCategory extends Record {
}
/** @returns {import("models").DiscussAppCategory} */
static insert(data) {
- /** @type {import("models").DiscussAppCategory} */
- const category = this.preinsert(data);
- assignDefined(category, data);
- return category;
+ return super.insert(data);
}
/** @type {string} */
diff --git a/addons/mail/static/src/core/common/follower_model.js b/addons/mail/static/src/core/common/follower_model.js
index 9c4aad8831c..284f64cdea0 100644
--- a/addons/mail/static/src/core/common/follower_model.js
+++ b/addons/mail/static/src/core/common/follower_model.js
@@ -12,10 +12,7 @@ export class Follower extends Record {
}
/** @returns {import("models").Follower} */
static insert(data) {
- /** @type {import("models").Follower} */
- const follower = this.preinsert(data);
- Object.assign(follower, data);
- return follower;
+ return super.insert(data);
}
followedThread = Record.one("Thread");
diff --git a/addons/mail/static/src/core/common/link_preview_model.js b/addons/mail/static/src/core/common/link_preview_model.js
index 801e7e95c3a..f8576e8b905 100644
--- a/addons/mail/static/src/core/common/link_preview_model.js
+++ b/addons/mail/static/src/core/common/link_preview_model.js
@@ -8,24 +8,14 @@ export class LinkPreview extends Record {
static get(data) {
return super.get(data);
}
- /**
- * @param {Object} data
- * @returns {import("models").LinkPreview}
- */
+ /** @returns {import("models").LinkPreview} */
static insert(data) {
- const message = this.store.Message.get(data.message_id);
- data.message = message;
- delete data.message_id;
- /** @type {import("models").LinkPreview} */
- const linkPreview = this.preinsert(data);
- Object.assign(linkPreview, data);
- message?.linkPreviews.add(linkPreview);
- return linkPreview;
+ return super.insert(data);
}
/** @type {number} */
id;
- message = Record.one("Message");
+ message = Record.one("Message", { inverse: "linkPreviews" });
/** @type {string} */
image_mimetype;
/** @type {string} */
@@ -58,7 +48,6 @@ export class LinkPreview extends Record {
get isCard() {
return !this.isImage && !this.isVideo;
}
-
}
LinkPreview.register();
diff --git a/addons/mail/static/src/core/common/mail_core_common_service.js b/addons/mail/static/src/core/common/mail_core_common_service.js
index d77488dadfe..d0afd78c5cf 100644
--- a/addons/mail/static/src/core/common/mail_core_common_service.js
+++ b/addons/mail/static/src/core/common/mail_core_common_service.js
@@ -49,8 +49,8 @@ export class MailCoreCommon {
if (!message) {
continue;
}
- message.delete();
this.env.bus.trigger("mail.message/delete", { message });
+ message.delete();
}
});
this.busService.subscribe("mail.message/notification_update", (payload) => {
@@ -69,7 +69,20 @@ export class MailCoreCommon {
const { message_ids: messageIds, starred } = payload;
for (const messageId of messageIds) {
const message = this.store.Message.insert({ id: messageId });
- this.messageService.updateStarred(message, starred);
+ message.isStarred = starred;
+ const starredBox = this.store.discuss.starred;
+ if (starred) {
+ starredBox.counter++;
+ starredBox.messages.add(message);
+ } else {
+ starredBox.counter--;
+ starredBox.messages.delete(message);
+ }
+ }
+ });
+ this.busService.subscribe("res.users.settings", (payload) => {
+ if (payload) {
+ this.userSettingsService.updateFromCommands(payload);
}
});
this.busService.subscribe("mail.record/insert", (payload) => {
@@ -100,18 +113,10 @@ export class MailCoreCommon {
}
const { Message: messageData } = payload;
if (messageData) {
- const isStarred = this.store.Message.get(messageData.id)?.isStarred;
- const message = this.store.Message.insert({
+ this.store.Message.insert({
...messageData,
body: messageData.body ? markup(messageData.body) : messageData.body,
});
- if (isStarred && message.isEmpty) {
- this.messageService.updateStarred(message, false);
- }
- }
- const { "res.users.settings": settings } = payload;
- if (settings) {
- this.userSettingsService.updateFromCommands(settings);
}
});
});
diff --git a/addons/mail/static/src/core/common/message.js b/addons/mail/static/src/core/common/message.js
index d16b1302178..efa8b278451 100644
--- a/addons/mail/static/src/core/common/message.js
+++ b/addons/mail/static/src/core/common/message.js
@@ -239,8 +239,8 @@ export class Message extends Component {
get showSubtypeDescription() {
return (
- this.message.subtypeDescription &&
- this.message.subtypeDescription.toLowerCase() !==
+ this.message.subtype_description &&
+ this.message.subtype_description.toLowerCase() !==
htmlToTextContentInline(this.message.body || "").toLowerCase()
);
}
@@ -252,7 +252,7 @@ export class Message extends Component {
if (this.props.message.type === "auto_comment") {
return _t("Automated message");
}
- if (!this.props.message.isDiscussion && this.props.message.type !== "user_notification") {
+ if (!this.props.message.is_discussion && this.props.message.type !== "user_notification") {
return _t("Note");
}
return _t("Message");
@@ -262,7 +262,7 @@ export class Message extends Component {
* @returns {boolean}
*/
get canAddReaction() {
- return Boolean(!this.message.isTransient && this.message.resId);
+ return Boolean(!this.message.is_transient && this.message.res_id);
}
get deletable() {
@@ -284,7 +284,7 @@ export class Message extends Component {
* @returns {boolean}
*/
get canToggleStar() {
- return Boolean(!this.message.isTransient && this.message.resId && this.store.user);
+ return Boolean(!this.message.is_transient && this.message.res_id && this.store.user);
}
get showUnfollow() {
@@ -440,7 +440,6 @@ export class Message extends Component {
const messageContent = convertBrToLineBreak(this.props.message.body);
this.props.message.composer = {
mentionedPartners: this.props.message.recipients,
- message: this.props.message,
textInputContent: messageContent,
selection: {
start: messageContent.length,
diff --git a/addons/mail/static/src/core/common/message.xml b/addons/mail/static/src/core/common/message.xml
index 0b78e640906..666aa820395 100644
--- a/addons/mail/static/src/core/common/message.xml
+++ b/addons/mail/static/src/core/common/message.xml
@@ -18,7 +18,7 @@
-
+
@@ -36,7 +36,7 @@
-
+
-
@@ -45,11 +45,11 @@
className="'ms-1'"
message="props.message"
thread="props.thread"/>
-
-
+
+
on
-
+
(from )
@@ -72,20 +72,20 @@
>
-
+
diff --git a/addons/mail/static/src/core/common/message_model.js b/addons/mail/static/src/core/common/message_model.js
index 89bef68e4d1..cf87ad9dea1 100644
--- a/addons/mail/static/src/core/common/message_model.js
+++ b/addons/mail/static/src/core/common/message_model.js
@@ -2,7 +2,7 @@
import { Record } from "@mail/core/common/record";
import { htmlToTextContentInline } from "@mail/utils/common/format";
-import { assignDefined } from "@mail/utils/common/misc";
+import { assignDefined, assignIn, onChange } from "@mail/utils/common/misc";
import { toRaw } from "@odoo/owl";
@@ -17,14 +17,23 @@ export class Message extends Record {
static id = "id";
/** @type {Object.} */
static records = {};
+ static new(data) {
+ const message = super.new(data);
+ onChange(message, "isEmpty", () => {
+ if (message.isEmpty && message.isStarred) {
+ message.isStarred = false;
+ const starred = this.store.discuss.starred;
+ starred.counter--;
+ starred.messages.delete(message);
+ }
+ });
+ return message;
+ }
/** @returns {import("models").Message} */
static get(data) {
return super.get(data);
}
- /**
- * @param {Object} data
- * @returns {import("models").Message}
- */
+ /** @returns {import("models").Message} */
static insert(data) {
if (data.res_id) {
this.store.Thread.insert({
@@ -32,46 +41,23 @@ export class Message extends Record {
id: data.res_id,
});
}
- /** @type {import("models").Message} */
- const message = this.preinsert(data);
- message.update(data);
- return message;
+ return super.insert(data);
}
/** @param {Object} data */
update(data) {
const {
- attachment_ids: attachments = this.attachments,
- default_subject: defaultSubject = this.defaultSubject,
- is_discussion: isDiscussion = this.isDiscussion,
- is_note: isNote = this.isNote,
- is_transient: isTransient = this.isTransient,
- linkPreviews = this.linkPreviews,
message_type: type = this.type,
- model: resModel = this.resModel,
module_icon,
- notifications = this.notifications,
- parentMessage,
- recipients = this.recipients,
record_name,
- res_id: resId = this.resId,
res_model_name,
- subtype_description: subtypeDescription = this.subtypeDescription,
...remainingData
} = data;
assignDefined(this, remainingData);
assignDefined(this, {
- defaultSubject,
- isDiscussion,
- isNote,
isStarred: this._store.user
? this.starred_partner_ids.includes(this._store.user.id)
: false,
- isTransient,
- parentMessage: parentMessage || undefined,
- resId,
- resModel,
- subtypeDescription,
type,
});
// origin thread before other information (in particular notification insert uses it)
@@ -85,13 +71,7 @@ export class Message extends Record {
: record_name || undefined,
});
}
- this.attachments = attachments.map((attachment) => ({ message: this, ...attachment }));
- if ("author" in data) {
- this.author = data.author;
- }
- this.linkPreviews = linkPreviews.map((data) => ({ ...data, message: this }));
- this.notifications = notifications.map((notif) => ({ ...notif, message: this }));
- this.recipients = recipients.map((recipient) => ({ ...recipient, type: "partner" }));
+ assignIn(this, data, ["author", "notifications", "reactions", "recipients"]);
if ("user_follower_id" in data && data.user_follower_id && this._store.self) {
this.originThread.selfFollower = {
followedThread: this.originThread,
@@ -100,9 +80,6 @@ export class Message extends Record {
partner: this._store.self,
};
}
- if ("messageReactionGroups" in data) {
- this.reactions = data.messageReactionGroups;
- }
if (this.isNotification && !this.notificationType) {
const parser = new DOMParser();
const htmlBody = parser.parseFromString(this.body, "text/html");
@@ -110,36 +87,36 @@ export class Message extends Record {
}
}
- attachments = Record.many("Attachment");
+ attachments = Record.many("Attachment", { inverse: "message" });
author = Record.one("Persona");
/** @type {string} */
body;
- composer = Record.one("Composer", { onDelete: (r) => r.delete() });
+ composer = Record.one("Composer", { inverse: "message", onDelete: (r) => r.delete() });
/** @type {string} */
- defaultSubject;
+ default_subject;
/** @type {number|string} */
id;
/** @type {boolean} */
- isDiscussion;
+ is_discussion;
/** @type {boolean} */
- isNote;
+ is_note;
/** @type {boolean} */
isStarred;
/** @type {boolean} */
- isTransient;
- linkPreviews = Record.many("LinkPreview");
+ is_transient;
+ linkPreviews = Record.many("LinkPreview", { inverse: "message" });
/** @type {number[]} */
needaction_partner_ids = [];
/** @type {number[]} */
history_partner_ids = [];
parentMessage = Record.one("Message");
- reactions = Record.many("MessageReactions");
- notifications = Record.many("Notification");
+ reactions = Record.many("MessageReactions", { inverse: "message" });
+ notifications = Record.many("Notification", { inverse: "message" });
recipients = Record.many("Persona");
/** @type {number|string} */
- resId;
+ res_id;
/** @type {string|undefined} */
- resModel;
+ model;
/** @type {string} */
scheduledDatetime;
/** @type {Number[]} */
@@ -147,7 +124,7 @@ export class Message extends Record {
/** @type {string} */
subject;
/** @type {string} */
- subtypeDescription;
+ subtype_description;
/** @type {Object[]} */
trackingValues = [];
/** @type {string} */
@@ -207,7 +184,7 @@ export class Message extends Record {
}
get isHighlightedFromMention() {
- return this.isSelfMentioned && this.resModel === "discuss.channel";
+ return this.isSelfMentioned && this.model === "discuss.channel";
}
get isSelfAuthored() {
@@ -222,7 +199,7 @@ export class Message extends Record {
}
get hasActions() {
- return !this.isTransient;
+ return !this.is_transient;
}
/**
@@ -236,7 +213,7 @@ export class Message extends Record {
* @returns {boolean}
*/
get isNotification() {
- return this.type === "notification" && this.resModel === "discuss.channel";
+ return this.type === "notification" && this.model === "discuss.channel";
}
get isSubjectSimilarToOriginThreadName() {
@@ -251,17 +228,17 @@ export class Message extends Record {
get isSubjectDefault() {
const threadName = this.originThread?.name?.trim().toLowerCase();
- const defaultSubject = this.defaultSubject ? this.defaultSubject.toLowerCase() : "";
+ const defaultSubject = this.default_subject ? this.default_subject.toLowerCase() : "";
const candidates = new Set([defaultSubject, threadName]);
return candidates.has(this.subject?.toLowerCase());
}
get originThread() {
- return this._store.Thread.get({ model: this.resModel, id: this.resId });
+ return this._store.Thread.get({ model: this.model, id: this.res_id });
}
get resUrl() {
- return `${url("/web")}#model=${this.resModel}&id=${this.resId}`;
+ return `${url("/web")}#model=${this.model}&id=${this.res_id}`;
}
get editDate() {
@@ -277,7 +254,7 @@ export class Message extends Record {
this.isBodyEmpty &&
this.attachments.length === 0 &&
this.trackingValues.length === 0 &&
- !this.subtypeDescription
+ !this.subtype_description
);
}
get isBodyEmpty() {
diff --git a/addons/mail/static/src/core/common/message_reactions.xml b/addons/mail/static/src/core/common/message_reactions.xml
index 786c1930fe6..9d671fc684a 100644
--- a/addons/mail/static/src/core/common/message_reactions.xml
+++ b/addons/mail/static/src/core/common/message_reactions.xml
@@ -4,9 +4,9 @@
+ t-attf-class="{{ props.message.is_discussion ? 'mt-n2' : 'mt-1' }}">