From 11cc762d7ab9ecff2055bcaaee83bee6a685f657 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 11 Feb 2022 12:10:43 +0000 Subject: [PATCH] [FIX] mail,project: remove chatter flickering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, when a form view was re-rendered (e.g. when switching to "edit" or "readonly" modes, when using the pager...), the chatter briefly disappeared and then reappeared, which produced a flickering. This has been introduced by the adaptation of the JS codebase to owl 2 [1]: when the form view had to be re-rendered, we reused the DOM element containing the chatter in the new rendering (done in memory), which was thus detached from the DOM. That element was re-inserted into the DOM later on, alongside the rest of the newly rendered elements. This commit fixes the issue by using a specific "hook" element when rendering the form view, and replacing that hook by the actual (unchanged) chatter container element when the rendering is done and the DOM is patched. This commit also removes unnecessary overrides in project: - _renderNode was dead code, classname "oe_project_sharing_chatter" no longer exists in the codebase since [2] - _makeChatterContainerTarget replicated what was originally already done in the mail FormRenderer (for appearently no valid reason), so obviously, that override wasn't synchronized with this fix. [1] edd79ff560cf321ebd0488e4917abb7424fea1bb [2] a28cc6333af3a1799fd5f5437a8f88de1b73b70f closes odoo/odoo#84429 Signed-off-by: Sébastien Theys (seb) --- .../widgets/form_renderer/form_renderer.js | 28 ++++++-- .../tests/form_renderer_tests.js | 66 ++++++++++++++++++- .../project_sharing/views/form/renderer.js | 23 ------- 3 files changed, 88 insertions(+), 29 deletions(-) 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 86fef09c235..89868f8ee16 100644 --- a/addons/mail/static/src/widgets/form_renderer/form_renderer.js +++ b/addons/mail/static/src/widgets/form_renderer/form_renderer.js @@ -27,6 +27,12 @@ FormRenderer.include({ * This is set when arch contains `div.oe_chatter`. */ this._chatterContainerTarget = undefined; + /** + * This jQuery element will be set when rendering the form view, and + * used as a hook to insert the ChatterContainer in the right place, + * when applying the rendering into the DOM. + */ + this.$chatterContainerHook = undefined; // Do not load chatter in form view dialogs this._isFromFormViewDialog = params.isFromFormViewDialog; }, @@ -105,12 +111,12 @@ FormRenderer.include({ * @returns {jQuery.Element} */ _makeChatterContainerTarget() { - if (this._chatterContainerTarget) { - return $(this._chatterContainerTarget); + if (!this._chatterContainerTarget) { + this._chatterContainerTarget = document.createElement("div"); + this._chatterContainerTarget.classList.add("o_FormRenderer_chatterContainer"); } - const $el = $('
'); - this._chatterContainerTarget = $el[0]; - return $el; + this.$chatterContainerHook = $('
'); + return this.$chatterContainerHook; }, /** * Mount the chatter @@ -142,6 +148,18 @@ FormRenderer.include({ } return this._super(...arguments); }, + /** + * The last rendering of the form view has just been applied into the DOM, + * so we replace our chatter container hook by the actual chatter container. + * + * @override + */ + _updateView() { + this._super(...arguments); + if (this._hasChatter()) { + this.$chatterContainerHook.replaceWith(this._chatterContainerTarget); + } + }, /** * Overrides the function to render the chatter once the form view is * rendered. diff --git a/addons/mail/static/src/widgets/form_renderer/tests/form_renderer_tests.js b/addons/mail/static/src/widgets/form_renderer/tests/form_renderer_tests.js index de5e5d94d25..69a9ca1b570 100644 --- a/addons/mail/static/src/widgets/form_renderer/tests/form_renderer_tests.js +++ b/addons/mail/static/src/widgets/form_renderer/tests/form_renderer_tests.js @@ -10,8 +10,10 @@ import { } from '@mail/utils/test_utils'; import config from 'web.config'; +import fieldRegistry from 'web.field_registry'; import FormView from 'web.FormView'; -import { dom } from 'web.test_utils'; +import { dom, nextTick } from 'web.test_utils'; +import { registerCleanup } from "@web/../tests/helpers/cleanup"; const { triggerEvent } = dom; @@ -1048,6 +1050,68 @@ QUnit.test('[TECHNICAL] unfolded read more/less links should not fold on message ); }); +QUnit.test('chatter does not flicker when the form view is re-rendered', async function (assert) { + this.data['res.partner'].records.push( + { display_name: "first partner", id: 11 }, + { display_name: "second partner", id: 12 } + ); + + // define an asynchronous field and use it in the form to ease testing + let def; + const FieldChar = fieldRegistry.get("char"); + const AsyncWidget = FieldChar.extend({ + willStart() { + return Promise.resolve(def); + }, + }); + fieldRegistry.add("async_widget", AsyncWidget); + registerCleanup(() => { + delete fieldRegistry.map.async_widget; + }); + + await this.createView({ + data: this.data, + hasView: true, + // View params + View: FormView, + model: 'res.partner', + arch: ` +
+ + + +
+
+ `, + viewOptions: { + currentId: 11, + ids: [11, 12], + }, + }); + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "there should be a chatter" + ); + def = makeDeferred(); + document.querySelector('.o_pager_next').click(); + await nextTick(); + + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "there should be a chatter" + ); + def.resolve(); + await nextTick(); + + assert.strictEqual( + document.querySelectorAll(`.o_Chatter`).length, + 1, + "there should be a chatter" + ); +}); + }); }); }); diff --git a/addons/project/static/src/project_sharing/views/form/renderer.js b/addons/project/static/src/project_sharing/views/form/renderer.js index f4acf457a66..614bb776f3a 100644 --- a/addons/project/static/src/project_sharing/views/form/renderer.js +++ b/addons/project/static/src/project_sharing/views/form/renderer.js @@ -25,30 +25,7 @@ export default FormRenderer.extend({ project_sharing_id: session.project_id, }; }, - _makeChatterContainerTarget() { - const $el = $('
'); - this._chatterContainerTarget = $el[0]; - return $el; - }, _mountChatterContainerComponent() { this._chatterContainerComponent.appendTo(this._chatterContainerTarget); }, - _renderNode(node) { - if (node.tag === 'div' && node.attrs.class === 'oe_project_sharing_chatter') { - let isVisible = true; - if (node.attrs.modifiers && node.attrs.modifiers.invisible) { - const record = this._getRecord(this.state.id); - if (record) { - isVisible = !record.evalModifiers(node.attrs.modifiers).invisible; - } - } - if (isVisible) { - if (this._isFromFormViewDialog) { - return $('
'); - } - return this._makeChatterContainerTarget(); - } - } - return this._super(...arguments); - }, });