From d824a173445c4ae722af7690bf2ac63046dda6e8 Mon Sep 17 00:00:00 2001 From: Benoit Socias Date: Tue, 19 Apr 2022 12:24:30 +0000 Subject: [PATCH] [FIX] web_editor: reset m2m suggestions also after RPC returns Since [1] when the m2m widget was introduced, when the network latency is high enough, the `_search()` method can be called several times in parallel. The former list of existing records is removed before doing the RPC call, then populated with the results received after the RPC call returns. With the proper timing and race conditions, this could lead to having the list cleared several times, then populated several times with the incoming results. This commit makes sure the list is cleared after the RPC results are obtained instead of before. Steps to reproduce: - Create and save a blog post with several tags. - Edit blog post. - Configure network latency to one second in browser's developer tools. - Remove one tag. - Remove another tag after 500ms. (Because the search happens after a `setTimeout`.) - Open tag suggestions. => List contained the concatenation of the two suggestion lists. [1]: https://github.com/odoo/odoo/commit/be05ac7e2b8d866a72693b761f3bc8576d54e59e task-2811746 closes odoo/odoo#109479 X-original-commit: 48564abe484f61c09ca98c3cddd5b4b0b55e6497 Signed-off-by: Benoit Socias (bso) Signed-off-by: Quentin Smetz (qsm) --- .../static/src/js/editor/snippets.options.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 517bc881e53..45b9bfc4706 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -2638,18 +2638,6 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ * @private */ async _search(needle) { - this._userValueWidgets = this._userValueWidgets.filter(widget => !widget.isDestroyed()); - // Remove select options - this._userValueWidgets - .filter(widget => { - return widget instanceof ButtonUserValueWidget && - widget.el.parentElement.matches('we-selection-items'); - }).forEach(button => { - if (button.isPreviewed()) { - button.notifyValueChange('reset'); - } - button.destroy(); - }); const recTuples = await this._rpc({ model: this.options.model, method: 'name_search', @@ -2665,6 +2653,18 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ method: 'read', args: [recTuples.map(([id, _name]) => id), this.options.fields], }); + // Remove select options. + this._userValueWidgets.filter(widget => { + return widget instanceof ButtonUserValueWidget && + !widget.isDestroyed() && + widget.el.parentElement.matches('we-selection-items'); + }).forEach(button => { + if (button.isPreviewed()) { + button.notifyValueChange('reset'); + } + button.destroy(); + }); + this._userValueWidgets = this._userValueWidgets.filter(widget => !widget.isDestroyed()); records.forEach(record => { this.displayNameCache[record.id] = record.display_name; });