diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py
index 838d99efc98..c65d02a5f0f 100644
--- a/addons/mail/__manifest__.py
+++ b/addons/mail/__manifest__.py
@@ -77,6 +77,9 @@
'web/static/src/webclient/webclient.scss',
'web/static/src/webclient/webclient_extra.scss',
'web/static/src/core/utils/*.scss',
+ # depends on BS variables, can't be loaded in assets_primary or assets_secondary
+ 'mail/static/src/scss/variables/derived_variables.scss',
+ 'mail/static/src/scss/composer.scss',
# Dependency of notification_group, notification_request, thread_needaction_preview and thread_preview
'mail/static/src/components/notification_list/notification_list_item.scss',
'mail/static/src/component_hooks/*.js',
@@ -162,7 +165,6 @@
'web.assets_qweb': [
'mail/static/src/xml/*.xml',
'mail/static/src/components/*/*.xml',
- 'mail/static/src/widgets/*/*.xml',
],
},
'license': 'LGPL-3',
diff --git a/addons/mail/static/src/components/chatter/chatter.js b/addons/mail/static/src/components/chatter/chatter.js
index ab6dc10fc54..c2bf0669c81 100644
--- a/addons/mail/static/src/components/chatter/chatter.js
+++ b/addons/mail/static/src/components/chatter/chatter.js
@@ -2,7 +2,6 @@
import { useComponentToModel } from '@mail/component_hooks/use_component_to_model';
import { useRefToModel } from '@mail/component_hooks/use_ref_to_model';
-import { useUpdate } from '@mail/component_hooks/use_update';
import { registerMessagingComponent } from '@mail/utils/messaging_component';
import { LegacyComponent } from '@web/legacy/legacy_component';
@@ -13,15 +12,10 @@ export class Chatter extends LegacyComponent {
*/
setup() {
super.setup();
- useUpdate({ func: () => this._update() });
useComponentToModel({ fieldName: 'component' });
useRefToModel({ fieldName: 'scrollPanelRef', refName: 'scrollPanel' });
}
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
/**
* @returns {Chatter}
*/
@@ -29,26 +23,6 @@ export class Chatter extends LegacyComponent {
return this.props.record;
}
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- */
- _notifyRendered() {
- this.trigger('o-chatter-rendered');
- }
-
- /**
- * @private
- */
- _update() {
- if (this.chatter.thread) {
- this._notifyRendered();
- }
- }
-
}
Object.assign(Chatter, {
diff --git a/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.js b/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.js
new file mode 100644
index 00000000000..4603b44602d
--- /dev/null
+++ b/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.js
@@ -0,0 +1,32 @@
+/** @odoo-module **/
+
+import { useComponentToModel } from '@mail/component_hooks/use_component_to_model';
+import { useRefToModel } from '@mail/component_hooks/use_ref_to_model';
+import { useUpdateToModel } from '@mail/component_hooks/use_update_to_model';
+import { registerMessagingComponent } from '@mail/utils/messaging_component';
+
+import { LegacyComponent } from '@web/legacy/legacy_component';
+
+export class WebClientViewAttachmentView extends LegacyComponent {
+ /**
+ * @override
+ */
+ setup() {
+ useComponentToModel({ fieldName: 'component' });
+ useRefToModel({ fieldName: 'iframeViewerPdfRef', refName: 'iframeViewerPdf' });
+ useUpdateToModel({ methodName: 'onComponentUpdate' });
+ }
+ /**
+ * @returns {WebClientViewAttachmentView}
+ */
+ get webClientViewAttachmentView() {
+ return this.props.record;
+ }
+}
+
+Object.assign(WebClientViewAttachmentView, {
+ props: { record: Object },
+ template: 'mail.WebClientViewAttachmentView',
+});
+
+registerMessagingComponent(WebClientViewAttachmentView);
diff --git a/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.scss b/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.scss
similarity index 100%
rename from addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.scss
rename to addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.scss
diff --git a/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.xml b/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.xml
new file mode 100644
index 00000000000..ec45d724369
--- /dev/null
+++ b/addons/mail/static/src/components/web_client_view_attachment_view/web_client_view_attachment_view.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/mail/static/src/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container.js b/addons/mail/static/src/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container.js
new file mode 100644
index 00000000000..afdabf2e61e
--- /dev/null
+++ b/addons/mail/static/src/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container.js
@@ -0,0 +1,91 @@
+/** @odoo-module **/
+
+import { useModels } from '@mail/component_hooks/use_models';
+// ensure components are registered beforehand.
+import '@mail/components/web_client_view_attachment_view/web_client_view_attachment_view';
+import { replace } from '@mail/model/model_field_command';
+import { getMessagingComponent } from '@mail/utils/messaging_component';
+
+const { Component, onWillDestroy, onWillUpdateProps } = owl;
+
+const getNextId = (function () {
+ let tmpId = 0;
+ return () => {
+ tmpId += 1;
+ return tmpId;
+ };
+})();
+
+/**
+ * This component abstracts attachment view component to its parent, so that it
+ * can be mounted and receive messaging data even when an attachment view
+ * component cannot be created. Indeed, in order to create an attachment view
+ * component, we must create an attachment view record, the latter requiring
+ * messaging to be initialized. The view may attempt to create an attachment
+ * view before messaging has been initialized, so this component delays the
+ * mounting of attachment view until it becomes initialized.
+ */
+export class WebClientViewAttachmentViewContainer extends Component {
+
+ /**
+ * @override
+ */
+ setup() {
+ useModels();
+ super.setup();
+ this.webClientViewAttachmentView = undefined;
+ this.webClientViewAttachmentViewId = getNextId();
+ this._insertFromProps(this.props);
+ onWillUpdateProps(nextProps => this._insertFromProps(nextProps));
+ onWillDestroy(() => this._deleteRecord());
+ }
+
+ /**
+ * @private
+ */
+ _deleteRecord() {
+ if (this.webClientViewAttachmentView) {
+ if (this.webClientViewAttachmentView.exists()) {
+ this.webClientViewAttachmentView.delete();
+ }
+ this.webClientViewAttachmentView = undefined;
+ }
+ }
+
+ /**
+ * @private
+ */
+ async _insertFromProps(props) {
+ const messaging = await this.env.services.messaging.get();
+ if (this.__owl__.status === 5 /* destroyed */) {
+ this._deleteRecord();
+ return;
+ }
+ if (!props.threadId) {
+ this._deleteRecord();
+ return;
+ }
+ const thread = messaging.models['Thread'].insert({
+ id: props.threadId,
+ model: props.threadModel,
+ });
+ this.webClientViewAttachmentView = messaging.models['WebClientViewAttachmentView'].insert({
+ id: this.webClientViewAttachmentViewId,
+ thread: replace(thread),
+ });
+ this.render();
+ }
+
+}
+
+Object.assign(WebClientViewAttachmentViewContainer, {
+ components: { WebClientViewAttachmentView: getMessagingComponent('WebClientViewAttachmentView') },
+ props: {
+ threadId: {
+ type: Number,
+ optional: true,
+ },
+ threadModel: String,
+ },
+ template: 'mail.WebClientViewAttachmentViewContainer',
+});
diff --git a/addons/mail/static/src/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container.xml b/addons/mail/static/src/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container.xml
new file mode 100644
index 00000000000..3efb6449260
--- /dev/null
+++ b/addons/mail/static/src/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/mail/static/src/models/thread.js b/addons/mail/static/src/models/thread.js
index ccbd04627f5..b2bc43ee74d 100644
--- a/addons/mail/static/src/models/thread.js
+++ b/addons/mail/static/src/models/thread.js
@@ -579,6 +579,7 @@ registerModel({
});
}
this.update(values);
+ this.messaging.messagingBus.trigger('o-thread-loaded-data', { thread: this });
},
/**
* Add current user to provided thread's followers.
@@ -949,6 +950,17 @@ registerModel({
kwargs: { name: newName },
});
},
+ /**
+ * @param {Attachment} attachment
+ */
+ async setMainAttachment(attachment) {
+ this.update({ mainAttachment: replace(attachment) });
+ await this.messaging.rpc({
+ model: 'ir.attachment',
+ method: 'register_as_main_attachment',
+ args: [[this.mainAttachment.id]],
+ });
+ },
/**
* Unfollow current partner from this thread.
*/
diff --git a/addons/mail/static/src/models/web_client_view_attachment_view.js b/addons/mail/static/src/models/web_client_view_attachment_view.js
new file mode 100644
index 00000000000..605b3e7a9d1
--- /dev/null
+++ b/addons/mail/static/src/models/web_client_view_attachment_view.js
@@ -0,0 +1,64 @@
+/** @odoo-module **/
+
+import { registerModel } from '@mail/model/model_core';
+import { attr, one } from '@mail/model/model_field';
+import { OnChange } from '@mail/model/model_onchange';
+
+import { hidePDFJSButtons } from '@web/legacy/js/libs/pdfjs';
+
+registerModel({
+ name: 'WebClientViewAttachmentView',
+ identifyingFields: ['id'],
+ recordMethods: {
+ /**
+ * @param {MouseEvent} ev
+ */
+ onClickNext(ev) {
+ ev.preventDefault();
+ const index = this.thread.attachmentsInWebClientView.findIndex(attachment => attachment === this.thread.mainAttachment);
+ this.setMainAttachmentFromIndex(index === this.thread.attachmentsInWebClientView.length - 1 ? 0 : index + 1);
+ },
+ /**
+ * @param {MouseEvent} ev
+ */
+ onClickPrevious(ev) {
+ ev.preventDefault();
+ const index = this.thread.attachmentsInWebClientView.findIndex(attachment => attachment === this.thread.mainAttachment);
+ this.setMainAttachmentFromIndex(index === 0 ? this.thread.attachmentsInWebClientView.length - 1 : index - 1);
+ },
+ onComponentUpdate() {
+ if (this.iframeViewerPdfRef.el) {
+ hidePDFJSButtons(this.iframeViewerPdfRef.el);
+ }
+ this.component.trigger('preview_attachment_validation');
+ },
+ async setMainAttachmentFromIndex(index) {
+ await this.thread.setMainAttachment(this.thread.attachmentsInWebClientView[index]);
+ },
+ /**
+ * @private
+ */
+ _onChangeThreadAttachmentsInWebClientView() {
+ if (!this.thread.mainAttachment && this.thread.attachmentsInWebClientView.length > 0) {
+ this.setMainAttachmentFromIndex(0);
+ }
+ },
+ },
+ fields: {
+ component: attr(),
+ id: attr({
+ readonly: true,
+ required: true,
+ }),
+ iframeViewerPdfRef: attr(),
+ thread: one('Thread', {
+ required: true,
+ }),
+ },
+ onChanges: [
+ new OnChange({
+ dependencies: ['thread.attachmentsInWebClientView'],
+ methodName: '_onChangeThreadAttachmentsInWebClientView',
+ }),
+ ],
+});
diff --git a/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.js b/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.js
deleted file mode 100644
index 9f8ae7617be..00000000000
--- a/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/** @odoo-module **/
-
-import { replace } from '@mail/model/model_field_command';
-
-import core from 'web.core';
-import Widget from 'web.Widget';
-import { hidePDFJSButtons } from '@web/legacy/js/libs/pdfjs';
-
-const QWeb = core.qweb;
-
-export const AttachmentViewer = Widget.extend({
- className: 'o_attachment_preview_container',
- events: {
- 'click .arrow.o_move_next': '_onClickNext',
- 'click .arrow.o_move_previous': '_onClickPrevious',
- },
- /**
- * @constructor
- * @override
- * @param {Widget} parent
- * @param {Thread} thread
- */
- init(parent, thread) {
- this._super.apply(this, arguments);
- this.thread = thread;
- this.attachments = this.thread.attachmentsInWebClientView;
- this._setActive();
- },
- /**
- * Render attachment.
- *
- * @override
- */
- start() {
- this._renderAttachment();
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Update attachments list and activeAttachment.
-
- * @param {Thread} thread
- */
- updateContents(thread) {
- this.thread = thread;
- this.attachments = this.thread.attachmentsInWebClientView;
- this._setActive();
- this._renderAttachment();
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Render template
- *
- * @private
- */
- _renderAttachment() {
- this.$el.empty();
- this.$el.append(QWeb.render('mail.widgets.AttachmentViewer', { thread: this.thread }));
- if (this.thread.mainAttachment && this.thread.mainAttachment.isPdf) {
- hidePDFJSButtons(this.el);
- }
- },
-
- /**
- * @private
- */
- _setActive() {
- if (!this.thread.mainAttachment && this.thread.attachmentsInWebClientView.length > 0) {
- this._switch_main_attachment(0);
- }
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Sets the attachment at position index as the new main attachment of
- * the related model, and display it.
- **/
- async _switch_main_attachment(index) {
- this.thread.update({ mainAttachment: replace(this.thread.attachmentsInWebClientView[index]) });
- await this._rpc({
- model: 'ir.attachment',
- method: 'register_as_main_attachment',
- args: [[this.thread.mainAttachment['id']]],
- });
- this._renderAttachment();
- },
-
- /**
- * On click move to next attachment.
- *
- * @private
- * @param {MouseEvent} ev
- */
- _onClickNext(ev) {
- ev.preventDefault();
- var index = _.findIndex(this.thread.attachmentsInWebClientView, this.thread.mainAttachment);
- index = index === this.thread.attachmentsInWebClientView.length - 1 ? 0 : index + 1;
- this._switch_main_attachment(index);
- },
- /**
- * On click move to previous attachment.
- *
- * @private
- * @param {MouseEvent} ev
- */
- _onClickPrevious(ev) {
- ev.preventDefault();
- var index = _.findIndex(this.thread.attachmentsInWebClientView, this.thread.mainAttachment);
- index = index === 0 ? this.thread.attachmentsInWebClientView.length - 1 : index - 1;
- this._switch_main_attachment(index);
- },
-});
diff --git a/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.xml b/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.xml
deleted file mode 100644
index fd017ca00a8..00000000000
--- a/addons/mail/static/src/widgets/attachment_viewer/attachment_viewer.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
![]()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/mail/static/src/widgets/form_renderer/form_renderer.js b/addons/mail/static/src/widgets/form_renderer/form_renderer.js
index 2c4929eee9c..9bbf6ac9057 100644
--- a/addons/mail/static/src/widgets/form_renderer/form_renderer.js
+++ b/addons/mail/static/src/widgets/form_renderer/form_renderer.js
@@ -1,7 +1,8 @@
/** @odoo-module **/
import { ChatterContainer } from '@mail/components/chatter_container/chatter_container';
-import { AttachmentViewer } from '@mail/widgets/attachment_viewer/attachment_viewer';
+import { WebClientViewAttachmentViewContainer } from '@mail/components/web_client_view_attachment_view_container/web_client_view_attachment_view_container';
+import { Listener } from '@mail/model/model_listener';
import dom from 'web.dom';
import FormRenderer from 'web.FormRenderer';
@@ -23,6 +24,10 @@ FormRenderer.include({
*/
init(parent, state, params) {
this._super(...arguments);
+ this.modelsListener = new Listener({
+ name: 'interchangeChatter',
+ onChange: () => this._interchangeChatter(),
+ });
this.hasChatter = params.hasChatter && !params.isFromFormViewDialog;
this.isChatterInSheet = false;
this.hasAttachmentViewerFeature = params.hasAttachmentViewerFeature;
@@ -49,14 +54,15 @@ FormRenderer.include({
* when applying the rendering into the DOM.
*/
this.chatterContainerTargetPlaceholder = undefined;
- this.attachmentViewer = undefined;
+ this.webClientViewAttachmentViewContainer = undefined;
this.attachmentViewerTarget = undefined;
if (this.hasAttachmentViewerFeature) {
this.attachmentViewerTarget = document.createElement("div");
this.attachmentViewerTarget.classList.add("o_attachment_preview");
+ this.webClientViewAttachmentViewContainer = new ComponentWrapper(this, WebClientViewAttachmentViewContainer, this._makeWebClientViewAttachmentViewContainerProps());
+ this.webClientViewAttachmentViewContainer.mount(this.attachmentViewerTarget);
}
this.attachmentViewerTargetPlaceholder = undefined;
- this.on('o_chatter_rendered', this, ev => this._onChatterRendered(ev));
},
async initChatter() {
this._chatterContainerComponent = new ChatterContainerWrapperComponent(
@@ -99,6 +105,9 @@ FormRenderer.include({
if (this.hasChatter) {
await this._updateChatterContainerComponent();
}
+ if (this.hasAttachmentViewer()) {
+ await this._updateWebClientViewAttachmentViewContainer();
+ }
},
/**
* The last rendering of the form view has just been applied into the DOM,
@@ -135,8 +144,10 @@ FormRenderer.include({
*/
destroy() {
this._super(...arguments);
+ if (owl.Component.env.services.messaging) {
+ owl.Component.env.services.messaging.modelManager.removeListener(this.modelsListener);
+ }
this._chatterContainerComponent = undefined;
- this.off('o_chatter_rendered', this);
},
//--------------------------------------------------------------------------
@@ -176,10 +187,13 @@ FormRenderer.include({
}
$(this._chatterContainerTarget).insertAfter($sheetBg);
}
- if (this.hasAttachmentViewer()) {
- $(this.attachmentViewerTarget).insertAfter($sheetBg);
- } else {
- dom.append($sheetBg, $(this.attachmentViewerTarget));
+ if (this.hasAttachmentViewerFeature) {
+ if (this.hasAttachmentViewer()) {
+ $(this.attachmentViewerTarget).insertAfter($sheetBg);
+ this._updateWebClientViewAttachmentViewContainer();
+ } else {
+ this.attachmentViewerTarget.remove();
+ }
}
this._updateChatterContainerComponent();
},
@@ -211,55 +225,24 @@ FormRenderer.include({
threadModel: this.state.model,
};
},
+ _makeWebClientViewAttachmentViewContainerProps() {
+ return {
+ threadId: this.state.res_id,
+ threadModel: this.state.model,
+ };
+ },
/**
* @private
*/
async _updateChatterContainerComponent() {
const props = this._makeChatterContainerProps();
- try {
- await this._chatterContainerComponent.update(props);
- } catch (error) {
- if (error.message !== "Mounting operation cancelled") {
- throw error;
- }
- }
+ await this._chatterContainerComponent.update(props);
},
/**
* @private
- * @param {OdooEvent} ev
- * @param {Object} ev.data
- * @param {Attachment[]} ev.data.attachments
- * @param {Thread} ev.data.thread
*/
- _onChatterRendered(ev) {
- if (!this.hasAttachmentViewer()) {
- if (this.attachmentViewer) {
- this.attachmentViewer.destroy();
- this.attachmentViewer = undefined;
- this._interchangeChatter();
- }
- return;
- }
- var self = this;
- const thread = this.messaging.models['Thread'].insert({
- id: this.state.res_id,
- model: this.state.model,
- });
- if (this.attachmentViewer) {
- // FIXME should be improved : what if somehow an attachment is replaced in a thread ?
- if (
- this.attachmentViewer.thread !== thread ||
- this.attachmentViewer.attachments.length !== thread.attachmentsInWebClientView.length
- ) {
- this.attachmentViewer.updateContents(thread);
- }
- this.trigger_up('preview_attachment_validation');
- } else {
- this.attachmentViewer = new AttachmentViewer(this, thread);
- this.attachmentViewer.appendTo($(this.attachmentViewerTarget)).then(function () {
- self.trigger_up('preview_attachment_validation');
- self._interchangeChatter();
- });
- }
+ async _updateWebClientViewAttachmentViewContainer() {
+ const props = this._makeWebClientViewAttachmentViewContainerProps();
+ await this.webClientViewAttachmentViewContainer.update(props);
},
});
diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js
index 17e4c374b74..2b78f9c1235 100644
--- a/addons/mail/static/tests/helpers/mock_server.js
+++ b/addons/mail/static/tests/helpers/mock_server.js
@@ -125,6 +125,10 @@ patch(MockServer.prototype, 'mail', {
if (new RegExp('/mail/channel/\\d+/partner/\\d+/avatar_128').test(route)) {
return;
}
+ if (args.model === 'ir.attachment' && args.method === 'register_as_main_attachment') {
+ const ids = args.args[0];
+ return this._mockIrAttachmentRegisterAsMainAttachment(ids);
+ }
// mail.activity methods
if (args.model === 'mail.activity' && args.method === 'action_feedback') {
const ids = args.args[0];
@@ -528,7 +532,7 @@ patch(MockServer.prototype, 'mail', {
[['res_id', '=', thread.id], ['res_model', '=', thread_model]],
); // order not done for simplicity
res['attachments'] = this._mockIrAttachment_attachmentFormat(attachments.map(attachment => attachment.id));
- res['mainAttachment'] = thread.message_main_attachment_id ? [['insert-and-replace', { 'id': thread.message_main_attachment_id }]] : [['clear']];
+ res['mainAttachment'] = thread.message_main_attachment_id ? [['insert-and-replace', { 'id': thread.message_main_attachment_id[0] }]] : [['clear']];
}
if (request_list.includes('followers')) {
const followers = this.pyEnv['mail.followers'].searchRead([['id', 'in', thread.message_follower_ids || []]]);
@@ -613,6 +617,31 @@ patch(MockServer.prototype, 'mail', {
return res;
});
},
+ /**
+ * Simulates `register_as_main_attachment` on `ir.attachment`.
+ *
+ * @private
+ * @param {integer} ids
+ * @param {boolean} [force=true]
+ * @returns {boolean} dummy value for mock server
+ */
+ _mockIrAttachmentRegisterAsMainAttachment(ids, force = true) {
+ const [attachment] = this.getRecords('ir.attachment', [['id', 'in', ids]]);
+ if (!attachment.res_model) {
+ return true; // dummy value for mock server
+ }
+ if (!this.models[attachment.res_model].fields['message_main_attachment_id']) {
+ return true; // dummy value for mock server
+ }
+ const [record] = this.pyEnv[attachment.res_model].searchRead([['id', '=', attachment.res_id]]);
+ if (force || !record.message_main_attachment_id) {
+ this.pyEnv[attachment.res_model].write(
+ [record.id],
+ { message_main_attachment_id: attachment.id },
+ );
+ }
+ return true; // dummy value for mock server
+ },
/**
* Simulates `_action_done` on `mail.activity`.
*
diff --git a/addons/mail/static/tests/helpers/test_utils.js b/addons/mail/static/tests/helpers/test_utils.js
index 45dd7c30b1a..435108523cf 100644
--- a/addons/mail/static/tests/helpers/test_utils.js
+++ b/addons/mail/static/tests/helpers/test_utils.js
@@ -446,25 +446,44 @@ function getOpenDiscuss(afterEvent, webClient, { context = {}, params, ...props
}
function getOpenFormView(afterEvent, openView) {
- return async function openFormView(action, { props, waitUntilMessagesLoaded = true } = {}) {
+ return async function openFormView(action, { props, waitUntilDataLoaded = true, waitUntilMessagesLoaded = true } = {}) {
action['views'] = [[false, 'form']];
- if (waitUntilMessagesLoaded) {
- return afterNextRender(() => afterEvent({
- eventName: 'o-thread-view-hint-processed',
- func: () => openView(action, props),
- message: "should wait until chatter loaded its messages",
- predicate: ({ hint, threadViewer }) => {
- return (
- hint.type === 'messages-loaded' &&
- threadViewer &&
- threadViewer.thread.model === action.res_model &&
- threadViewer.thread.id === action.res_id
- )
- },
- }));
+ const func = () => openView(action, props);
+ const waitData = func => afterNextRender(() => afterEvent({
+ eventName: 'o-thread-loaded-data',
+ func,
+ message: "should wait until chatter loaded its data",
+ predicate: ({ thread }) => {
+ return (
+ thread.model === action.res_model &&
+ thread.id === action.res_id
+ );
+ },
+ }));
+ const waitMessages = func => afterNextRender(() => afterEvent({
+ eventName: 'o-thread-view-hint-processed',
+ func,
+ message: "should wait until chatter loaded its messages",
+ predicate: ({ hint, threadViewer }) => {
+ return (
+ hint.type === 'messages-loaded' &&
+ threadViewer &&
+ threadViewer.thread.model === action.res_model &&
+ threadViewer.thread.id === action.res_id
+ );
+ },
+ }));
+ if (waitUntilDataLoaded && waitUntilMessagesLoaded) {
+ return waitData(() => waitMessages(func));
}
- return openView(action, props);
- }
+ if (waitUntilDataLoaded) {
+ return waitData(func);
+ }
+ if (waitUntilMessagesLoaded) {
+ return waitMessages(func);
+ }
+ return func();
+ };
}
//------------------------------------------------------------------------------