[IMP] web_editor: perf; skip mutually-cancelling actions in editor

Previously, when the user quickly hovered over many options (eg when
trying to click on the last item of a select menu), all of the options
that were briefly hovered would be previewed in succession. While this
is not a problem for options that are fast to change, some option take
some time (eg, image filters), but all of the intermediary previews
would still show one after another, which is both very slow and
completely useless.

This commit adds a per-widget action queue. When a widget is called, it
will immediately add an action item to this queue, but when the
snippet_edition mutex becomes available to execute this action, it will
first check that this action will not be immediately reverted, and if it
will, the action is skipped entirely.

task-2337344

closes odoo/odoo#58256

X-original-commit: 4d1e47213627304329fa438cae6d6c58484dbb32
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Samuel Degueldre
2020-09-22 15:52:26 +00:00
parent cb85e4baf5
commit fe388460d8
@@ -1816,6 +1816,7 @@ const SnippetOptionWidget = Widget.extend({
this.ownerDocument = this.$target[0].ownerDocument;
this._userValueWidgets = [];
this._actionQueues = new Map();
},
/**
* @override
@@ -2684,9 +2685,34 @@ const SnippetOptionWidget = Widget.extend({
}
}
// Queue action so that we can later skip useless actions.
if (!this._actionQueues.get(widget)) {
this._actionQueues.set(widget, []);
}
const currentAction = {previewMode};
this._actionQueues.get(widget).push(currentAction);
// Ask a mutexed snippet update according to the widget value change
const shouldRecordUndo = (!previewMode && !ev.data.isSimulatedEvent);
this.trigger_up('snippet_edition_request', {exec: async () => {
// Filter actions that are counterbalanced by earlier/later actions
const actionQueue = this._actionQueues.get(widget).filter(({previewMode}, i, actions) => {
const prev = actions[i - 1];
const next = actions[i + 1];
if (previewMode === true && next && next.previewMode) {
return false;
} else if (previewMode === 'reset' && prev && prev.previewMode) {
return false;
}
return true;
});
// Skip action if it's been counterbalanced
if (!actionQueue.includes(currentAction)) {
this._actionQueues.set(widget, actionQueue);
return;
}
this._actionQueues.set(widget, actionQueue.filter(action => action !== currentAction));
if (ev.data.prepare) {
ev.data.prepare();
}