[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) <aab@odoo.com>
This commit is contained in:
Michael (mcm)
2023-01-18 10:16:08 +01:00
parent 3b5c46d327
commit 07f121faff
3 changed files with 102 additions and 38 deletions
@@ -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) {
@@ -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 = {
@@ -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": `
<form string="Settings" js_class="base_settings">
<app string="CRM" name="crm">
<block>
<setting label="Foo" help="this is foo">
<field name="foo"/>
</setting>
</block>
</app>
</form>`,
"res.config.settings,false,search": `<search/>`,
"task,view_ref,form": `
<form>
<field name="display_name"/>
</form>`,
"task,false,search": "<search></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);