diff options
Diffstat (limited to 'addons/mail/static/src/js/composers/basic_composer.js')
| -rw-r--r-- | addons/mail/static/src/js/composers/basic_composer.js | 187 |
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 |
