[FIX] mail: avoid losing chatter record when changing container
Form view has multiple containers that are exclusive, before this commit switching from one container to the other would lose all current chatter state. closes odoo/odoo#102934 X-original-commit: a3d151d0c9c84c2dcffc28a342c40f38d0b28370 Related: odoo/enterprise#32644 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com> Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
</t>
|
||||
<div class="o_Chatter_fixedPanel">
|
||||
<ChatterTopbar
|
||||
t-if="chatter.topbar"
|
||||
className="'o_Chatter_topbar'"
|
||||
record="chatter.topbar"
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getMessagingComponent } from "@mail/utils/messaging_component";
|
||||
|
||||
const { Component, onWillDestroy, onWillUpdateProps } = owl;
|
||||
|
||||
const getChatterNextTemporaryId = (function () {
|
||||
export const getChatterNextTemporaryId = (function () {
|
||||
let tmpId = 0;
|
||||
return () => {
|
||||
tmpId += 1;
|
||||
@@ -32,23 +32,21 @@ export class ChatterContainer extends Component {
|
||||
setup() {
|
||||
useModels();
|
||||
super.setup();
|
||||
this.chatter = undefined;
|
||||
this.chatterId = getChatterNextTemporaryId();
|
||||
this.localChatter = undefined;
|
||||
this._insertFromProps(this.props);
|
||||
onWillUpdateProps(nextProps => this._willUpdateProps(nextProps));
|
||||
onWillDestroy(() => this._onWillDestroy());
|
||||
onWillUpdateProps(nextProps => {
|
||||
this._insertFromProps(nextProps);
|
||||
});
|
||||
onWillDestroy(() => this.deleteLocalChatter());
|
||||
}
|
||||
|
||||
_willUpdateProps(nextProps) {
|
||||
this._insertFromProps(nextProps);
|
||||
get chatter() {
|
||||
return this.props.chatter || this.localChatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_onWillDestroy() {
|
||||
if (this.chatter && this.chatter.exists()) {
|
||||
this.chatter.delete();
|
||||
deleteLocalChatter() {
|
||||
if (this.localChatter && this.localChatter.exists()) {
|
||||
this.localChatter.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,15 +62,23 @@ export class ChatterContainer extends Component {
|
||||
if (owl.status(this) === "destroyed") {
|
||||
return;
|
||||
}
|
||||
const values = { id: this.chatterId, ...props };
|
||||
const values = { ...props };
|
||||
delete values.chatter;
|
||||
delete values.className;
|
||||
if (values.threadId === undefined) {
|
||||
values.threadId = clear();
|
||||
}
|
||||
this.chatter = messaging.models['Chatter'].insert(values);
|
||||
const hasToCreateChatter = !props.chatter && !this.localChatter;
|
||||
if (hasToCreateChatter) {
|
||||
this.localChatter = messaging.models['Chatter'].insert({ id: getChatterNextTemporaryId(), ...values });
|
||||
}
|
||||
const chatter = props.chatter || this.localChatter;
|
||||
if (!hasToCreateChatter) {
|
||||
chatter.update(values);
|
||||
}
|
||||
if (owl.status(this) === "destroyed") {
|
||||
// insert might trigger a re-render which might destroy the current component
|
||||
this.chatter.delete();
|
||||
this.deleteLocalChatter();
|
||||
return;
|
||||
}
|
||||
/**
|
||||
@@ -90,7 +96,9 @@ export class ChatterContainer extends Component {
|
||||
* calling the props change method but it is in general not a good
|
||||
* assumption to make.
|
||||
*/
|
||||
this.chatter.refresh();
|
||||
if (chatter.thread) {
|
||||
chatter.refresh();
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
@@ -99,6 +107,10 @@ export class ChatterContainer extends Component {
|
||||
Object.assign(ChatterContainer, {
|
||||
components: { Chatter: getMessagingComponent('Chatter') },
|
||||
props: {
|
||||
chatter: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
optional: true,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="mail.ChatterContainer" owl="1">
|
||||
<div class="o_ChatterContainer flex-grow-1 d-flex" t-att-class="{ 'o-isInFormSheetBg mx-auto': props.isInFormSheetBg }" t-attf-class="{{ props.className }}" data-command-category="mail" t-ref="root">
|
||||
<t t-if="chatter">
|
||||
<t t-if="chatter and chatter.thread">
|
||||
<Chatter record="chatter"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
|
||||
@@ -172,6 +172,9 @@ registerModel({
|
||||
* @private
|
||||
*/
|
||||
_onThreadIdOrThreadModelChanged() {
|
||||
if (!this.threadModel) {
|
||||
return;
|
||||
}
|
||||
if (this.threadId) {
|
||||
if (this.thread && this.thread.isTemporary) {
|
||||
this.thread.delete();
|
||||
@@ -261,6 +264,9 @@ registerModel({
|
||||
}),
|
||||
dropZoneView: one('DropZoneView', {
|
||||
compute() {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
if (this.useDragVisibleDropZone.isVisible) {
|
||||
return {};
|
||||
}
|
||||
@@ -403,6 +409,9 @@ registerModel({
|
||||
*/
|
||||
threadViewer: one('ThreadViewer', {
|
||||
compute() {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
return {
|
||||
hasThreadView: this.hasThreadView,
|
||||
order: 'desc',
|
||||
@@ -410,10 +419,11 @@ registerModel({
|
||||
};
|
||||
},
|
||||
inverse: 'chatter',
|
||||
required: true,
|
||||
}),
|
||||
topbar: one('ChatterTopbar', {
|
||||
default: {},
|
||||
compute() {
|
||||
return this.thread ? {} : clear();
|
||||
},
|
||||
inverse: 'chatter',
|
||||
}),
|
||||
useDragVisibleDropZone: one('UseDragVisibleDropZone', {
|
||||
|
||||
@@ -54,7 +54,7 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
// since dropdown is conditionally shown based on state, dropdownRef can be null
|
||||
if (this.dropdownRef.el && !this.dropdownRef.el.contains(ev.target)) {
|
||||
if (this.dropdownRef && this.dropdownRef.el && !this.dropdownRef.el.contains(ev.target)) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -37,6 +37,7 @@ function compileChatter(node, params) {
|
||||
}
|
||||
const chatterContainerXml = createElement("ChatterContainer");
|
||||
setAttributes(chatterContainerXml, {
|
||||
"chatter": params.chatter,
|
||||
"hasActivities": hasActivities,
|
||||
"hasFollowers": hasFollowers,
|
||||
"hasMessageList": hasMessageList,
|
||||
@@ -116,6 +117,7 @@ export class MailFormCompiler extends ViewCompiler {
|
||||
|
||||
compileChatter(node) {
|
||||
return compileChatter(node, {
|
||||
chatter: "chatter",
|
||||
threadId: "model.root.resId or undefined",
|
||||
threadModel: "model.root.resModel",
|
||||
webRecord: "model.root",
|
||||
@@ -134,6 +136,7 @@ registry.category("form_compilers").add("chatter_compiler", {
|
||||
selector: "div.oe_chatter",
|
||||
fn: (node) =>
|
||||
compileChatter(node, {
|
||||
chatter: "props.chatter",
|
||||
threadId: "props.record.resId or undefined",
|
||||
threadModel: "props.record.resModel",
|
||||
webRecord: "props.record",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { useModels } from "@mail/component_hooks/use_models";
|
||||
import { ChatterContainer } from "@mail/components/chatter_container/chatter_container";
|
||||
import { ChatterContainer, getChatterNextTemporaryId } from "@mail/components/chatter_container/chatter_container";
|
||||
import { WebClientViewAttachmentViewContainer } from "@mail/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container";
|
||||
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
@@ -15,16 +15,27 @@ import { evalDomain } from "@web/views/utils";
|
||||
|
||||
import { MailFormCompiler } from "./form_compiler";
|
||||
|
||||
const { onMounted, onWillUnmount } = owl;
|
||||
const { onMounted, onWillDestroy, onWillUnmount } = owl;
|
||||
|
||||
patch(FormController.prototype, "mail", {
|
||||
setup() {
|
||||
this._super();
|
||||
this.uiService = useService("ui");
|
||||
this.hasAttachmentViewerInArch = false;
|
||||
this.chatter = undefined;
|
||||
|
||||
if (this.env.services.messaging) {
|
||||
useModels();
|
||||
this.env.services.messaging.modelManager.messagingCreatedPromise.then(() => {
|
||||
if (owl.status(this) === "destroyed") {
|
||||
return;
|
||||
}
|
||||
const messaging = this.env.services.messaging.modelManager.messaging;
|
||||
this.chatter = messaging.models['Chatter'].insert({ id: getChatterNextTemporaryId() });
|
||||
if (owl.status(this) === "destroyed") {
|
||||
this.chatter.delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { archInfo } = this.props;
|
||||
@@ -49,6 +60,11 @@ patch(FormController.prototype, "mail", {
|
||||
this.onResize = useDebounced(this.render, 200);
|
||||
onMounted(() => browser.addEventListener("resize", this.onResize));
|
||||
onWillUnmount(() => browser.removeEventListener("resize", this.onResize));
|
||||
onWillDestroy(() => {
|
||||
if (this.chatter && this.chatter.exists()) {
|
||||
this.chatter.delete();
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @returns {Messaging|undefined}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//Layout/t[@t-component='props.Renderer']" position="attributes">
|
||||
<attribute name="chatter">chatter</attribute>
|
||||
<attribute name="hasAttachmentViewerInArch">hasAttachmentViewerInArch</attribute>
|
||||
<attribute name="hasAttachmentViewer">hasAttachmentViewer()</attribute>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user