[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:
committed by
qsm-odoo
co-authored by
Samuel Degueldre
parent
51a08bbfa3
commit
62b4e968c9
@@ -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,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user