diff options
| author | Sébastien Theys <seb@odoo.com> | 2020-06-15 18:12:21 +0000 |
|---|---|---|
| committer | Sébastien Theys <seb@odoo.com> | 2020-06-15 18:12:21 +0000 |
| commit | 3fea5b2136627c1d7e83b2a5e111952fe3eb10a1 (patch) | |
| tree | 5eefad07972a87ce4689b83260f981bc87aadad4 /addons/web | |
| parent | 0a82848606151ef60eb6523e9c4c161171f214e7 (diff) | |
[REF] mail, *: refactor messaging with OWL
This commit is a significant rewriting of client-side discuss, chatter,
chat window, and messaging menu using OWL. The behavior should be broadly
the same, with some slight functional changes here and there.
From a technical standpoint, the code of messaging is mainly organized in 2
main groups of modules:
- models, which are logical entities that depict the client-side state of
messaging as a whole.
- components, which are in charge of displaying information from models.
This refactoring also introduces new JS guidelines regarding folder structure
(/static) and naming rules for JS modules.
Community PR: https://github.com/odoo/odoo/pull/39023
Enterprise PR: https://github.com/odoo/enterprise/pull/6249
Task-1914207
This PR is a collaborative work by Alexandre, Julien, Sébastien and Xavier,
with the precious help of Lucas to speed it up towards the end.
closes odoo/odoo#39023
Related: odoo/enterprise#6249
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Co-authored-by: Alexandre Kühn <aku@odoo.com>
Co-authored-by: Julien Giannone <jgi@odoo.com>
Co-authored-by: Lucas Perais <lpe@odoo.com>
Co-authored-by: Sébastien Theys <seb@odoo.com>
Co-authored-by: Xavier Dubuc <xdu@odoo.com>
Diffstat (limited to 'addons/web')
| -rw-r--r-- | addons/web/static/src/js/common_env.js | 17 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/rpc.js | 14 | ||||
| -rw-r--r-- | addons/web/static/src/js/owl_compatibility.js | 7 | ||||
| -rw-r--r-- | addons/web/static/src/js/tools/test_menus.js | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_renderer.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_renderer.js | 14 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_view.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/view_dialogs.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/scss/form_view.scss | 3 | ||||
| -rw-r--r-- | addons/web/static/src/scss/form_view_extra.scss | 12 | ||||
| -rw-r--r-- | addons/web/static/src/scss/primary_variables.scss | 4 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_env.js | 9 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils_mock.js | 5 |
13 files changed, 71 insertions, 32 deletions
diff --git a/addons/web/static/src/js/common_env.js b/addons/web/static/src/js/common_env.js index 0e31b8e3ceb..567607d50b5 100644 --- a/addons/web/static/src/js/common_env.js +++ b/addons/web/static/src/js/common_env.js @@ -25,10 +25,27 @@ odoo.define("web.commonEnv", function (require) { const { _t } = require("web.translation"); const utils = require("web.utils"); + const browser = { + clearInterval: window.clearInterval.bind(window), + clearTimeout: window.clearTimeout.bind(window), + Date: window.Date, + fetch: (window.fetch || (() => { })).bind(window), + Notification: window.Notification, + requestAnimationFrame: window.requestAnimationFrame.bind(window), + setInterval: window.setInterval.bind(window), + setTimeout: window.setTimeout.bind(window), + }; + Object.defineProperty(browser, 'innerHeight', { + get: () => window.innerHeight, + }); + Object.defineProperty(browser, 'innerWidth', { + get: () => window.innerWidth, + }); // Build the basic env const env = { _t, + browser, bus, device, isDebug, diff --git a/addons/web/static/src/js/core/rpc.js b/addons/web/static/src/js/core/rpc.js index b09f7542b5c..e0d33355001 100644 --- a/addons/web/static/src/js/core/rpc.js +++ b/addons/web/static/src/js/core/rpc.js @@ -3,7 +3,7 @@ odoo.define('web.rpc', function (require) { var ajax = require('web.ajax'); -return { +const rpc = { /** * Perform a RPC. Please note that this is not the preferred way to do a * rpc if you are in the context of a widget. In that case, you should use @@ -14,7 +14,7 @@ return { * @returns {Promise<any>} */ query: function (params, options) { - var query = this.buildQuery(params); + var query = rpc.buildQuery(params); return ajax.rpc(query.route, query.params, options); }, /** @@ -66,14 +66,14 @@ return { // In kwargs, we look for "orderby" rather than "orderBy" (note the absence of capital B), // since the Python argument to the actual function is "orderby". orderBy = options.orderBy || params.orderBy || params.kwargs.orderby; - params.kwargs.orderby = orderBy ? this._serializeSort(orderBy) : orderBy; + params.kwargs.orderby = orderBy ? rpc._serializeSort(orderBy) : orderBy; params.kwargs.lazy = 'lazy' in options ? options.lazy : params.lazy; if (options.method === 'web_read_group') { params.kwargs.expand = options.expand || params.expand || params.kwargs.expand; params.kwargs.expand_limit = options.expand_limit || params.expand_limit || params.kwargs.expand_limit; var expandOrderBy = options.expand_orderby || params.expand_orderby || params.kwargs.expand_orderby; - params.kwargs.expand_orderby = expandOrderBy ? this._serializeSort(expandOrderBy) : expandOrderBy; + params.kwargs.expand_orderby = expandOrderBy ? rpc._serializeSort(expandOrderBy) : expandOrderBy; } } @@ -86,7 +86,7 @@ return { // In kwargs, we look for "order" rather than "orderBy" since the Python // argument to the actual function is "order". orderBy = options.orderBy || params.orderBy || params.kwargs.order; - params.kwargs.order = orderBy ? this._serializeSort(orderBy) : orderBy; + params.kwargs.order = orderBy ? rpc._serializeSort(orderBy) : orderBy; } if (options.route === '/web/dataset/search_read') { @@ -97,7 +97,7 @@ return { params.limit = options.limit || params.limit; params.offset = options.offset || params.offset; orderBy = options.orderBy || params.orderBy; - params.sort = orderBy ? this._serializeSort(orderBy) : orderBy; + params.sort = orderBy ? rpc._serializeSort(orderBy) : orderBy; params.context = options.context || params.context || {}; } @@ -123,4 +123,6 @@ return { }, }; +return rpc; + }); diff --git a/addons/web/static/src/js/owl_compatibility.js b/addons/web/static/src/js/owl_compatibility.js index fe76a309085..103c62dd3ba 100644 --- a/addons/web/static/src/js/owl_compatibility.js +++ b/addons/web/static/src/js/owl_compatibility.js @@ -392,8 +392,11 @@ odoo.define('web.OwlCompatibility', function () { */ destroy() { if (this.parentWidget) { - const index = children.get(this.parentWidget).indexOf(this); - children.get(this.parentWidget).splice(index, 1); + const parentChildren = children.get(this.parentWidget); + if (parentChildren) { + const index = parentChildren.indexOf(this); + children.get(this.parentWidget).splice(index, 1); + } } super.destroy(); } diff --git a/addons/web/static/src/js/tools/test_menus.js b/addons/web/static/src/js/tools/test_menus.js index c9c88449ff5..e340b79fd67 100644 --- a/addons/web/static/src/js/tools/test_menus.js +++ b/addons/web/static/src/js/tools/test_menus.js @@ -14,7 +14,7 @@ function createWebClientHooks() { var AbstractController = odoo.__DEBUG__.services['web.AbstractController']; - var Discuss = odoo.__DEBUG__.services['mail.Discuss']; + var DiscussWidget = odoo.__DEBUG__.services['mail/static/src/widgets/discuss/discuss.js']; var WebClient = odoo.__DEBUG__.services["web.WebClient"]; WebClient.include({ @@ -36,12 +36,11 @@ }, }); - if (Discuss) { - Discuss.include({ - _fetchAndRenderThread: function() { - return this._super.apply(this, arguments).then(function (){ - viewUpdateCount++; - }); + if (DiscussWidget) { + DiscussWidget.include({ + on_attach_callback: async function () { + await this._super(...arguments); + viewUpdateCount++; }, }); } diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 7a9693c11fc..fd285010054 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -816,6 +816,9 @@ var BasicRenderer = AbstractRenderer.extend(WidgetAdapterMixin, { self._registerModifiers(node, record, widget); widget.$el.addClass('o_widget'); $el.replaceWith(widget.$el); + if (self._isInDom && widget.on_attach_callback) { + widget.on_attach_callback(); + } }); return $el; diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js index fb8e908c0e1..1927d688c2c 100644 --- a/addons/web/static/src/js/views/form/form_renderer.js +++ b/addons/web/static/src/js/views/form/form_renderer.js @@ -5,12 +5,13 @@ var BasicRenderer = require('web.BasicRenderer'); var config = require('web.config'); var core = require('web.core'); var dom = require('web.dom'); +const { WidgetAdapterMixin } = require('web.OwlCompatibility'); var viewUtils = require('web.viewUtils'); var _t = core._t; var qweb = core.qweb; -var FormRenderer = BasicRenderer.extend({ +var FormRenderer = BasicRenderer.extend(WidgetAdapterMixin, { className: "o_form_view", events: _.extend({}, BasicRenderer.prototype.events, { 'click .o_notification_box .oe_field_translate': '_onTranslate', @@ -44,9 +45,17 @@ var FormRenderer = BasicRenderer.extend({ return this._super.apply(this, arguments); }, /** + * @override + */ + destroy() { + this._super(...arguments); + WidgetAdapterMixin.destroy.call(this); + }, + /** * Called each time the form view is attached into the DOM */ on_attach_callback: function () { + WidgetAdapterMixin.on_attach_callback.call(this); this._isInDom = true; this._super.apply(this, arguments); }, @@ -54,6 +63,7 @@ var FormRenderer = BasicRenderer.extend({ * Called each time the renderer is detached from the DOM. */ on_detach_callback: function () { + WidgetAdapterMixin.on_detach_callback.call(this); this._isInDom = false; this._super.apply(this, arguments); }, @@ -1007,7 +1017,7 @@ var FormRenderer = BasicRenderer.extend({ this.$el.html($newContent); this.$el.toggleClass('o_form_nosheet', !this.has_sheet); if (this.has_sheet) { - this.$el.children().not('.oe_chatter') + this.$el.children().not('.o_FormRenderer_chatterContainer') .wrapAll($('<div/>', {class: 'o_form_sheet_bg'})); } this.$el.toggleClass('o_form_editable', this.mode === 'edit'); diff --git a/addons/web/static/src/js/views/form/form_view.js b/addons/web/static/src/js/views/form/form_view.js index ba58a131155..37cca1a0917 100644 --- a/addons/web/static/src/js/views/form/form_view.js +++ b/addons/web/static/src/js/views/form/form_view.js @@ -47,6 +47,7 @@ var FormView = BasicView.extend({ this.controllerParams.mode = mode; this.rendererParams.mode = mode; + this.rendererParams.isFromFormViewDialog = params.isFromFormViewDialog; }, //-------------------------------------------------------------------------- diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index 95a1ac9acd2..5eeab5ccede 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -200,6 +200,7 @@ var FormViewDialog = ViewDialog.extend({ model: self.model, parentID: self.parentID, recordID: self.recordID, + isFromFormViewDialog: true, }); return formview.getController(self); }).then(function (formView) { diff --git a/addons/web/static/src/scss/form_view.scss b/addons/web/static/src/scss/form_view.scss index b887bca8506..e283f9d1965 100644 --- a/addons/web/static/src/scss/form_view.scss +++ b/addons/web/static/src/scss/form_view.scss @@ -826,9 +826,6 @@ max-width: none; } } - .oe_chatter { - display: none; - } } &:not(.modal-lg) .o_form_view { .o_group { diff --git a/addons/web/static/src/scss/form_view_extra.scss b/addons/web/static/src/scss/form_view_extra.scss index 163d1212299..07fb8bb775b 100644 --- a/addons/web/static/src/scss/form_view_extra.scss +++ b/addons/web/static/src/scss/form_view_extra.scss @@ -1,5 +1,4 @@ .o_form_view { - $sheet-max-width: 1140px; $sheet-min-width: 650px; $sheet-padding: 16px; @@ -12,7 +11,7 @@ > .o_form_sheet { min-width: $sheet-min-width; - max-width: $sheet-max-width; + max-width: $o-form-view-sheet-max-width; min-height: 330px; border: 1px solid #c8c8d3; box-shadow: 0 4px 20px rgba(0,0,0,0.15); @@ -89,15 +88,6 @@ border-right: none; } } - - // Chatter - .oe_chatter { - display: block; - min-width: $sheet-min-width + 2* $sheet-padding; - max-width: $sheet-max-width + 2* $sheet-padding; - margin: 0 auto; - padding: 16px 16px 48px; - } } // Overridden style when form view in modal diff --git a/addons/web/static/src/scss/primary_variables.scss b/addons/web/static/src/scss/primary_variables.scss index b241ebfad19..a65bfe65137 100644 --- a/addons/web/static/src/scss/primary_variables.scss +++ b/addons/web/static/src/scss/primary_variables.scss @@ -112,3 +112,7 @@ $o-kanban-mobile-empty-height: $o-kanban-image-width; // Used to manage spacing in complex dropdown menu $o-kanban-dashboard-dropdown-complex-gap: 5px; + +// Form view + +$o-form-view-sheet-max-width: 1140px !default; diff --git a/addons/web/static/tests/helpers/test_env.js b/addons/web/static/tests/helpers/test_env.js index 5a3256e98fd..c0020ff8308 100644 --- a/addons/web/static/tests/helpers/test_env.js +++ b/addons/web/static/tests/helpers/test_env.js @@ -28,6 +28,15 @@ odoo.define('web.test_env', async function (require) { }; const defaultEnv = { _t: env._t || Object.assign((s => s), { database }), + browser: Object.assign({ + setTimeout: window.setTimeout.bind(window), + clearTimeout: window.clearTimeout.bind(window), + setInterval: window.setInterval.bind(window), + clearInterval: window.clearInterval.bind(window), + requestAnimationFrame: window.requestAnimationFrame.bind(window), + Date: window.Date, + fetch: (window.fetch || (() => { })).bind(window), + }, env.browser), bus: env.bus || new Bus(), device: Object.assign({ isMobile: false }, env.device), isDebug: env.isDebug || (() => false), diff --git a/addons/web/static/tests/helpers/test_utils_mock.js b/addons/web/static/tests/helpers/test_utils_mock.js index 6df49cf59f3..1b70673f067 100644 --- a/addons/web/static/tests/helpers/test_utils_mock.js +++ b/addons/web/static/tests/helpers/test_utils_mock.js @@ -91,7 +91,7 @@ async function _getMockedOwlEnv(params, mockServer) { }, }, params.env.dataManager), services: Object.assign(services, params.env.services), - session: params.session || {}, + session: params.env.session || params.session || {}, }); // deploy services into the env @@ -290,6 +290,9 @@ function intercept(widget, eventName, fn, propagate) { */ function removeSrcAttribute(el, rpc) { var nodes; + if (el.nodeName === "#comment") { + return; + } el = el.nodeType === 8 ? el.nextSibling : el; if (el.nodeName === 'IMG' || el.nodeName === 'IFRAME') { nodes = [el]; |
