[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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user