diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js index cfca4d56..1efe301c 100644 --- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js @@ -6,6 +6,7 @@ import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import FileDragAndDropDialog from './file_drag_and_drop_dialog'; import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator'; +import { FileStatus } from '../react_s3_fine_uploader_utils'; import { getLangText } from '../../../utils/lang_utils'; @@ -155,8 +156,16 @@ let FileDragAndDrop = React.createClass({ areAssetsEditable, allowedExtensions } = this.props; - // has files only is true if there are files that do not have the status deleted or canceled - let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; + // has files only is true if there are files that do not have the status deleted, canceled, or failed + let hasFiles = filesToUpload + .filter((file) => { + return file.status !== FileStatus.DELETED && + file.status !== FileStatus.CANCELED && + file.status !== FileStatus.UPLOAD_FAILED && + file.size !== -1; + }) + .length > 0; + let updatedClassName = hasFiles ? 'has-files ' : ''; updatedClassName += dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; updatedClassName += ' file-drag-and-drop'; diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js index ca1be2d2..f5986786 100644 --- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js @@ -5,6 +5,7 @@ import React from 'react'; import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image'; import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other'; +import { FileStatus } from '../react_s3_fine_uploader_utils'; import { getLangText } from '../../../utils/lang_utils'; import { truncateTextAtCharIndex } from '../../../utils/general_utils'; import { extractFileExtensionFromString } from '../../../utils/file_utils'; @@ -33,9 +34,9 @@ const FileDragAndDropPreview = React.createClass({ }, toggleUploadProcess() { - if(this.props.file.status === 'uploading') { + if(this.props.file.status === FileStatus.UPLOADING) { this.props.handlePauseFile(this.props.file.id); - } else if(this.props.file.status === 'paused') { + } else if(this.props.file.status === FileStatus.PAUSED) { this.props.handleResumeFile(this.props.file.id); } }, @@ -51,7 +52,7 @@ const FileDragAndDropPreview = React.createClass({ // deleted using an HTTP DELETE request. if (handleDeleteFile && file.progress === 100 && - (file.status === 'upload successful' || file.status === 'online') && + (file.status === FileStatus.UPLOAD_SUCCESSFUL || file.status === FileStatus.ONLINE) && file.s3UrlSafe) { handleDeleteFile(file.id); } else if(handleCancelFile) { diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index c5d2cb1c..562f9449 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -13,8 +13,8 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import AppConstants from '../../constants/application_constants'; +import { displayValidFilesFilter, FileStatus, transformAllowedExtensionsToInputAcceptProp } from './react_s3_fine_uploader_utils'; import { computeHashOfFile } from '../../utils/file_utils'; -import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp } from './react_s3_fine_uploader_utils'; import { getCookie } from '../../utils/fetch_api_utils'; import { getLangText } from '../../utils/lang_utils'; @@ -449,7 +449,7 @@ const ReactS3FineUploader = React.createClass({ // Set the state of the completed file to 'upload successful' in order to // remove it from the GUI - files[id].status = 'upload successful'; + files[id].status = FileStatus.UPLOAD_SUCCESSFUL; files[id].key = this.state.uploader.getKey(id); let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: files }); @@ -536,7 +536,7 @@ const ReactS3FineUploader = React.createClass({ onCancel(id) { // when a upload is canceled, we need to update this components file array - this.setStatusOfFile(id, 'canceled') + this.setStatusOfFile(id, FileStatus.CANCELED) .then(() => { if(typeof this.props.handleChangedFile === 'function') { this.props.handleChangedFile(this.state.filesToUpload[id]); @@ -576,7 +576,7 @@ const ReactS3FineUploader = React.createClass({ // fetch blobs for images response = response.map((file) => { file.url = file.s3UrlSafe; - file.status = 'online'; + file.status = FileStatus.ONLINE; file.progress = 100; return file; }); @@ -604,7 +604,7 @@ const ReactS3FineUploader = React.createClass({ onDeleteComplete(id, xhr, isError) { if(isError) { - this.setStatusOfFile(id, 'online'); + this.setStatusOfFile(id, FileStatus.ONLINE); let notification = new GlobalNotificationModel(getLangText('There was an error deleting your file.'), 'danger', 10000); GlobalNotificationActions.appendGlobalNotification(notification); @@ -633,9 +633,9 @@ const ReactS3FineUploader = React.createClass({ // We set the files state to 'deleted' immediately, so that the user is not confused with // the unresponsiveness of the UI // - // If there is an error during the deletion, we will just change the status back to 'online' + // If there is an error during the deletion, we will just change the status back to FileStatus.ONLINE // and display an error message - this.setStatusOfFile(fileId, 'deleted') + this.setStatusOfFile(fileId, FileStatus.DELETED) .then(() => { if(typeof this.props.handleChangedFile === 'function') { this.props.handleChangedFile(this.state.filesToUpload[fileId]); @@ -651,7 +651,7 @@ const ReactS3FineUploader = React.createClass({ // To check which files are already uploaded from previous sessions we check their status. // If they are, it is "online" - if(this.state.filesToUpload[fileId].status !== 'online') { + if(this.state.filesToUpload[fileId].status !== FileStatus.ONLINE) { // delete file from server this.state.uploader.deleteFile(fileId); // this is being continued in onDeleteFile, as @@ -672,7 +672,7 @@ const ReactS3FineUploader = React.createClass({ handlePauseFile(fileId) { if(this.state.uploader.pauseUpload(fileId)) { - this.setStatusOfFile(fileId, 'paused'); + this.setStatusOfFile(fileId, FileStatus.PAUSED); } else { throw new Error(getLangText('File upload could not be paused.')); } @@ -680,7 +680,7 @@ const ReactS3FineUploader = React.createClass({ handleResumeFile(fileId) { if(this.state.uploader.continueUpload(fileId)) { - this.setStatusOfFile(fileId, 'uploading'); + this.setStatusOfFile(fileId, FileStatus.UPLOADING); } else { throw new Error(getLangText('File upload could not be resumed.')); } @@ -860,12 +860,12 @@ const ReactS3FineUploader = React.createClass({ // // If the user deletes one of those files, then fineuploader will still keep it in his // files array but with key, progress undefined and size === -1 but - // status === 'upload successful'. + // status === FileStatus.UPLOAD_SUCCESSFUL. // This poses a problem as we depend on the amount of files that have - // status === 'upload successful', therefore once the file is synced, - // we need to tag its status as 'deleted' (which basically happens here) + // status === FileStatus.UPLOAD_SUCCESSFUL, therefore once the file is synced, + // we need to tag its status as FileStatus.DELETED (which basically happens here) if(oldAndNewFiles[i].size === -1 && (!oldAndNewFiles[i].progress || oldAndNewFiles[i].progress === 0)) { - oldAndNewFiles[i].status = 'deleted'; + oldAndNewFiles[i].status = FileStatus.DELETED; } if(oldAndNewFiles[i].originalName === oldFiles[j].name) { @@ -901,7 +901,7 @@ const ReactS3FineUploader = React.createClass({ return Q.Promise((resolve) => { let changeSet = {}; - if(status === 'deleted' || status === 'canceled') { + if (status === FileStatus.DELETED || status === FileStatus.CANCELED || status === FileStatus.UPLOAD_FAILED) { changeSet.progress = { $set: 0 }; } @@ -914,9 +914,19 @@ const ReactS3FineUploader = React.createClass({ }, isDropzoneInactive() { - const filesToDisplay = this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1); + const { areAssetsEditable, enableLocalHashing, multiple, showErrorStates, uploadMethod } = this.props; + const { errorState, filesToUpload } = this.state; - if ((this.props.enableLocalHashing && !this.props.uploadMethod) || !this.props.areAssetsEditable || !this.props.multiple && filesToDisplay.length > 0) { + const filesToDisplay = filesToUpload.filter((file) => { + return file.status !== FileStatus.DELETED && + file.status !== FileStatus.CANCELED && + file.status !== FileStatus.UPLOAD_FAILED && + file.size !== -1; + }); + + if ((enableLocalHashing && !uploadMethod) || !areAssetsEditable || + (showErrorStates && errorState.errorClass) || + (!multiple && filesToDisplay.length > 0)) { return true; } else { return false; diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js b/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js index ed76c5e8..2e0a046b 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js @@ -1,11 +1,18 @@ 'use strict'; +import fineUploader from 'fineUploader'; +// Re-export qq.status from FineUploader with an additional online +// state that we use to keep track of files from S3. +export const FileStatus = Object.assign(fineUploader.status, { + ONLINE: 'online' +}); + export const formSubmissionValidation = { /** * Returns a boolean if there has been at least one file uploaded * successfully without it being deleted or canceled. * @param {array of files} files provided by react fine uploader - * @return {boolean} + * @return {boolean} */ atLeastOneUploadedFile(files) { files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');