diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js index 0e3517a2..a0b2d251 100644 --- a/js/components/ascribe_forms/form.js +++ b/js/components/ascribe_forms/form.js @@ -238,7 +238,16 @@ let Form = React.createClass({ renderChildren() { return ReactAddons.Children.map(this.props.children, (child, i) => { if (child) { - return ReactAddons.addons.cloneWithProps(child, { + + // Since refs will be overwritten by this functions return statement, + // we still want to be able to define refs for nested `Form` or `Property` + // children, which is why we're upfront simply invoking the callback-ref- + // function before overwriting it. + if(typeof child.ref === 'function' && this.refs[child.props.name]) { + child.ref(this.refs[child.props.name]); + } + + return React.cloneElement(child, { handleChange: this.handleChangeChild, ref: child.props.name, key: i, diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index 76bbd68e..232ef444 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -17,7 +17,9 @@ import AscribeSpinner from '../ascribe_spinner'; import { getLangText } from '../../utils/lang_utils'; import { mergeOptions } from '../../utils/general_utils'; -import { formSubmissionValidation, displayValidFilesFilter } from '../ascribe_uploader/react_s3_fine_uploader_utils'; +import { formSubmissionValidation, + displayValidFilesFilter, + displayRemovedFilesFilter } from '../ascribe_uploader/react_s3_fine_uploader_utils'; let RegisterPieceForm = React.createClass({ @@ -50,9 +52,7 @@ let RegisterPieceForm = React.createClass({ getInitialState(){ return mergeOptions( { - digitalWorkKeyReady: false, - thumbnailKeyReady: true, - thumbnailKeyDialogExpanded: false + digitalWorkFile: null }, UserStore.getState() ); @@ -84,31 +84,34 @@ let RegisterPieceForm = React.createClass({ }; }, - handleSelectFiles(files) { - const validFiles = files.filter(displayValidFilesFilter); - - if(validFiles.length > 0) { - const { type: mimeType } = validFiles[0]; - const mimeSubType = mimeType && mimeType.split('/').length ? mimeType.split('/')[1] - : 'unknown'; - const thumbnailKeyDialogExpanded = AppConstants.supportedThumbnailFileFormats.indexOf(mimeSubType) === -1; - this.setState({ thumbnailKeyDialogExpanded }); - } else { - // Reset the thumbnail that has been set in `handleSelectFilesThumbnail` - let file = this.refs.form.refs.digital_work_key.refs.input.refs.fineuploader.state.filesToUpload[0]; - file.type = ''; - file.url = ''; - + handleChangedDigitalWork(digitalWorkFile) { + if (digitalWorkFile && + (digitalWorkFile.status === 'deleted' || digitalWorkFile.status === 'canceled')) { this.refs.form.refs.thumbnail_file.reset(); - this.setState({ thumbnailKeyDialogExpanded: false }); + this.setState({ digitalWorkFile: null }); + } else { + this.setState({ digitalWorkFile }); } }, - handleSelectFilesThumbnail([thumbnailFile, ]) { - // This is truly terrible, but at least we're not coding this mess into ReactS3Fineuploader - let file = this.refs.form.refs.digital_work_key.refs.input.refs.fineuploader.state.filesToUpload[0]; - file.type = thumbnailFile.type; - file.url = thumbnailFile.url; + handleChangedThumbnail(thumbnailFile) { + const { digitalWorkFile } = this.state; + const { fineuploader } = this.refs.digitalWorkFineUploader.refs; + + fineuploader.setThumbnailForFileId(digitalWorkFile.id, thumbnailFile.url); + }, + + isThumbnailDialogExpanded() { + const { digitalWorkFile } = this.state; + + if(digitalWorkFile) { + const { type: mimeType } = digitalWorkFile; + const mimeSubType = mimeType && mimeType.split('/').length ? mimeType.split('/')[1] + : 'unknown'; + return AppConstants.supportedThumbnailFileFormats.indexOf(mimeSubType) === -1; + } else { + return false; + } }, render() { @@ -122,8 +125,7 @@ let RegisterPieceForm = React.createClass({ location, children, enableLocalHashing } = this.props; - const { currentUser, - thumbnailKeyDialogExpanded } = this.state; + const { currentUser} = this.state; const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; const hashLocally = profileHashLocally && enableLocalHashing; @@ -155,8 +157,9 @@ let RegisterPieceForm = React.createClass({ + label={getLangText('Your Work')}> this.refs.digitalWorkFineUploader = ref} keyRoutine={{ url: AppConstants.serverUrl + 's3/key/', fileClass: 'digitalwork' @@ -172,17 +175,18 @@ let RegisterPieceForm = React.createClass({ disabled={!isFineUploaderEditable} enableLocalHashing={hashLocally} uploadMethod={location.query.method} - handleSelectFiles={this.handleSelectFiles}/> + handleChangedFile={this.handleChangedDigitalWork}/> + expanded={this.isThumbnailDialogExpanded()}> this.refs.thumbnailFineUploader = ref} fileInputElement={UploadButton({ className: 'btn btn-secondary btn-sm' })} createBlobRoutine={{ url: ApiUrls.blob_thumbnails }} - handleSelectFiles={this.handleSelectFilesThumbnail} + handleChangedFile={this.handleChangedThumbnail} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} keyRoutine={{ url: AppConstants.serverUrl + 's3/key/', diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js index 8d1ad70e..eb20fcd1 100644 --- a/js/components/ascribe_forms/input_fineuploader.js +++ b/js/components/ascribe_forms/input_fineuploader.js @@ -52,7 +52,7 @@ const InputFineUploader = React.createClass({ singular: string, plural: string }), - handleSelectFiles: func + handleChangedFile: func }, getDefaultProps() { @@ -106,7 +106,7 @@ const InputFineUploader = React.createClass({ enableLocalHashing, fileClassToUpload, uploadMethod, - handleSelectFiles } = this.props; + handleChangedFile } = this.props; let editable = this.props.isFineUploaderActive; // if disabled is actually set by property, we want to override @@ -145,7 +145,7 @@ const InputFineUploader = React.createClass({ enableLocalHashing={enableLocalHashing} uploadMethod={uploadMethod} fileClassToUpload={fileClassToUpload} - handleSelectFiles={handleSelectFiles}/> + handleChangedFile={handleChangedFile}/> ); } }); diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index 4d01790d..063d27dd 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -231,7 +231,16 @@ const Property = React.createClass({ // if the component is actually being shown (!== 'expanded === false') if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) { return ReactAddons.Children.map(this.props.children, (child) => { - return ReactAddons.addons.cloneWithProps(child, { + + // Since refs will be overriden by this functions return statement, + // we still want to be able to define refs for nested `Form` or `Property` + // children, which is why we're upfront simply invoking the callback-ref- + // function before overriding it. + if(typeof child.ref === 'function' && this.refs.input) { + child.ref(this.refs.input); + } + + return React.cloneElement(child, { style, onChange: this.handleChange, onFocus: this.handleFocus, 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 5528ec25..ca1be2d2 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 @@ -106,12 +106,13 @@ const FileDragAndDropPreview = React.createClass({ let previewElement; // Decide whether an image or a placeholder picture should be displayed - if(file.type.split('/')[0] === 'image') { + // If a file has its `thumbnailUrl` defined, then we display it also as an image + if(file.type.split('/')[0] === 'image' || file.thumbnailUrl) { previewElement = ( { - if(typeof this.props.handleSelectFiles === 'function') { - this.props.handleSelectFiles(this.state.filesToUpload); + if(typeof this.props.handleChangedFile === 'function') { + this.props.handleChangedFile(this.state.filesToUpload[id]); } }); @@ -624,8 +637,8 @@ const ReactS3FineUploader = React.createClass({ // and display an error message this.setStatusOfFile(fileId, 'deleted') .then(() => { - if(typeof this.props.handleSelectFiles === 'function') { - this.props.handleSelectFiles(this.state.filesToUpload); + if(typeof this.props.handleChangedFile === 'function') { + this.props.handleChangedFile(this.state.filesToUpload[fileId]); } }); @@ -872,8 +885,10 @@ const ReactS3FineUploader = React.createClass({ // information to the outside components, so they can act on it (in our case, because // we want the user to define a thumbnail when the actual work is not renderable // (like e.g. a .zip file)) - if(typeof this.props.handleSelectFiles === 'function') { - this.props.handleSelectFiles(this.state.filesToUpload); + if(typeof this.props.handleChangedFile === 'function') { + // its save to assume that the last file in `filesToUpload` is always + // the latest file added + this.props.handleChangedFile(this.state.filesToUpload.slice(-1)[0]); } }); }, 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 cd1dbce2..ed76c5e8 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js @@ -42,6 +42,15 @@ export function displayValidFilesFilter(file) { return file.status !== 'deleted' && file.status !== 'canceled'; } +/** + * Filter function for filtering all files except for deleted and canceled files + * @param {object} file A file from filesToUpload that has status as a prop. + * @return {boolean} + */ +export function displayRemovedFilesFilter(file) { + return file.status === 'deleted' || file.status === 'canceled'; +} + /** * Filter function for which files to integrate in the progress process