diff --git a/addons/web/static/src/legacy/action_adapters.js b/addons/web/static/src/legacy/action_adapters.js index 95e4f773726..d0c234ed4a1 100644 --- a/addons/web/static/src/legacy/action_adapters.js +++ b/addons/web/static/src/legacy/action_adapters.js @@ -69,6 +69,10 @@ class ActionAdapter extends ComponentAdapter { ); } + get actionId() { + throw new Error("Should be implement by specific adapters"); + } + _trigger_up(ev) { const payload = ev.data; if (ev.name === "do_action") { @@ -147,6 +151,12 @@ class ActionAdapter extends ComponentAdapter { } super.willUnmount(); } + __destroy() { + if (this.actionService.__legacy__isActionInStack(this.actionId)) { + this.widget = null; + } + super.__destroy(...arguments); + } } export class ClientActionAdapter extends ActionAdapter { @@ -156,6 +166,10 @@ export class ClientActionAdapter extends ActionAdapter { this.env = Component.env; } + get actionId() { + return this.props.widgetArgs[0].jsId; + } + async willStart() { if (this.props.widget) { this.widget = this.props.widget; @@ -240,6 +254,10 @@ export class ViewAdapter extends ActionAdapter { this.env = Component.env; } + get actionId() { + return this.props.viewParams.action.jsId; + } + async willStart() { if (this.props.widget) { this.widget = this.props.widget; @@ -254,7 +272,9 @@ export class ViewAdapter extends ActionAdapter { if (this.__owl__.status === 5 /* DESTROYED */) { // the component might have been destroyed meanwhile, but if so, `this.widget` wasn't // destroyed by OwlCompatibility layer as it wasn't set yet, so destroy it now - this.widget.destroy(); + if (!this.actionService.__legacy__isActionInStack(this.actionId)) { + this.widget.destroy(); + } return Promise.resolve(); } return this.widget._widgetRenderAndInsert(() => {}); diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js index bb051382a39..23b24263389 100644 --- a/addons/web/static/src/webclient/actions/action_service.js +++ b/addons/web/static/src/webclient/actions/action_service.js @@ -1215,6 +1215,9 @@ function makeActionManager(env) { get currentController() { return _getCurrentController(); }, + __legacy__isActionInStack(actionId) { + return controllerStack.find((c) => c.action.jsId === actionId); + }, }; } diff --git a/addons/web/static/tests/webclient/actions/concurrency_tests.js b/addons/web/static/tests/webclient/actions/concurrency_tests.js index c879df5a97e..e68b66cbae6 100644 --- a/addons/web/static/tests/webclient/actions/concurrency_tests.js +++ b/addons/web/static/tests/webclient/actions/concurrency_tests.js @@ -582,4 +582,39 @@ QUnit.module("ActionManager", (hooks) => { "/web/dataset/search_read", ]); }); + + QUnit.test("click multiple times to open a record", async function (assert) { + assert.expect(5); + + const def = testUtils.makeTestPromise(); + const defs = [null, def]; + const mockRPC = async (route, args) => { + if (args.method === "read") { + await Promise.resolve(defs.shift()); + } + }; + + const webClient = await createWebClient({ serverData, mockRPC }); + await doAction(webClient, 3); + assert.containsOnce(webClient, ".o_list_view"); + + await testUtils.dom.click(webClient.el.querySelector(".o_list_view .o_data_row")); + await legacyExtraNextTick(); + assert.containsOnce(webClient, ".o_form_view"); + + await testUtils.dom.click(webClient.el.querySelector(".o_back_button")); + await legacyExtraNextTick(); + + assert.containsOnce(webClient, ".o_list_view"); + + await testUtils.dom.click(webClient.el.querySelector(".o_list_view .o_data_row")); + await testUtils.dom.click(webClient.el.querySelector(".o_list_view .o_data_row")); + await legacyExtraNextTick(); + assert.containsOnce(webClient, ".o_list_view"); + + def.resolve(); + await nextTick(); + await legacyExtraNextTick(); + assert.containsOnce(webClient, ".o_form_view"); + }); });