summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components/file_uploader/file_uploader.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/file_uploader/file_uploader.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/file_uploader/file_uploader.js')
-rw-r--r--addons/mail/static/src/components/file_uploader/file_uploader.js241
1 files changed, 241 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/file_uploader/file_uploader.js b/addons/mail/static/src/components/file_uploader/file_uploader.js
new file mode 100644
index 00000000..4e57eadd
--- /dev/null
+++ b/addons/mail/static/src/components/file_uploader/file_uploader.js
@@ -0,0 +1,241 @@
+odoo.define('mail/static/src/components/file_uploader/file_uploader.js', function (require) {
+'use strict';
+
+const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js');
+
+const core = require('web.core');
+
+const { Component } = owl;
+const { useRef } = owl.hooks;
+
+class FileUploader extends Component {
+
+ /**
+ * @override
+ */
+ constructor(...args) {
+ super(...args);
+ this._fileInputRef = useRef('fileInput');
+ this._fileUploadId = _.uniqueId('o_FileUploader_fileupload');
+ this._onAttachmentUploaded = this._onAttachmentUploaded.bind(this);
+ useShouldUpdateBasedOnProps({
+ compareDepth: {
+ attachmentLocalIds: 1,
+ newAttachmentExtraData: 3,
+ },
+ });
+ }
+
+ mounted() {
+ $(window).on(this._fileUploadId, this._onAttachmentUploaded);
+ }
+
+ willUnmount() {
+ $(window).off(this._fileUploadId);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @param {FileList|Array} files
+ * @returns {Promise}
+ */
+ async uploadFiles(files) {
+ await this._unlinkExistingAttachments(files);
+ this._createTemporaryAttachments(files);
+ await this._performUpload(files);
+ this._fileInputRef.el.value = '';
+ }
+
+ openBrowserFileUploader() {
+ this._fileInputRef.el.click();
+ }
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @deprecated
+ * @private
+ * @param {Object} fileData
+ * @returns {mail.attachment}
+ */
+ _createAttachment(fileData) {
+ return this.env.models['mail.attachment'].create(Object.assign(
+ {},
+ fileData,
+ this.props.newAttachmentExtraData
+ ));
+ }
+
+ /**
+ * @private
+ * @param {File} file
+ * @returns {FormData}
+ */
+ _createFormData(file) {
+ let formData = new window.FormData();
+ formData.append('callback', this._fileUploadId);
+ formData.append('csrf_token', core.csrf_token);
+ formData.append('id', this.props.uploadId);
+ formData.append('model', this.props.uploadModel);
+ formData.append('ufile', file, file.name);
+ return formData;
+ }
+
+ /**
+ * @private
+ * @param {FileList|Array} files
+ */
+ _createTemporaryAttachments(files) {
+ for (const file of files) {
+ this.env.models['mail.attachment'].create(
+ Object.assign(
+ {
+ filename: file.name,
+ isTemporary: true,
+ name: file.name
+ },
+ this.props.newAttachmentExtraData
+ ),
+ );
+ }
+ }
+ /**
+ * @private
+ * @param {FileList|Array} files
+ * @returns {Promise}
+ */
+ async _performUpload(files) {
+ for (const file of files) {
+ const uploadingAttachment = this.env.models['mail.attachment'].find(attachment =>
+ attachment.isTemporary &&
+ attachment.filename === file.name
+ );
+ if (!uploadingAttachment) {
+ // Uploading attachment no longer exists.
+ // This happens when an uploading attachment is being deleted by user.
+ continue;
+ }
+ try {
+ const response = await this.env.browser.fetch('/web/binary/upload_attachment', {
+ method: 'POST',
+ body: this._createFormData(file),
+ signal: uploadingAttachment.uploadingAbortController.signal,
+ });
+ let html = await response.text();
+ const template = document.createElement('template');
+ template.innerHTML = html.trim();
+ window.eval(template.content.firstChild.textContent);
+ } catch (e) {
+ if (e.name !== 'AbortError') {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * @private
+ * @param {FileList|Array} files
+ * @returns {Promise}
+ */
+ async _unlinkExistingAttachments(files) {
+ for (const file of files) {
+ const attachment = this.props.attachmentLocalIds
+ .map(attachmentLocalId => this.env.models['mail.attachment'].get(attachmentLocalId))
+ .find(attachment => attachment.name === file.name && attachment.size === file.size);
+ // if the files already exits, delete the file before upload
+ if (attachment) {
+ attachment.remove();
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {jQuery.Event} ev
+ * @param {...Object} filesData
+ */
+ async _onAttachmentUploaded(ev, ...filesData) {
+ for (const fileData of filesData) {
+ const { error, filename, id, mimetype, name, size } = fileData;
+ if (error || !id) {
+ this.env.services['notification'].notify({
+ type: 'danger',
+ message: owl.utils.escape(error),
+ });
+ const relatedTemporaryAttachments = this.env.models['mail.attachment']
+ .find(attachment =>
+ attachment.filename === filename &&
+ attachment.isTemporary
+ );
+ for (const attachment of relatedTemporaryAttachments) {
+ attachment.delete();
+ }
+ return;
+ }
+ // FIXME : needed to avoid problems on uploading
+ // Without this the useStore selector of component could be not called
+ // E.g. in attachment_box_tests.js
+ await new Promise(resolve => setTimeout(resolve));
+ const attachment = this.env.models['mail.attachment'].insert(
+ Object.assign(
+ {
+ filename,
+ id,
+ mimetype,
+ name,
+ size,
+ },
+ this.props.newAttachmentExtraData
+ ),
+ );
+ this.trigger('o-attachment-created', { attachment });
+ }
+ }
+
+ /**
+ * Called when there are changes in the file input.
+ *
+ * @private
+ * @param {Event} ev
+ * @param {EventTarget} ev.target
+ * @param {FileList|Array} ev.target.files
+ */
+ async _onChangeAttachment(ev) {
+ await this.uploadFiles(ev.target.files);
+ }
+
+}
+
+Object.assign(FileUploader, {
+ defaultProps: {
+ uploadId: 0,
+ uploadModel: 'mail.compose.message'
+ },
+ props: {
+ attachmentLocalIds: {
+ type: Array,
+ element: String,
+ },
+ newAttachmentExtraData: {
+ type: Object,
+ optional: true,
+ },
+ uploadId: Number,
+ uploadModel: String,
+ },
+ template: 'mail.FileUploader',
+});
+
+return FileUploader;
+
+});