diff --git a/addons/web/static/src/core/dialog/dialog_container.js b/addons/web/static/src/core/dialog/dialog_container.js
new file mode 100644
index 00000000000..87895912317
--- /dev/null
+++ b/addons/web/static/src/core/dialog/dialog_container.js
@@ -0,0 +1,60 @@
+/** @odoo-module **/
+
+import { registry } from "../registry";
+import { useService } from "../service_hook";
+
+const { Component, tags } = owl;
+
+class ErrorHandler extends Component {
+ catchError(error) {
+ this.props.callback();
+ throw error;
+ }
+}
+ErrorHandler.template = tags.xml``;
+
+export class DialogContainer extends Component {
+ setup() {
+ this.dialogs = {};
+ const { bus } = useService("dialog");
+ bus.on("ADD", this, (dialog) => {
+ this.dialogs[dialog.id] = dialog;
+ this.render();
+ });
+ bus.on("CLOSE", this, (id) => {
+ this.closeDialog(id);
+ });
+ }
+
+ onDialogClosed(id) {
+ this.closeDialog(id);
+ }
+
+ closeDialog(id) {
+ if (this.dialogs[id].options && this.dialogs[id].options.onCloseCallback) {
+ this.dialogs[id].options.onCloseCallback();
+ }
+ delete this.dialogs[id];
+ this.render();
+ }
+
+ errorCallBack(id) {
+ return () => this.closeDialog(id);
+ }
+
+ __destroy() {
+ this.env.bus.off("ADD", this);
+ this.env.bus.off("CLOSE", this);
+ super.__destroy();
+ }
+}
+DialogContainer.components = { ErrorHandler };
+DialogContainer.template = tags.xml`
+
+
+
+
+
+ `;
+
+registry.category("main_components").add("DialogContainer", DialogContainer);
diff --git a/addons/web/static/src/core/dialog/dialog_service.js b/addons/web/static/src/core/dialog/dialog_service.js
index dc9f7e23666..5dcebc0b869 100644
--- a/addons/web/static/src/core/dialog/dialog_service.js
+++ b/addons/web/static/src/core/dialog/dialog_service.js
@@ -1,76 +1,33 @@
/** @odoo-module **/
import { registry } from "../registry";
-import { useService } from "../service_hook";
import { Dialog } from "./dialog";
-const { Component, core, tags, useState } = owl;
+const { core } = owl;
const { EventBus } = core;
-class ErrorHandler extends Component {
- catchError(error) {
- this.props.callback();
- throw error;
- }
-}
-ErrorHandler.template = tags.xml``;
-
-export class DialogContainer extends Component {
- setup() {
- this.dialogs = useState({});
- this.dialogId = 1;
- const { bus } = useService("dialog");
- bus.on("UPDATE", this, (dialogClass, props, options) => {
- this.addDialog(dialogClass, props, options);
- });
- }
-
- addDialog(dialogClass, props, options) {
- if (!(dialogClass.prototype instanceof Dialog)) {
- throw new Error(dialogClass.name + " must be a subclass of Dialog");
- }
- const id = this.dialogId++;
- this.dialogs[id] = {
- id,
- class: dialogClass,
- props,
- options,
- };
- }
-
- onDialogClosed(id) {
- this.doCloseDialog(id);
- }
-
- doCloseDialog(id) {
- if (this.dialogs[id].options && this.dialogs[id].options.onCloseCallback) {
- this.dialogs[id].options.onCloseCallback();
- }
- delete this.dialogs[id];
- }
-
- errorCallBack(id) {
- return () => this.doCloseDialog(id);
- }
-}
-DialogContainer.components = { ErrorHandler };
-DialogContainer.template = tags.xml`
-
-
-
-
-
- `;
-
-registry.category("main_components").add("DialogContainer", DialogContainer);
-
export const dialogService = {
- start(env) {
+ start() {
const bus = new EventBus();
+ let dialogId = 0;
function open(dialogClass, props, options) {
- bus.trigger("UPDATE", dialogClass, props, options);
+ if (!(dialogClass.prototype instanceof Dialog)) {
+ throw new Error(dialogClass.name + " must be a subclass of Dialog");
+ }
+ const id = ++dialogId;
+ const dialog = {
+ id,
+ class: dialogClass,
+ props,
+ options,
+ };
+ bus.trigger("ADD", dialog);
+ return id;
}
- return { open, bus };
+ function close(id) {
+ bus.trigger("CLOSE", id);
+ }
+ return { open, close, bus };
},
};
diff --git a/addons/web/static/src/legacy/action_adapters.js b/addons/web/static/src/legacy/action_adapters.js
index e1b4d6fcb25..fcd9ece0309 100644
--- a/addons/web/static/src/legacy/action_adapters.js
+++ b/addons/web/static/src/legacy/action_adapters.js
@@ -4,7 +4,6 @@ import Context from "web.Context";
import core from "web.core";
import { ComponentAdapter } from "web.OwlCompatibility";
import { objectToQuery } from "../core/browser/router_service";
-import { useBus } from "../core/bus_hook";
import { useDebugMenu } from "../core/debug/debug_menu";
import { Dialog } from "../core/dialog/dialog";
import { useEffect } from "../core/effect_hook";
@@ -39,34 +38,35 @@ class ActionAdapter extends ComponentAdapter {
// This is protected in legacy (backward compatibility) but should not e supported in Wowl
this.tempQuery = {};
let originalUpdateControlPanel;
- useEffect(() => {
- this.title.setParts({ action: this.widget.getTitle() });
- const query = objectToQuery(this.widget.getState());
- Object.assign(query, this.tempQuery);
- this.tempQuery = null;
- this.__widget = this.widget;
- if (!this.wowlEnv.inDialog) {
- this.router.pushState(query);
- }
- this.wowlEnv.bus.on("ACTION_MANAGER:UPDATE", this, (info) => {
- if (info.type === "MAIN") {
- this.env.bus.trigger("close_dialogs");
+ useEffect(
+ () => {
+ this.title.setParts({ action: this.widget.getTitle() });
+ const query = objectToQuery(this.widget.getState());
+ Object.assign(query, this.tempQuery);
+ this.tempQuery = null;
+ this.__widget = this.widget;
+ if (!this.wowlEnv.inDialog) {
+ this.router.pushState(query);
}
- cleanDomFromBootstrap();
- });
- originalUpdateControlPanel = this.__widget.updateControlPanel.bind(this.__widget);
- this.__widget.updateControlPanel = (newProps) => {
+ this.wowlEnv.bus.on("ACTION_MANAGER:UPDATE", this, () => {
+ this.env.bus.trigger("close_dialogs");
+ cleanDomFromBootstrap();
+ });
+ originalUpdateControlPanel = this.__widget.updateControlPanel.bind(this.__widget);
+ this.__widget.updateControlPanel = (newProps) => {
+ this.trigger("controller-title-updated", this.__widget.getTitle());
+ return originalUpdateControlPanel(newProps);
+ };
this.trigger("controller-title-updated", this.__widget.getTitle());
- return originalUpdateControlPanel(newProps);
- };
- this.trigger("controller-title-updated", this.__widget.getTitle());
- core.bus.trigger("DOM_updated");
+ core.bus.trigger("DOM_updated");
- return () => {
- this.__widget.updateControlPanel = originalUpdateControlPanel;
- this.wowlEnv.bus.off("ACTION_MANAGER:UPDATE", this);
- };
- }, () => []);
+ return () => {
+ this.__widget.updateControlPanel = originalUpdateControlPanel;
+ this.wowlEnv.bus.off("ACTION_MANAGER:UPDATE", this);
+ };
+ },
+ () => []
+ );
}
_trigger_up(ev) {
@@ -188,34 +188,37 @@ function useMagicLegacyReload() {
let legacyReloadProm = null;
const getReloadProm = () => legacyReloadProm;
let manualReload;
- useEffect(() => {
- const widget = comp.widget;
- const controllerReload = widget.reload;
- widget.reload = function (...args) {
- manualReload = true;
- legacyReloadProm = controllerReload.call(widget, ...args);
- return legacyReloadProm.then(() => {
- if (manualReload) {
- legacyReloadProm = null;
- manualReload = false;
- }
- });
- };
- const controllerUpdate = widget.update;
- widget.update = function (...args) {
- const updateProm = controllerUpdate.call(widget, ...args);
- const manualUpdate = !manualReload;
- if (manualUpdate) {
- legacyReloadProm = updateProm;
- }
- return updateProm.then(() => {
+ useEffect(
+ () => {
+ const widget = comp.widget;
+ const controllerReload = widget.reload;
+ widget.reload = function (...args) {
+ manualReload = true;
+ legacyReloadProm = controllerReload.call(widget, ...args);
+ return legacyReloadProm.then(() => {
+ if (manualReload) {
+ legacyReloadProm = null;
+ manualReload = false;
+ }
+ });
+ };
+ const controllerUpdate = widget.update;
+ widget.update = function (...args) {
+ const updateProm = controllerUpdate.call(widget, ...args);
+ const manualUpdate = !manualReload;
if (manualUpdate) {
- legacyReloadProm = null;
+ legacyReloadProm = updateProm;
}
- });
- };
- widget[magicReloadSymbol] = getReloadProm;
- }, () => []);
+ return updateProm.then(() => {
+ if (manualUpdate) {
+ legacyReloadProm = null;
+ }
+ });
+ };
+ widget[magicReloadSymbol] = getReloadProm;
+ },
+ () => []
+ );
return getReloadProm;
}
@@ -226,7 +229,6 @@ export class ViewAdapter extends ActionAdapter {
this.vm = useService("view");
this.shouldUpdateWidget = true;
this.magicReload = useMagicLegacyReload();
- const envWowl = this.env;
useDebugMenu("action", {
action: this.props.viewParams.action,
component: this,
@@ -235,23 +237,6 @@ export class ViewAdapter extends ActionAdapter {
if (this.props.viewInfo.type === "form") {
useDebugMenu("form");
}
- if (!envWowl.inDialog) {
- useBus(envWowl.bus, "ACTION_MANAGER:UPDATE", (info) => {
- switch (info.type) {
- case "OPEN_DIALOG": {
- // we are a main action, and a dialog is going to open:
- // we should not reload
- this.shouldUpdateWidget = false;
- break;
- }
- case "CLOSE_DIALOG": {
- this.shouldUpdateWidget = false;
- info.closingProms.push(() => this.magicReload());
- break;
- }
- }
- });
- }
this.env = Component.env;
}
diff --git a/addons/web/static/src/webclient/actions/action_container.js b/addons/web/static/src/webclient/actions/action_container.js
index e8f31ab27a4..7bb144f229d 100644
--- a/addons/web/static/src/webclient/actions/action_container.js
+++ b/addons/web/static/src/webclient/actions/action_container.js
@@ -9,36 +9,9 @@ const { Component, tags } = owl;
// -----------------------------------------------------------------------------
export class ActionContainer extends Component {
setup() {
- this.main = {};
- this.dialog = {};
+ this.info = {};
this.env.bus.on("ACTION_MANAGER:UPDATE", this, (info) => {
- switch (info.type) {
- case "MAIN":
- this.main = info;
- break;
- case "OPEN_DIALOG": {
- const { onClose } = this.dialog;
- this.dialog = {
- id: info.id,
- props: info.props,
- onClose: onClose || info.onClose,
- };
- break;
- }
- case "CLOSE_DIALOG": {
- let onClose;
- if (this.dialog.id) {
- onClose = this.dialog.onClose;
- } else {
- onClose = info.onClose;
- }
- if (onClose) {
- onClose(info.onCloseInfo);
- }
- this.dialog = {};
- break;
- }
- }
+ this.info = info;
this.render();
});
}
@@ -47,17 +20,11 @@ export class ActionContainer extends Component {
this.env.bus.off("ACTION_MANAGER:UPDATE", this);
super.destroy();
}
-
- onDialogClosed() {
- this.dialog = {};
- this.render();
- }
}
ActionContainer.components = { ActionDialog };
ActionContainer.template = tags.xml`
`;
diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js
index 7cf9edb03d8..ee375c42b9a 100644
--- a/addons/web/static/src/webclient/actions/action_service.js
+++ b/addons/web/static/src/webclient/actions/action_service.js
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { KeyNotFoundError } from "@web/core/registry";
+import { cleanDomFromBootstrap } from "@web/legacy/utils";
import { browser } from "../../core/browser/browser";
import { useBus } from "../../core/bus_hook";
import { makeContext } from "../../core/context";
@@ -9,6 +10,7 @@ import { evaluateExpr } from "../../core/py_js/py";
import { registry } from "../../core/registry";
import { KeepLast } from "../../core/utils/concurrency";
import { sprintf } from "../../core/utils/strings";
+import { ActionDialog } from "./action_dialog";
/**
* @typedef {"current" | "fullscreen" | "new" | "self" | "inline"} ActionMode */
@@ -57,6 +59,7 @@ function makeActionManager(env) {
let controllerStack = [];
let dialogCloseProm;
let actionCache = {};
+ let dialog = {};
// The state action (or default user action if none) is loaded as soon as possible
// so that the next "doAction" will have its action ready when needed.
@@ -536,6 +539,7 @@ function makeActionManager(env) {
ControllerComponent.Component = controller.Component;
if (action.target === "new") {
+ cleanDomFromBootstrap();
const actionDialogProps = {
// TODO add size
ActionComponent: ControllerComponent,
@@ -544,12 +548,16 @@ function makeActionManager(env) {
if (action.name) {
actionDialogProps.title = action.name;
}
- env.bus.trigger("ACTION_MANAGER:UPDATE", {
- type: "OPEN_DIALOG",
- id: ++id,
- props: actionDialogProps,
- onClose: options.onClose,
- });
+
+ const { onClose } = dialog;
+ if (dialog.id) {
+ env.services.dialog.close(dialog.id);
+ }
+ const dialogId = env.services.dialog.open(ActionDialog, actionDialogProps);
+ dialog = {
+ id: dialogId,
+ onClose: onClose || options.onClose,
+ };
return currentActionProm;
}
@@ -571,7 +579,6 @@ function makeActionManager(env) {
controller.props.breadcrumbs = _getBreadcrumbs(nextStack.slice(0, nextStack.length - 1));
const closingProm = _executeCloseAction();
env.bus.trigger("ACTION_MANAGER:UPDATE", {
- type: "MAIN",
id: ++id,
Component: ControllerComponent,
componentProps: controller.props,
@@ -903,13 +910,20 @@ function makeActionManager(env) {
}
async function _executeCloseAction(params = {}) {
- const closingProms = [];
- env.bus.trigger("ACTION_MANAGER:UPDATE", {
- type: "CLOSE_DIALOG",
- closingProms,
- ...params,
- });
- await Promise.all([dialogCloseProm].concat(closingProms.map((fn) => fn())));
+ cleanDomFromBootstrap();
+ let onClose;
+ if (dialog.id) {
+ onClose = dialog.onClose;
+ env.services.dialog.close(dialog.id);
+ } else {
+ onClose = params.onClose;
+ }
+ dialog = {};
+ if (onClose) {
+ await onClose(params.onCloseInfo);
+ }
+
+ return dialogCloseProm;
}
// ---------------------------------------------------------------------------
diff --git a/addons/web/static/tests/core/dialog_service_tests.js b/addons/web/static/tests/core/dialog_service_tests.js
index e865ab91a84..4ccdf514036 100644
--- a/addons/web/static/tests/core/dialog_service_tests.js
+++ b/addons/web/static/tests/core/dialog_service_tests.js
@@ -1,6 +1,7 @@
/** @odoo-module **/
-import { DialogContainer, dialogService } from "@web/core/dialog/dialog_service";
+import { DialogContainer } from "@web/core/dialog/dialog_container";
+import { dialogService } from "@web/core/dialog/dialog_service";
import { ErrorDialog } from "@web/core/errors/error_dialogs";
import { errorService } from "@web/core/errors/error_service";
import { registry } from "@web/core/registry";
diff --git a/addons/web/static/tests/webclient/actions/helpers.js b/addons/web/static/tests/webclient/actions/helpers.js
index 96ec2778fa2..82d1d24742b 100644
--- a/addons/web/static/tests/webclient/actions/helpers.js
+++ b/addons/web/static/tests/webclient/actions/helpers.js
@@ -19,6 +19,7 @@ import { effectService } from "@web/webclient/effects/effect_service";
import { hotkeyService } from "@web/webclient/hotkeys/hotkey_service";
import { menuService } from "@web/webclient/menu_service";
import { WebClient } from "@web/webclient/webclient";
+import { DialogContainer } from "@web/core/dialog/dialog_container";
// This import is needed because of it's sideeffects, for exemple :
// web.test_utils easyload xml templates at line : 124:130.
// Also it set the autocomplete delay time for the field Many2One at 0 for the tests at line : 132:137
diff --git a/addons/web/static/tests/webclient/actions/legacy_tests.js b/addons/web/static/tests/webclient/actions/legacy_tests.js
index 7c3794ba6ef..5482360410c 100644
--- a/addons/web/static/tests/webclient/actions/legacy_tests.js
+++ b/addons/web/static/tests/webclient/actions/legacy_tests.js
@@ -1,6 +1,6 @@
/** @odoo-module **/
-import { DialogContainer } from "@web/core/dialog/dialog_service";
+import { DialogContainer } from "@web/core/dialog/dialog_container";
import { registry } from "@web/core/registry";
import { NotificationContainer } from "@web/core/notifications/notification_container";
import testUtils from "web.test_utils";
diff --git a/addons/web/static/tests/webclient/actions/report_action_tests.js b/addons/web/static/tests/webclient/actions/report_action_tests.js
index 0c6869a3b9e..892f48eec3a 100644
--- a/addons/web/static/tests/webclient/actions/report_action_tests.js
+++ b/addons/web/static/tests/webclient/actions/report_action_tests.js
@@ -8,6 +8,7 @@ import { clearRegistryWithCleanup } from "../../helpers/mock_env";
import { makeFakeNotificationService, makeFakeUserService } from "../../helpers/mock_services";
import { createWebClient, doAction, getActionManagerTestConfig } from "./helpers";
import { mockDownload } from "@web/../tests/helpers/utils";
+import { DialogContainer } from "@web/core/dialog/dialog_container";
let testConfig;
@@ -18,6 +19,7 @@ QUnit.module("ActionManager", (hooks) => {
hooks.beforeEach(() => {
testConfig = getActionManagerTestConfig();
clearRegistryWithCleanup(mainComponentRegistry);
+ mainComponentRegistry.add("DialogContainer", DialogContainer);
});
QUnit.module("Report actions");
@@ -284,7 +286,7 @@ QUnit.module("ActionManager", (hooks) => {
};
const webClient = await createWebClient({ testConfig, mockRPC });
let customHandlerCalled = false;
- registry.category("ir.actions.report handlers").add("custom_handler", async action => {
+ registry.category("ir.actions.report handlers").add("custom_handler", async (action) => {
if (action.id === 7 && !customHandlerCalled) {
customHandlerCalled = true;
assert.step("calling custom handler");
diff --git a/addons/web/static/tests/webclient/actions/window_action_tests.js b/addons/web/static/tests/webclient/actions/window_action_tests.js
index cd0e749b391..ea77bd72e16 100644
--- a/addons/web/static/tests/webclient/actions/window_action_tests.js
+++ b/addons/web/static/tests/webclient/actions/window_action_tests.js
@@ -1,7 +1,7 @@
/** @odoo-module **/
import { debugService } from "@web/core/debug/debug_service";
-import { DialogContainer } from "@web/core/dialog/dialog_service";
+import { DialogContainer } from "@web/core/dialog/dialog_container";
import { registry } from "@web/core/registry";
import { editView } from "@web/legacy/debug_manager";
import { clearUncommittedChanges } from "@web/webclient/actions/action_service";