[FIX] website, web_editor: ensure the loader appears on click options

Before this commit the loader did not always get displayed when
switching to the options tab.

After this commit the loader always gets displayed ASAP when switching
to the options tab until the tab is ready and displayed.

task-2439295
https://github.com/odoo/odoo/pull/65223

X-original-commit: f2fa17540e130b5cf3f70570a3a97495cfbb7499
Co-authored-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
Benoit Socias
2021-02-15 18:30:08 +00:00
committed by qsm-odoo
co-authored by Samuel Degueldre
parent 51a08bbfa3
commit 62b4e968c9
2 changed files with 57 additions and 31 deletions
@@ -2214,22 +2214,29 @@ var SnippetsMenu = Widget.extend({
async _execWithLoadingEffect(action, contentLoading = true, delay = 500) {
const mutexExecResult = this._mutex.exec(action);
if (!this.loadingTimers[contentLoading]) {
this.loadingTimers[contentLoading] = setTimeout(() => {
const addLoader = () => {
this.loadingElements[contentLoading] = this._createLoadingElement();
if (contentLoading) {
this.$snippetEditorArea.append(this.loadingElements[contentLoading]);
} else {
this.el.appendChild(this.loadingElements[contentLoading]);
}
}, delay);
};
if (delay) {
this.loadingTimers[contentLoading] = setTimeout(addLoader, delay);
} else {
addLoader();
}
this._mutex.getUnlockedDef().then(() => {
// Note: we remove the loading element at the end of the
// execution queue *even if subsequent actions are content
// related or not*. This is a limitation of the loading feature,
// the goal is still to limit the number of elements in that
// queue anyway.
clearTimeout(this.loadingTimers[contentLoading]);
this.loadingTimers[contentLoading] = undefined;
if (delay) {
clearTimeout(this.loadingTimers[contentLoading]);
this.loadingTimers[contentLoading] = undefined;
}
if (this.loadingElements[contentLoading]) {
this.loadingElements[contentLoading].remove();
@@ -154,37 +154,56 @@ weSnippetEditor.Class.include({
*/
async _onThemeTabClick(ev) {
// Note: nothing async here but start the loading effect asap
this._execWithLoadingEffect(async () => new Promise(resolve => setTimeout(() => resolve(), 0)), false, 0);
let releaseLoader;
try {
const promise = new Promise(resolve => releaseLoader = resolve);
this._execWithLoadingEffect(() => promise, false, 0);
// loader is added to the DOM synchronously
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
// ensure loader is rendered: first call asks for the (already done) DOM update,
// second call happens only after rendering the first "updates"
if (!this.topFakeOptionEl) {
let el;
for (const [elementName, title] of this.optionsTabStructure) {
const newEl = document.createElement(elementName);
newEl.dataset.name = title;
if (el) {
el.appendChild(newEl);
} else {
this.topFakeOptionEl = newEl;
if (!this.topFakeOptionEl) {
let el;
for (const [elementName, title] of this.optionsTabStructure) {
const newEl = document.createElement(elementName);
newEl.dataset.name = title;
if (el) {
el.appendChild(newEl);
} else {
this.topFakeOptionEl = newEl;
}
el = newEl;
}
el = newEl;
this.bottomFakeOptionEl = el;
this.el.appendChild(this.topFakeOptionEl);
}
this.bottomFakeOptionEl = el;
this.el.appendChild(this.topFakeOptionEl);
// Need all of this in that order so that:
// - the element is visible and can be enabled and the onFocus method is
// called each time.
// - the element is hidden afterwards so it does not take space in the
// DOM, same as the overlay which may make a scrollbar appear.
this.topFakeOptionEl.classList.remove('d-none');
const editorPromise = this._activateSnippet($(this.bottomFakeOptionEl));
releaseLoader(); // because _activateSnippet uses the same mutex as the loader
releaseLoader = undefined;
const editor = await editorPromise;
this.topFakeOptionEl.classList.add('d-none');
editor.toggleOverlay(false);
this._updateLeftPanelContent({
tab: this.tabs.THEME,
});
} catch (e) {
// Normally the loading effect is removed in case of error during the action but here
// the actual activity is happening outside of the action, the effect must therefore
// be cleared in case of error as well
if (releaseLoader) {
releaseLoader();
}
throw e;
}
// Need all of this in that order so that:
// - the element is visible and can be enabled and the onFocus method is
// called each time.
// - the element is hidden afterwards so it does not take space in the
// DOM, same as the overlay which may make a scrollbar appear.
this.topFakeOptionEl.classList.remove('d-none');
const editor = await this._activateSnippet($(this.bottomFakeOptionEl));
this.topFakeOptionEl.classList.add('d-none');
editor.toggleOverlay(false);
this._updateLeftPanelContent({
tab: this.tabs.THEME,
});
},
});