[IMP] mail: add hover download button on image attachments
PURPOSE Improve how image attachments are downloaded from the chatter by adding the `download` button when hovering image files SPECIFICATIONS add a `Download` button (icon) on the bottom right corner of image attachments on hover Task-2802810 closes odoo/odoo#92013 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
committed by
Alexandre Kühn
parent
947770ce7e
commit
222ae02cea
@@ -20,15 +20,14 @@
|
||||
<i class="fa fa-spin fa-spinner"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="attachmentImage.attachment.isDeletable">
|
||||
<div class="o_AttachmentImage_imageOverlay position-absolute top-0 bottom-0 start-0 end-0 text-right p-2 text-white opacity-0 opacity-100-hover">
|
||||
<div class="o_AttachmentImage_action o_AttachmentImage_actionUnlink btn btn-sm btn-dark rounded opacity-75 opacity-100-hover"
|
||||
t-att-class="{'o-pretty': attachmentImage.attachmentList.composerViewOwner}" t-on-click="attachmentImage.onClickUnlink" title="Remove"
|
||||
>
|
||||
<i class="fa fa-trash"/>
|
||||
</div>
|
||||
<div class="o_AttachmentImage_imageOverlay position-absolute top-0 bottom-0 start-0 end-0 p-2 text-white opacity-0 opacity-100-hover d-flex align-items-end flax-wrap flex-column">
|
||||
<div t-if="attachmentImage.attachment.isDeletable" class="o_AttachmentImage_action o_AttachmentImage_actionUnlink btn btn-sm btn-dark rounded opacity-75 opacity-100-hover" t-att-class="{'o-pretty': attachmentImage.attachmentList.composerViewOwner}" t-on-click="attachmentImage.onClickUnlink" title="Remove">
|
||||
<i class="fa fa-trash"/>
|
||||
</div>
|
||||
</t>
|
||||
<div t-if="attachmentImage.hasDownloadButton" class="o_AttachmentImage_action o_AttachmentImage_actionDownload btn btn-sm btn-dark rounded opacity-75 opacity-100-hover mt-auto" t-on-click="attachmentImage.onClickDownload" title="Download">
|
||||
<i class="fa fa-download"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -3,15 +3,36 @@
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear, insertAndReplace, replace } from '@mail/model/model_field_command';
|
||||
import { isEventHandled, markEventHandled } from '@mail/utils/utils';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentImage',
|
||||
identifyingFields: ['attachmentList', 'attachment'],
|
||||
recordMethods: {
|
||||
/**
|
||||
* Opens the attachment viewer when clicking on viewable attachment.
|
||||
* Called when clicking on download icon.
|
||||
*
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickImage() {
|
||||
onClickDownload(ev) {
|
||||
markEventHandled(ev, 'AttachmentImage.onClickDownload');
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.attachment.download();
|
||||
},
|
||||
/**
|
||||
* Opens the attachment viewer when clicking on viewable attachment.
|
||||
*
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickImage(ev) {
|
||||
if (isEventHandled(ev, 'AttachmentImage.onClickDownload')) {
|
||||
return;
|
||||
}
|
||||
if (isEventHandled(ev, 'AttachmentImage.onClickUnlink')) {
|
||||
return;
|
||||
}
|
||||
if (!this.attachment || !this.attachment.isViewable) {
|
||||
return;
|
||||
}
|
||||
@@ -26,8 +47,8 @@ registerModel({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickUnlink(ev) {
|
||||
ev.stopPropagation(); // prevents from opening viewer
|
||||
if (!this.attachment) {
|
||||
markEventHandled(ev, 'AttachmentImage.onClickUnlink');
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
if (this.attachmentList.composerViewOwner) {
|
||||
@@ -36,6 +57,16 @@ registerModel({
|
||||
this.update({ attachmentDeleteConfirmDialog: insertAndReplace() });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeHasDownloadButton() {
|
||||
if (!this.attachment || !this.attachmentList) {
|
||||
return clear();
|
||||
}
|
||||
return !this.attachmentList.composerViewOwner && !this.attachment.isUploading;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {number}
|
||||
@@ -99,6 +130,13 @@ registerModel({
|
||||
readonly: true,
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
* Determines whether `this` should display a download button.
|
||||
*/
|
||||
hasDownloadButton: attr({
|
||||
compute: '_computeHasDownloadButton',
|
||||
default: false,
|
||||
}),
|
||||
/**
|
||||
* Determines the max height of this attachment image in px.
|
||||
*/
|
||||
|
||||
@@ -698,6 +698,36 @@ QUnit.test('prevent attachment delete on non-authored message in channels', asyn
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('allow attachment image download on message', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({});
|
||||
const irAttachmentId1 = pyEnv['ir.attachment'].create({
|
||||
name: "Blah.jpg",
|
||||
mimetype: 'image/jpeg',
|
||||
});
|
||||
pyEnv['mail.message'].create({
|
||||
attachment_ids: [irAttachmentId1],
|
||||
body: '<p>Test</p>',
|
||||
model: 'mail.channel',
|
||||
res_id: mailChannelId1,
|
||||
});
|
||||
const { openDiscuss } = await start({
|
||||
discuss: {
|
||||
context: {
|
||||
active_id: mailChannelId1,
|
||||
},
|
||||
},
|
||||
});
|
||||
await openDiscuss();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_AttachmentImage_actionDownload',
|
||||
"should have download attachment button"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('subtype description should be displayed if it is different than body', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user