[IMP] website: block the client action clicks when blocking the iframe

Before this commit, the BlockIframe component was managing its
visibility based on the 'BLOCK'/'UNBLOCK' events. Also, it was possible
to continue to interact with the SnippetsMenu when the iframe was
blocked.

With the previous commit modifications, it is needed to block the clicks
and prevent interactions with the SnippetsMenu when reloading the
iframe. For that, the BlockIframe component is reworked: a specific
state for this component is added at the WebsitePreview level. This
allows to add a o_is_blocked class on the container which prevents the
clicks on the client action when it is blocked. Therefore, it is not
blocking only the iframe anymore, but the whole preview. It is renamed
accordingly.
The 'loaderDelay' parameter is dropped, as it was used only to show the
loader after the snippets menu transition. As now the snippets menu is
removed after the reload, it is not useful anymore.

Related to https://github.com/odoo/odoo/commit/31cc10b91dc7762e23b4bde9b945be0c4ce3fe3b

task-2687506

Part-of: odoo/odoo#97408
This commit is contained in:
Younn Olivier
2022-08-31 22:13:21 +02:00
committed by qsm-odoo
parent edc4a09cef
commit 3c2febddb6
7 changed files with 68 additions and 68 deletions
@@ -1,7 +1,7 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { useService } from '@web/core/utils/hooks';
import { useService, useBus } from '@web/core/utils/hooks';
import core from 'web.core';
import { AceEditorAdapterComponent } from '../../components/ace_editor/ace_editor';
import { WebsiteEditorComponent } from '../../components/editor/editor';
@@ -10,42 +10,8 @@ import {OptimizeSEODialog} from '@website/components/dialog/seo';
const { Component, onWillStart, onMounted, onWillUnmount, useRef, useEffect, useState } = owl;
class BlockIframe extends Component {
setup() {
this.websiteService = useService('website');
this.state = useState({
blockIframe: false,
showLoader: false,
});
this.processes = [];
this.ANONYMOUS_PROCESS_ID = 'ANONYMOUS_PROCESS_ID';
this.websiteService.bus.addEventListener("BLOCK", this.block.bind(this));
this.websiteService.bus.addEventListener("UNBLOCK", this.unblock.bind(this));
}
block(event) {
if (event.detail.showLoader && !this.state.showLoader) {
setTimeout(() => {
this.state.showLoader = true;
}, event.detail.loaderDelay);
}
if (!this.processes.length) {
this.state.blockIframe = true;
}
this.processes.push(event.detail.processId || this.ANONYMOUS_PROCESS_ID);
}
unblock(event) {
const processId = event.detail.processId || this.ANONYMOUS_PROCESS_ID;
const processIndex = this.processes.indexOf(processId);
if (processIndex > -1) {
this.processes.splice(processIndex, 1);
}
if (this.processes.length === 0) {
this.state.blockIframe = false;
this.state.showLoader = false;
}
}
}
BlockIframe.template = 'website.BlockIframe';
class BlockPreview extends Component {}
BlockPreview.template = 'website.BlockPreview';
export class WebsitePreview extends Component {
setup() {
@@ -62,6 +28,13 @@ export class WebsitePreview extends Component {
this.iframefallback = useRef('iframefallback');
this.container = useRef('container');
this.websiteContext = useState(this.websiteService.context);
this.blockedState = useState({
isBlocked: false,
showLoader: false,
});
useBus(this.websiteService.bus, 'BLOCK', (event) => this.block(event.detail));
useBus(this.websiteService.bus, 'UNBLOCK', () => this.unblock());
onWillStart(async () => {
await this.websiteService.fetchWebsites();
@@ -98,12 +71,12 @@ export class WebsitePreview extends Component {
}, () => [this.props.action.context.params]);
onMounted(() => {
this.websiteService.blockIframe(true, 0, 'load-iframe');
this.iframe.el.addEventListener('load', () => this.websiteService.unblockIframe('load-iframe'), { once: true });
this.websiteService.blockPreview(true, 'load-iframe');
this.iframe.el.addEventListener('load', () => this.websiteService.unblockPreview('load-iframe'), { once: true });
// For a frontend page, it is better to use the
// OdooFrameContentLoaded event to unblock the iframe, as it is
// triggered faster than the load event.
this.iframe.el.addEventListener('OdooFrameContentLoaded', () => this.websiteService.unblockIframe('load-iframe'), { once: true });
this.iframe.el.addEventListener('OdooFrameContentLoaded', () => this.websiteService.unblockPreview('load-iframe'), { once: true });
});
onWillUnmount(() => {
@@ -173,6 +146,16 @@ export class WebsitePreview extends Component {
});
}
block({ showLoader = true } = {}) {
this.blockedState.isBlocked = true;
this.blockedState.showLoader = showLoader;
}
unblock() {
this.blockedState.isBlocked = false;
this.blockedState.showLoader = false;
}
addWelcomeMessage() {
if (this.websiteService.isPublisher) {
const $wrap = $(this.iframe.el.contentDocument.querySelector('#wrapwrap.homepage')).find('#wrap');
@@ -313,7 +296,7 @@ export class WebsitePreview extends Component {
WebsitePreview.template = 'website.WebsitePreview';
WebsitePreview.components = {
WebsiteEditorComponent,
BlockIframe,
BlockPreview,
WebsiteTranslator,
AceEditorAdapterComponent,
};
@@ -18,6 +18,10 @@
margin-right: $o-we-sidebar-width !important;
}
&.o_is_blocked {
pointer-events: none;
}
.o_iframe_container {
position: relative;
height: 100%;
@@ -43,7 +47,7 @@
box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
}
.o_block_iframe {
.o_block_preview {
border-radius: 25px;
}
}
@@ -65,6 +69,10 @@
border-bottom: 0px;
}
}
.o_block_preview {
z-index: $zindex-modal-backdrop + 1 !important;
}
}
header {
@@ -73,6 +81,6 @@ header {
}
}
.o_block_iframe {
z-index: $zindex-dropdown - 1;
.o_block_preview {
z-index: $zindex-dropdown - 1 !important;
}
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="website.BlockIframe" owl="1">
<t t-if="this.state.blockIframe">
<div class="o_block_iframe position-absolute d-flex justify-content-center align-items-center flex-column h-100 w-100" t-att-class="{ 'bg-black-50': this.state.showLoader }">
<t t-if="this.state.showLoader">
<div class="o_spinner mb-4">
<img src="/web/static/img/spin.svg" alt="Loading..."/>
</div>
</t>
<t t-name="website.BlockPreview" owl="1">
<div class="o_blockUI o_block_preview position-fixed d-flex justify-content-center align-items-center flex-column h-100 w-100 bg-black-50">
<div class="o_spinner mb-4">
<img src="/web/static/img/spin.svg" alt="Loading..."/>
</div>
</t>
</div>
</t>
<t t-name="website.WebsitePreview" owl="1">
<div class="o_website_preview" t-att-class="{ 'editor_enable editor_has_snippets': this.websiteContext.snippetsLoaded }" t-ref="container">
<div class="o_website_preview" t-att-class="{ 'editor_enable editor_has_snippets': this.websiteContext.snippetsLoaded, 'o_is_blocked': this.blockedState.isBlocked }" t-ref="container">
<BlockPreview t-if="this.blockedState.showLoader"/>
<div class="o_iframe_container" t-att-class="{ 'o_is_mobile': this.websiteContext.isMobile }">
<BlockIframe/>
<iframe t-if="!testMode"
t-att-src="iframeFallbackUrl"
class="o_ignore_in_tour"
@@ -25,7 +25,6 @@ export class WebsiteEditorComponent extends Component {
useChildSubEnv(legacyEnv);
onWillStart(async () => {
this.websiteService.blockIframe(false);
this.Wysiwyg = await this.websiteService.loadWysiwyg();
});
@@ -36,6 +35,7 @@ export class WebsiteEditorComponent extends Component {
}, () => [this.websiteContext.isPublicRootReady]);
onMounted(() => {
this.websiteService.blockPreview(false);
if (this.websiteContext.isPublicRootReady) {
this.publicRootReady();
}
@@ -56,7 +56,7 @@ export class WebsiteEditorComponent extends Component {
publicRootReady() {
if (this.websiteService.currentWebsite.metadata.translatable) {
this.websiteContext.edition = false;
this.websiteService.unblockIframe();
this.websiteService.unblockPreview();
} else {
this.state.showWysiwyg = true;
}
@@ -72,7 +72,7 @@ export class WebsiteEditorComponent extends Component {
this.state.reloading = false;
}
this.wysiwygOptions.invalidateSnippetCache = false;
this.websiteService.unblockIframe();
this.websiteService.unblockPreview();
this.websiteService.hideLoader();
}
/**
@@ -83,7 +83,7 @@ export class WebsiteEditorComponent extends Component {
* @param widgetEl {HTMLElement} Widget element of the editor to copy.
*/
willReload(widgetEl) {
this.websiteService.blockIframe();
this.websiteService.blockPreview();
if (widgetEl) {
this.loadingDummy = markup(widgetEl.innerHTML);
}
@@ -120,9 +120,9 @@ export class WebsiteEditorComponent extends Component {
async quit({ reloadIframe = true, onLeave } = {}) {
this.onWillUnmount = onLeave;
if (reloadIframe) {
this.websiteService.blockIframe(true);
this.websiteService.blockPreview();
await this.props.reloadIframe();
this.websiteService.unblockIframe();
this.websiteService.unblockPreview();
}
this.websiteContext.snippetsLoaded = false;
setTimeout(this.destroyAfterTransition.bind(this), 400);
@@ -92,7 +92,7 @@
.o_loading_dummy {
position: absolute;
z-index: 100000;
z-index: $zindex-modal-backdrop;
}
.o_website_fullscreen {
@@ -43,14 +43,14 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({
this.__onSelectionChange = ev => {
this._toggleAnimatedTextButton();
};
this.ownerDocument.addEventListener('selectionchange', this.__onSelectionChange);
this.$body[0].addEventListener('selectionchange', this.__onSelectionChange);
},
/**
* @override
*/
destroy() {
this._super(...arguments);
this.ownerDocument.removeEventListener('selectionchange', this.__onSelectionChange);
this.$body[0].removeEventListener('selectionchange', this.__onSelectionChange);
this.$body[0].classList.remove('o_animated_text_highlighted');
},
@@ -23,6 +23,8 @@ export const unslugHtmlDataObject = (repr) => {
};
};
const ANONYMOUS_PROCESS_ID = 'ANONYMOUS_PROCESS_ID';
export const websiteService = {
dependencies: ['orm', 'action', 'user', 'dialog', 'hotkey'],
async start(env, { orm, action, user, dialog, hotkey }) {
@@ -39,6 +41,7 @@ export const websiteService = {
let isDesigner;
let hasMultiWebsites;
let actionJsId;
let blockingProcesses = [];
const context = reactive({
showNewContentModal: false,
showAceEditor: false,
@@ -198,11 +201,20 @@ export const websiteService = {
}
return Wysiwyg;
},
blockIframe(showLoader = true, loaderDelay = 0, processId) {
bus.trigger('BLOCK', {showLoader, loaderDelay, processId});
blockPreview(showLoader, processId) {
if (!blockingProcesses.length) {
bus.trigger('BLOCK', { showLoader });
}
blockingProcesses.push(processId || ANONYMOUS_PROCESS_ID);
},
unblockIframe(processId) {
bus.trigger('UNBLOCK', { processId });
unblockPreview(processId) {
const processIndex = blockingProcesses.indexOf(processId || ANONYMOUS_PROCESS_ID);
if (processIndex > -1) {
blockingProcesses.splice(processIndex, 1);
if (blockingProcesses.length === 0) {
bus.trigger('UNBLOCK');
}
}
},
showLoader(props) {
bus.trigger('SHOW-WEBSITE-LOADER', props);