From 07f121faff1a1b7af1dfa6b7a7bc832bda4a1841 Mon Sep 17 00:00:00 2001 From: "Michael (mcm)" Date: Wed, 11 Jan 2023 08:52:30 +0000 Subject: [PATCH] [FIX] web: confirm to leave settings Before this commit, the confirmation dialog was opened only when the user clicked on an action button. Now, the dialog is also shown when the user clicks a menu item in the navbar. Of course this dialog is shown only if the user has changed a setting. closes odoo/odoo#110194 Task: 3102800 X-original-commit: a180178bfd73d6e5f95ee5a9e9cdc1b0d64fc574 Signed-off-by: Aaron Bohy (aab) --- .../static/src/views/form/form_controller.js | 20 +++--- .../settings_form_controller.js | 64 ++++++++++--------- .../settings_form_view_tests.js | 56 ++++++++++++++++ 3 files changed, 102 insertions(+), 38 deletions(-) diff --git a/addons/web/static/src/views/form/form_controller.js b/addons/web/static/src/views/form/form_controller.js index d467b75741f..423b922b951 100644 --- a/addons/web/static/src/views/form/form_controller.js +++ b/addons/web/static/src/views/form/form_controller.js @@ -194,15 +194,7 @@ export class FormController extends Component { useSetupView({ rootRef, - beforeLeave: () => { - if (this.model.root.isDirty) { - return this.model.root.save({ - noReload: true, - stayInEdition: true, - useSaveErrorDialog: true, - }); - } - }, + beforeLeave: () => this.beforeLeave(), beforeUnload: (ev) => this.beforeUnload(ev), getLocalState: () => { // TODO: export the whole model? @@ -299,6 +291,16 @@ export class FormController extends Component { } } + async beforeLeave() { + if (this.model.root.isDirty) { + return this.model.root.save({ + noReload: true, + stayInEdition: true, + useSaveErrorDialog: true, + }); + } + } + async beforeUnload(ev) { const isValid = await this.model.root.urgentSave(); if (!isValid) { diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js b/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js index 11534bed480..7481545fe67 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_controller.js @@ -32,9 +32,6 @@ export class SettingsFormController extends formView.Controller { () => [this.searchState.value] ); useEffect(() => { - if (this.env.__beforeLeave__) { - this.env.__beforeLeave__.remove(this); - } if (this.env.__getLocalState__) { this.env.__getLocalState__.remove(this); } @@ -50,45 +47,27 @@ export class SettingsFormController extends formView.Controller { if (clickParams.name === "cancel") { return true; } - let _continue = true; if ( this.model.root.isDirty && !["execute"].includes(clickParams.name) && !clickParams.noSaveDialog ) { - const message = this.env._t("Would you like to save your changes?"); - await new Promise((resolve) => { - this.dialogService.add(SettingsConfirmationDialog, { - body: message, - confirm: async () => { - await this._save(); - // It doesn't make sense to do the action of the button - // as the res.config.settings `execute` method will trigger a reload. - _continue = false; - resolve(); - }, - cancel: async () => { - await this.model.root.discard(); - await this.model.root.save({ stayInEdition: true }); - _continue = true; - resolve(); - }, - stayHere: () => { - _continue = false; - resolve(); - }, - }); - }); + return this._confirmSave(); } else { - _continue = await this.model.root.save({ stayInEdition: true }); + return this.model.root.save({ stayInEdition: true }); } - return _continue; } displayName() { return this.env._t("Settings"); } + beforeLeave() { + if (this.model.root.isDirty) { + return this._confirmSave(); + } + } + //This is needed to avoid the auto save when unload beforeUnload() {} @@ -121,6 +100,33 @@ export class SettingsFormController extends formView.Controller { pick(this.model.root, "context", "evalContext", "resModel", "resId", "resIds"), }); } + + async _confirmSave() { + let _continue = true; + await new Promise((resolve) => { + this.dialogService.add(SettingsConfirmationDialog, { + body: this.env._t("Would you like to save your changes?"), + confirm: async () => { + await this._save(); + // It doesn't make sense to do the action of the button + // as the res.config.settings `execute` method will trigger a reload. + _continue = false; + resolve(); + }, + cancel: async () => { + await this.model.root.discard(); + await this.model.root.save({ stayInEdition: true }); + _continue = true; + resolve(); + }, + stayHere: () => { + _continue = false; + resolve(); + }, + }); + }); + return _continue; + } } SettingsFormController.components = { diff --git a/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js b/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js index 0975815ea6d..d634175dbb8 100644 --- a/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js +++ b/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js @@ -517,6 +517,62 @@ QUnit.module("SettingsFormView", (hooks) => { ]); }); + QUnit.test( + "settings views ask for confirmation when leaving if dirty", + async function (assert) { + serverData.actions = { + 1: { + id: 1, + name: "Settings view", + res_model: "res.config.settings", + type: "ir.actions.act_window", + views: [[1, "form"]], + }, + 4: { + id: 4, + name: "Other action", + res_model: "task", + type: "ir.actions.act_window", + views: [["view_ref", "form"]], + }, + }; + + serverData.views = { + "res.config.settings,1,form": ` +
+ + + + + + + +
`, + "res.config.settings,false,search": ``, + "task,view_ref,form": ` +
+ + `, + "task,false,search": "", + }; + + const webClient = await createWebClient({ serverData }); + await doAction(webClient, 1); + + const action = doAction(webClient, 4); + await nextTick(); + assert.containsNone(target, ".modal", "do not open modal if there is no change"); + await action; + + await doAction(webClient, 1); + await click(target, ".o_field_boolean input"); + doAction(webClient, 4); + await nextTick(); + assert.containsOnce(target, ".modal", "open modal if there is change"); + assert.strictEqual(target.querySelector(".modal-title").textContent, "Unsaved changes"); + } + ); + QUnit.test("Auto save: don't save on closing tab/browser", async function (assert) { assert.expect(3);