[FIX] mail: do not reload chatter when not needed
This RPC /mail/thread/data is called on every form view change, even when not saved. It comes from the chatter onWillUpdateProps and calling `load` then `fetchData` We don't want to reload chatter when every time props updates. Do it when the record `onWillLoadRoot` closes odoo/odoo#140662 Task: 3569123 Related: odoo/enterprise#50607 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -86,10 +86,11 @@ export class Activity extends Component {
|
||||
}
|
||||
|
||||
async onFileUploaded(data) {
|
||||
const thread = this.thread;
|
||||
const { id: attachmentId } = await this.attachmentUploader.uploadData(data);
|
||||
await this.activityService.markAsDone(this.props.data, [attachmentId]);
|
||||
this.props.onUpdate();
|
||||
await this.threadService.fetchNewMessages(this.thread);
|
||||
this.props.onUpdate(thread);
|
||||
await this.threadService.fetchNewMessages(thread);
|
||||
}
|
||||
|
||||
onClickAvatar(ev) {
|
||||
@@ -102,15 +103,17 @@ export class Activity extends Component {
|
||||
}
|
||||
|
||||
async edit() {
|
||||
const thread = this.thread;
|
||||
const id = this.props.data.id;
|
||||
await this.env.services["mail.activity"].edit(id);
|
||||
this.props.onUpdate();
|
||||
this.props.onUpdate(thread);
|
||||
}
|
||||
|
||||
async unlink() {
|
||||
const thread = this.thread;
|
||||
this.activityService.delete(this.props.data);
|
||||
await this.env.services.orm.unlink("mail.activity", [this.props.data.id]);
|
||||
this.props.onUpdate();
|
||||
this.props.onUpdate(thread);
|
||||
}
|
||||
|
||||
get thread() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
|
||||
/**
|
||||
@@ -19,6 +20,10 @@ export class ActivityMailTemplate extends Component {
|
||||
static props = ["activity", "onClickButtons?", "onUpdate?"];
|
||||
static template = "mail.ActivityMailTemplate";
|
||||
|
||||
setup() {
|
||||
this.store = useState(useService("mail.store"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
* @param {Object} mailTemplate
|
||||
@@ -54,10 +59,14 @@ export class ActivityMailTemplate extends Component {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.props.onClickButtons();
|
||||
const thread = this.store.Thread.get({
|
||||
model: this.props.activity.res_model,
|
||||
id: this.props.activity.res_id,
|
||||
});
|
||||
await this.env.services.orm.call(this.props.activity.res_model, "activity_send_mail", [
|
||||
[this.props.activity.res_id],
|
||||
mailTemplate.id,
|
||||
]);
|
||||
this.props.onUpdate();
|
||||
this.props.onUpdate(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export class ActivityMarkAsDone extends Component {
|
||||
const thread = this.threadService.getThread(res_model, res_id);
|
||||
await this.env.services["mail.activity"].markAsDone(this.props.activity);
|
||||
if (this.props.reload) {
|
||||
this.props.reload(this.props.activity.res_id, ["activities"]);
|
||||
this.props.reload(thread, ["activities"]);
|
||||
}
|
||||
await this.threadService.fetchNewMessages(thread);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export class ActivityMarkAsDone extends Component {
|
||||
);
|
||||
this.threadService.fetchNewMessages(thread);
|
||||
if (this.props.reload) {
|
||||
this.props.reload(this.props.activity.res_id, ["activities", "attachments"]);
|
||||
this.props.reload(thread, ["activities", "attachments"]);
|
||||
}
|
||||
if (!action) {
|
||||
return;
|
||||
@@ -65,7 +65,7 @@ export class ActivityMarkAsDone extends Component {
|
||||
});
|
||||
});
|
||||
if (this.props.reload) {
|
||||
this.props.reload(this.props.activity.res_id, ["activities"]);
|
||||
this.props.reload(thread, ["activities"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ export class Chatter extends Component {
|
||||
}
|
||||
if (isDragSourceExternalFile(ev.dataTransfer)) {
|
||||
const files = [...ev.dataTransfer.files];
|
||||
if (!this.props.threadId) {
|
||||
if (!this.state.thread.id) {
|
||||
const saved = await this.props.saveRecord?.();
|
||||
if (!saved) {
|
||||
return;
|
||||
@@ -138,36 +138,20 @@ export class Chatter extends Component {
|
||||
"o-mail-Chatter-dropzone"
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
if (this.props.threadId) {
|
||||
this.state.thread = this.store.Thread.insert({
|
||||
id: this.props.threadId,
|
||||
model: this.props.threadModel,
|
||||
name: this.props.webRecord?.data?.display_name || undefined,
|
||||
});
|
||||
}
|
||||
await this.load(this.props.threadId, [
|
||||
"followers",
|
||||
"attachments",
|
||||
"suggestedRecipients",
|
||||
]);
|
||||
onMounted(() => {
|
||||
this.changeThread(this.props.threadModel, this.props.threadId, this.props.webRecord);
|
||||
this.load(this.state.thread, ["followers", "attachments", "suggestedRecipients"]);
|
||||
});
|
||||
onWillUpdateProps((nextProps) => {
|
||||
this.load(nextProps.threadId, ["followers", "attachments", "suggestedRecipients"]);
|
||||
if (nextProps.threadId === false) {
|
||||
this.state.composerType = false;
|
||||
if (
|
||||
this.props.threadId !== nextProps.threadId ||
|
||||
this.props.threadModel !== nextProps.threadModel
|
||||
) {
|
||||
this.changeThread(nextProps.threadModel, nextProps.threadId, nextProps.webRecord);
|
||||
}
|
||||
this.attachmentUploader.thread = this.threadService.getThread(
|
||||
nextProps.threadModel,
|
||||
nextProps.threadId
|
||||
);
|
||||
if (this.onNextUpdate) {
|
||||
if (!this.onNextUpdate(nextProps)) {
|
||||
this.onNextUpdate = null;
|
||||
}
|
||||
}
|
||||
if (nextProps.threadId) {
|
||||
this.closeSearch();
|
||||
if (this.env.chatter?.fetchOnWillUpdateProps) {
|
||||
this.env.chatter.fetchOnWillUpdateProps = false;
|
||||
this.load(this.state.thread, ["followers", "attachments", "suggestedRecipients"]);
|
||||
}
|
||||
});
|
||||
useEffect(
|
||||
@@ -223,7 +207,7 @@ export class Chatter extends Component {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isDisabled() {
|
||||
return !this.props.threadId || !this.state.thread?.hasReadAccess;
|
||||
return !this.state.thread.id || !this.state.thread?.hasReadAccess;
|
||||
}
|
||||
|
||||
get attachments() {
|
||||
@@ -252,56 +236,60 @@ export class Chatter extends Component {
|
||||
return markup(formatter.format(recipients));
|
||||
}
|
||||
|
||||
changeThread(threadModel, threadId, webRecord) {
|
||||
this.state.thread = this.threadService.getThread(threadModel, threadId);
|
||||
this.state.thread.name = webRecord?.data?.display_name || undefined;
|
||||
this.attachmentUploader.thread = this.state.thread;
|
||||
if (threadId === false) {
|
||||
this.state.composerType = false;
|
||||
} else {
|
||||
this.onThreadCreated?.(this.state.thread);
|
||||
this.onThreadCreated = null;
|
||||
this.closeSearch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} threadId
|
||||
* Fetch data for the thread according to the request list.
|
||||
* @param {import("models").Thread} thread
|
||||
* @param {['activities'|'followers'|'attachments'|'messages'|'suggestedRecipients']} requestList
|
||||
*/
|
||||
load(
|
||||
threadId = this.props.threadId,
|
||||
requestList = ["followers", "attachments", "messages", "suggestedRecipients"]
|
||||
) {
|
||||
const { threadModel } = this.props;
|
||||
this.state.thread = this.threadService.getThread(threadModel, threadId);
|
||||
if (!threadId) {
|
||||
load(thread, requestList = ["followers", "attachments", "messages", "suggestedRecipients"]) {
|
||||
if (!thread.id || !this.state.thread?.eq(thread)) {
|
||||
return;
|
||||
}
|
||||
if (this.props.hasActivities && !requestList.includes("activities")) {
|
||||
requestList.push("activities");
|
||||
}
|
||||
this.threadService.fetchData(this.state.thread, requestList);
|
||||
this.threadService.fetchData(thread, requestList);
|
||||
}
|
||||
|
||||
async _follow(threadModel, threadId) {
|
||||
await this.orm.call(threadModel, "message_subscribe", [[threadId]], {
|
||||
async _follow(thread) {
|
||||
await this.orm.call(thread.model, "message_subscribe", [[thread.id]], {
|
||||
partner_ids: [this.store.self.id],
|
||||
});
|
||||
this.onFollowerChanged();
|
||||
this.onFollowerChanged(thread);
|
||||
}
|
||||
|
||||
async onClickFollow() {
|
||||
if (this.props.threadId) {
|
||||
this._follow(this.props.threadModel, this.props.threadId);
|
||||
if (this.state.thread.id) {
|
||||
this._follow(this.state.thread);
|
||||
} else {
|
||||
this.onNextUpdate = (nextProps) => {
|
||||
if (nextProps.threadId) {
|
||||
this._follow(nextProps.threadModel, nextProps.threadId);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
this.onThreadCreated = this._follow;
|
||||
await this.props.saveRecord?.();
|
||||
}
|
||||
}
|
||||
|
||||
async onClickUnfollow() {
|
||||
await this.threadService.removeFollower(this.state.thread.selfFollower);
|
||||
this.onFollowerChanged();
|
||||
const thread = this.state.thread;
|
||||
await this.threadService.removeFollower(thread.selfFollower);
|
||||
this.onFollowerChanged(thread);
|
||||
}
|
||||
|
||||
onFollowerChanged() {
|
||||
onFollowerChanged(thread) {
|
||||
document.body.click(); // hack to close dropdown
|
||||
this.reloadParentView();
|
||||
this.load(this.props.threadId, ["followers", "suggestedRecipients"]);
|
||||
this.load(thread, ["followers", "suggestedRecipients"]);
|
||||
}
|
||||
|
||||
onPostCallback() {
|
||||
@@ -311,11 +299,11 @@ export class Chatter extends Component {
|
||||
this.toggleComposer();
|
||||
this.state.jumpThreadPresent++;
|
||||
// Load new messages to fetch potential new messages from other users (useful due to lack of auto-sync in chatter).
|
||||
this.load(this.props.threadId, ["followers", "messages", "suggestedRecipients"]);
|
||||
this.load(this.state.thread, ["followers", "messages", "suggestedRecipients"]);
|
||||
}
|
||||
|
||||
onAddFollowers() {
|
||||
this.load(this.state.thread.id, ["followers", "suggestedRecipients"]);
|
||||
this.load(this.state.thread, ["followers", "suggestedRecipients"]);
|
||||
if (this.props.hasParentReloadOnFollowersUpdate) {
|
||||
this.reloadParentView();
|
||||
}
|
||||
@@ -337,19 +325,10 @@ export class Chatter extends Component {
|
||||
this.state.composerType = mode;
|
||||
}
|
||||
};
|
||||
if (this.props.threadId) {
|
||||
if (this.state.thread.id) {
|
||||
toggle();
|
||||
} else {
|
||||
this.onNextUpdate = (nextProps) => {
|
||||
// @returns {boolean} retry on next update
|
||||
// if there is no threadId, the save operation probably failed
|
||||
// probably because some required field is not set
|
||||
if (nextProps.threadId) {
|
||||
toggle();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
this.onThreadCreated = toggle;
|
||||
this.props.saveRecord?.();
|
||||
}
|
||||
}
|
||||
@@ -360,20 +339,14 @@ export class Chatter extends Component {
|
||||
|
||||
async scheduleActivity() {
|
||||
this.closeSearch();
|
||||
const schedule = async (threadId) => {
|
||||
await this.activityService.schedule(this.props.threadModel, [threadId]);
|
||||
this.load(this.props.threadId, ["activities", "messages"]);
|
||||
const schedule = async (thread) => {
|
||||
await this.activityService.schedule(thread.model, [thread.id]);
|
||||
this.load(thread, ["activities", "messages"]);
|
||||
};
|
||||
if (this.props.threadId) {
|
||||
schedule(this.props.threadId);
|
||||
if (this.state.thread.id) {
|
||||
schedule(this.state.thread);
|
||||
} else {
|
||||
this.onNextUpdate = (nextProps) => {
|
||||
if (nextProps.threadId) {
|
||||
schedule(nextProps.threadId);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
this.onThreadCreated = schedule;
|
||||
this.props.saveRecord?.();
|
||||
}
|
||||
}
|
||||
@@ -420,7 +393,7 @@ export class Chatter extends Component {
|
||||
}
|
||||
|
||||
async onClickAttachFile(ev) {
|
||||
if (this.props.threadId) {
|
||||
if (this.state.thread.id) {
|
||||
return;
|
||||
}
|
||||
const saved = await this.props.saveRecord?.();
|
||||
|
||||
@@ -65,7 +65,7 @@ export class FollowerList extends Component {
|
||||
async onClickEdit(ev, follower) {
|
||||
this.env.services.dialog.add(FollowerSubtypeDialog, {
|
||||
follower,
|
||||
onFollowerChanged: () => this.props.onFollowerChanged?.(),
|
||||
onFollowerChanged: () => this.props.onFollowerChanged?.(this.props.thread),
|
||||
});
|
||||
document.body.click(); // hack to close dropdown
|
||||
}
|
||||
@@ -75,7 +75,8 @@ export class FollowerList extends Component {
|
||||
* @param {import("models").Follower} follower
|
||||
*/
|
||||
async onClickRemove(ev, follower) {
|
||||
const thread = this.props.thread;
|
||||
await this.threadService.removeFollower(follower);
|
||||
this.props.onFollowerChanged?.();
|
||||
this.props.onFollowerChanged?.(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ export class FollowerSubtypeDialog extends Component {
|
||||
|
||||
async onClickApply() {
|
||||
const selectedSubtypes = this.state.subtypes.filter((s) => s.followed);
|
||||
const thread = this.props.follower.followedThread;
|
||||
if (selectedSubtypes.length === 0) {
|
||||
await this.env.services["mail.thread"].removeFollower(this.props.follower);
|
||||
} else {
|
||||
@@ -69,7 +70,7 @@ export class FollowerSubtypeDialog extends Component {
|
||||
{ type: "success" }
|
||||
);
|
||||
}
|
||||
this.props.onFollowerChanged();
|
||||
this.props.onFollowerChanged(thread);
|
||||
this.props.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,20 @@
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { FormController } from "@web/views/form/form_controller";
|
||||
import { x2ManyCommands } from "@web/core/orm_service";
|
||||
import { useSubEnv } from "@odoo/owl";
|
||||
|
||||
patch(FormController.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
useSubEnv({
|
||||
chatter: {
|
||||
fetchOnWillUpdateProps: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
onWillLoadRoot(nextConfiguration) {
|
||||
super.onWillLoadRoot(...arguments);
|
||||
this.env.chatter.fetchOnWillUpdateProps = true;
|
||||
const isSameThread =
|
||||
this.model.root?.resId === nextConfiguration.resId &&
|
||||
this.model.root?.resModel === nextConfiguration.resModel;
|
||||
|
||||
Reference in New Issue
Block a user