summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/js/composers/basic_composer.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/mail/static/src/js/composers/basic_composer.js')
-rw-r--r--addons/mail/static/src/js/composers/basic_composer.js187
1 files changed, 161 insertions, 26 deletions
diff --git a/addons/mail/static/src/js/composers/basic_composer.js b/addons/mail/static/src/js/composers/basic_composer.js
index 8a533cd4515..c8f7d702af4 100644
--- a/addons/mail/static/src/js/composers/basic_composer.js
+++ b/addons/mail/static/src/js/composers/basic_composer.js
@@ -28,6 +28,8 @@ var BasicComposer = Widget.extend({
'focusout .o_composer_button_emoji': '_onEmojiButtonFocusout',
'click .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageClick',
'focus .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageFocus',
+ 'dragover .o_file_drop_zone_container': '_onFileDragover',
+ 'drop .o_file_drop_zone_container': '_onFileDrop',
'input .o_input': '_onInput',
'keydown .o_composer_input textarea': '_onKeydown',
'keyup .o_composer_input': '_onKeyup',
@@ -124,9 +126,19 @@ var BasicComposer = Widget.extend({
// Mention
this._mentionManager.prependTo(this.$('.o_composer'));
+ // Drag-Drop files
+ // Allowing body to detect dragenter and dragleave for display
+ var $body = $('body');
+ this._dropZoneNS = _.uniqueId('o_dz_'); // For event namespace used when multiple chat window is open
+ $body.on('dragleave.' + this._dropZoneNS, this._onBodyFileDragLeave.bind(this));
+ $body.on("dragover." + this._dropZoneNS, this._onBodyFileDragover.bind(this));
+ $body.on("drop." + this._dropZoneNS, this._onBodyFileDrop.bind(this));
return this._super();
},
destroy: function () {
+ $("body").off('dragleave.' + this._dropZoneNS);
+ $("body").off('dragover.' + this._dropZoneNS);
+ $("body").off('drop.' + this._dropZoneNS);
$(window).off(this.fileuploadID);
return this._super.apply(this, arguments);
},
@@ -254,6 +266,24 @@ var BasicComposer = Widget.extend({
this._$emojisContainer.remove();
},
/**
+ * Making sure that dragging content is external files.
+ * Ignoring other content draging like text.
+ *
+ * @private
+ * @param {DataTransfer} dataTransfer
+ * @returns {boolean}
+ */
+ _isDragSourceExternalFile: function (dataTransfer) {
+ var DragDataType = dataTransfer.types;
+ if (DragDataType.constructor === DOMStringList) {
+ return DragDataType.contains('Files');
+ }
+ if (DragDataType.constructor === Array) {
+ return DragDataType.indexOf('Files') !== -1;
+ }
+ return false;
+ },
+ /**
* @private
* @param {string} search
* @returns {$.Deferred<Object[]>}
@@ -378,6 +408,67 @@ var BasicComposer = Widget.extend({
});
},
/**
+ * Allowing to upload attachment with file selector as well drag drop feature.
+ *
+ * @private
+ * @param {Array<File>} params.files
+ * @param {boolean} params.submitForm [optional]
+ */
+ _processAttachmentChange: function (params) {
+ var self = this,
+ attachments = this.get('attachment_ids'),
+ files = params.files,
+ submitForm = params.submitForm;
+ _.each(files, function (file) {
+ var attachment = _.findWhere(attachments, {
+ name: file.name,
+ size: file.size
+ });
+ // if the files already exits, delete the file before upload
+ if (attachment) {
+ self._attachmentDataSet.unlink([attachment.id]);
+ attachments = _.without(attachments, attachment);
+ }
+ });
+ var $form = this.$('form.o_form_binary_form');
+ if (submitForm) {
+ $form.submit();
+ this._$attachmentButton.prop('disabled', true);
+ } else {
+ var data = new FormData($form[0]);
+ _.each(files, function (file) {
+ // removing existing key with blank data and appending again with file info
+ // In safari, existing key will not be updated when append with new file.
+ data.delete("ufile");
+ data.append("ufile", file, file.name);
+ $.ajax({
+ url: $form.attr("action"),
+ type: "POST",
+ enctype: 'multipart/form-data',
+ processData: false,
+ contentType: false,
+ data: data,
+ success: function (result) {
+ var $el = $(result);
+ $.globalEval($el.contents().text());
+ }
+ });
+ });
+ }
+ var uploadAttachments = _.map(files, function (file){
+ return {
+ id: 0,
+ name: file.name,
+ filename: file.name,
+ url: '',
+ upload: true,
+ mimetype: '',
+ };
+ });
+ attachments = attachments.concat(uploadAttachments);
+ this.set('attachment_ids', attachments);
+ },
+ /**
* @private
*/
_renderAttachments: function () {
@@ -419,33 +510,10 @@ var BasicComposer = Widget.extend({
* @param {jQuery.Event} ev
*/
_onAttachmentChange: function (ev) {
- var self = this;
- var files = ev.target.files;
- var attachments = this.get('attachment_ids');
-
- _.each(files, function (file){
- var attachment = _.findWhere(attachments, {name: file.name});
- // if the files already exits, delete the file before upload
- if (attachment){
- self._attachmentDataSet.unlink([attachment.id]);
- attachments = _.without(attachments, attachment);
- }
- });
-
- this.$('form.o_form_binary_form').submit();
- this._$attachmentButton.prop('disabled', true);
- var uploadAttachments = _.map(files, function (file){
- return {
- id: 0,
- name: file.name,
- filename: file.name,
- url: '',
- upload: true,
- mimetype: '',
- };
+ this._processAttachmentChange({
+ files: ev.currentTarget.files,
+ submitForm: true
});
- attachments = attachments.concat(uploadAttachments);
- this.set('attachment_ids', attachments);
},
/**
* @private
@@ -497,6 +565,7 @@ var BasicComposer = Widget.extend({
name: file.name || file.filename,
filename: file.filename,
mimetype: file.mimetype,
+ size: file.size,
url: session.url('/web/content', { id: file.id, download: true }),
});
}
@@ -517,6 +586,46 @@ var BasicComposer = Widget.extend({
attachmentViewer.appendTo($('body'));
}
},
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onBodyFileDragLeave: function (ev) {
+ // On every dragenter chain created with parent child element
+ // That's why dragleave is fired every time when a child elemnt is hovered
+ // so here we hide dropzone based on mouse position
+ if (ev.originalEvent.clientX <= 0
+ || ev.originalEvent.clientY <= 0
+ || ev.originalEvent.clientX >= window.innerWidth
+ || ev.originalEvent.clientY >= window.innerHeight
+ ) {
+ this.$(".o_file_drop_zone_container").addClass("d-none");
+ }
+ },
+ /**
+ * When user start dragging on element drop area will be visible to drop selected files.
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onBodyFileDragover: function (ev) {
+ ev.preventDefault();
+ if (this._isDragSourceExternalFile(ev.originalEvent.dataTransfer)) {
+ this.$(".o_file_drop_zone_container").removeClass("d-none");
+ }
+ },
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onBodyFileDrop: function (ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ this.$(".o_file_drop_zone_container").addClass("d-none");
+ },
+ /**
+ * @private
+ */
_onClickAddAttachment: function () {
this.$('input.o_input_file').click();
this.$input.focus();
@@ -574,6 +683,32 @@ var BasicComposer = Widget.extend({
clearTimeout(this._hideEmojisTimeout);
},
/**
+ * Setting drop Effect to copy so when mouse pointer on dropzone
+ * cursor icon changed to copy ('+')
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onFileDragover: function (ev) {
+ ev.originalEvent.dataTransfer.dropEffect = "copy";
+ },
+ /**
+ * Called when user drop selected files on drop area
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onFileDrop: function (ev) {
+ ev.preventDefault();
+ // FIX: In case multiple chat windows are opened, and file droped in one of them
+ // at that time, other chat windows are still displaing drop areas so here hide them all with $ selector
+ $(".o_file_drop_zone_container").addClass("d-none");
+ if (this._isDragSourceExternalFile(ev.originalEvent.dataTransfer)) {
+ var files = ev.originalEvent.dataTransfer.files;
+ this._processAttachmentChange({ files: files });
+ }
+ },
+ /**
* Called when the input in the composer changes
*
* @private