[FIX] mail: handles unlink button spam on attachments

Before this commit, attempting to delete attachments multiple times
could result in errors, especially on slow networks.

This commit fixes this issue.

task-2363522

closes odoo/odoo#63189

X-original-commit: 528adfb313ab3c8d4aeb3309aa7371301ab8085e
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Thanh Dodeur
2020-12-10 16:06:25 +00:00
parent ed81f25bee
commit 4f4ff204da
2 changed files with 54 additions and 5 deletions
@@ -595,6 +595,41 @@ QUnit.test('close attachment viewer', async function (assert) {
);
});
QUnit.test('clicking on the delete attachment button multiple times should do the rpc only once', async function (assert) {
assert.expect(2);
await this.start({
async mockRPC(route, args) {
if (args.method === "unlink" && args.model === "ir.attachment") {
assert.step('attachment_unlink');
return;
}
return this._super(...arguments);
},
});
const attachment = this.env.models['mail.attachment'].create({
filename: "test.txt",
id: 750,
mimetype: 'text/plain',
name: "test.txt",
});
await this.createAttachmentComponent(attachment, {
detailsMode: 'hover',
});
await afterNextRender(() => {
document.querySelector('.o_Attachment_actionUnlink').click();
});
await afterNextRender(() => {
document.querySelector('.o_AttachmentDeleteConfirmDialog_confirmButton').click();
document.querySelector('.o_AttachmentDeleteConfirmDialog_confirmButton').click();
document.querySelector('.o_AttachmentDeleteConfirmDialog_confirmButton').click();
});
assert.verifySteps(
['attachment_unlink'],
"The unlink method must be called once"
);
});
});
});
});
@@ -97,12 +97,20 @@ function factory(dependencies) {
* Remove this attachment globally.
*/
async remove() {
if (this.isUnlinkPending) {
return;
}
if (!this.isTemporary) {
await this.async(() => this.env.services.rpc({
model: 'ir.attachment',
method: 'unlink',
args: [this.id],
}, { shadow: true }));
this.update({ isUnlinkPending: true });
try {
await this.async(() => this.env.services.rpc({
model: 'ir.attachment',
method: 'unlink',
args: [this.id],
}, { shadow: true }));
} finally {
this.update({ isUnlinkPending: false });
}
} else if (this.uploadingAbortController) {
this.uploadingAbortController.abort();
}
@@ -335,6 +343,12 @@ function factory(dependencies) {
compute: '_computeIsTextFile',
dependencies: ['fileType'],
}),
/**
* True if an unlink RPC is pending, used to prevent multiple unlink attempts.
*/
isUnlinkPending: attr({
default: false,
}),
isViewable: attr({
compute: '_computeIsViewable',
dependencies: [