diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index 83484731..5c6ccf7e 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -7,6 +7,7 @@ import UserActions from '../../actions/user_actions'; import Form from './form'; import Property from './property'; +import PropertyCollapsible from './property_collapsible'; import InputFineUploader from './input_fineuploader'; import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button'; @@ -16,7 +17,7 @@ import AscribeSpinner from '../ascribe_spinner'; import { getLangText } from '../../utils/lang_utils'; import { mergeOptions } from '../../utils/general_utils'; -import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; +import { formSubmissionValidation, displayValidFilesFilter } from '../ascribe_uploader/react_s3_fine_uploader_utils'; let RegisterPieceForm = React.createClass({ @@ -50,7 +51,8 @@ let RegisterPieceForm = React.createClass({ return mergeOptions( { digitalWorkKeyReady: false, - thumbnailKeyReady: true + thumbnailKeyReady: true, + thumbnailKeyDialogExpanded: false }, UserStore.getState() ); @@ -82,6 +84,20 @@ let RegisterPieceForm = React.createClass({ }; }, + handleSelectFiles(files) { + const validFiles = files.filter(displayValidFilesFilter); + + if(validFiles.length > 0) { + const { type: fileType } = validFiles[0].type; + const fileExtension = fileType && fileType.split('/').length ? fileType.split('/')[1] + : 'unknown'; + const thumbnailKeyDialogExpanded = AppConstants.supportedThumbnailFileFormats.indexOf(fileExtension) === -1; + this.setState({ thumbnailKeyDialogExpanded }); + } else { + this.setState({ thumbnailKeyDialogExpanded: false }); + } + }, + render() { const { disabled, handleSuccess, @@ -95,7 +111,8 @@ let RegisterPieceForm = React.createClass({ enableLocalHashing } = this.props; const { currentUser, digitalWorkKeyReady, - thumbnailKeyReady } = this.state; + thumbnailKeyReady, + thumbnailKeyDialogExpanded } = this.state; const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; const hashLocally = profileHashLocally && enableLocalHashing; @@ -141,10 +158,12 @@ let RegisterPieceForm = React.createClass({ onLoggedOut={onLoggedOut} disabled={!isFineUploaderEditable} enableLocalHashing={hashLocally} - uploadMethod={location.query.method} /> + uploadMethod={location.query.method} + handleSelectFiles={this.handleSelectFiles}/> + name="thumbnail_file" + expanded={thumbnailKeyDialogExpanded}> + fileClassToUpload={fileClassToUpload} + handleSelectFiles={handleSelectFiles}/> ); } }); diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index d0c22dc2..a6a4fd16 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -3,52 +3,54 @@ import React from 'react'; import ReactAddons from 'react/addons'; -import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; -import Tooltip from 'react-bootstrap/lib/Tooltip'; +import Panel from 'react-bootstrap/lib/Panel'; import AppConstants from '../../constants/application_constants'; import { mergeOptions } from '../../utils/general_utils'; -let Property = React.createClass({ - propTypes: { - hidden: React.PropTypes.bool, +const { bool, element, string, oneOfType, func, object, arrayOf } = React.PropTypes; - editable: React.PropTypes.bool, +const Property = React.createClass({ + propTypes: { + hidden: bool, + + editable: bool, // If we want Form to have a different value for disabled as Property has one for // editable, we need to set overrideForm to true, as it will then override Form's // disabled value for individual Properties - overrideForm: React.PropTypes.bool, + overrideForm: bool, - tooltip: React.PropTypes.element, - label: React.PropTypes.string, - value: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.element + label: string, + value: oneOfType([ + string, + element ]), - footer: React.PropTypes.element, - handleChange: React.PropTypes.func, - ignoreFocus: React.PropTypes.bool, - name: React.PropTypes.string.isRequired, - className: React.PropTypes.string, + footer: element, + handleChange: func, + ignoreFocus: bool, + name: string.isRequired, + className: string, - onClick: React.PropTypes.func, - onChange: React.PropTypes.func, - onBlur: React.PropTypes.func, + onClick: func, + onChange: func, + onBlur: func, - children: React.PropTypes.oneOfType([ - React.PropTypes.arrayOf(React.PropTypes.element), - React.PropTypes.element + children: oneOfType([ + arrayOf(element), + element ]), - style: React.PropTypes.object + style: object, + expanded: bool }, getDefaultProps() { return { editable: true, hidden: false, + expanded: true, className: '' }; }, @@ -190,7 +192,7 @@ let Property = React.createClass({ }, getClassName() { - if(this.props.hidden){ + if(this.props.hidden || !this.props.expanded){ return 'is-hidden'; } if(!this.props.editable){ @@ -235,16 +237,8 @@ let Property = React.createClass({ render() { let footer = null; - let tooltip = ; let style = this.props.style ? mergeOptions({}, this.props.style) : {}; - if(this.props.tooltip){ - tooltip = ( - - {this.props.tooltip} - ); - } - if(this.props.footer){ footer = (
@@ -261,16 +255,16 @@ let Property = React.createClass({ className={'ascribe-property-wrapper ' + this.getClassName()} onClick={this.handleFocus} style={style}> - +
{this.getLabelAndErrors()} {this.renderChildren(style)} {footer}
-
+
); } diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 0c440908..8c1ddef0 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -48,7 +48,8 @@ const ReactS3FineUploader = React.createClass({ number ]) }), - submitFile: func, + handleSelectFiles: func, // is for when a file is dropped or selected + submitFile: func, // is for when a file has been successfully uploaded, TODO: rename to handleSubmitFile autoUpload: bool, debug: bool, objectProperties: shape({ @@ -522,7 +523,12 @@ 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, 'canceled') + .then(() => { + if(typeof this.props.handleSelectFiles === 'function') { + this.props.handleSelectFiles(this.state.filesToUpload); + } + }); let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000); GlobalNotificationActions.appendGlobalNotification(notification); @@ -616,7 +622,12 @@ const ReactS3FineUploader = React.createClass({ // // If there is an error during the deletion, we will just change the status back to 'online' // and display an error message - this.setStatusOfFile(fileId, 'deleted'); + this.setStatusOfFile(fileId, 'deleted') + .then(() => { + if(typeof this.props.handleSelectFiles === 'function') { + this.props.handleSelectFiles(this.state.filesToUpload); + } + }); // In some instances (when the file was already uploaded and is just displayed to the user // - for example in the contract or additional files dialog) @@ -856,21 +867,35 @@ const ReactS3FineUploader = React.createClass({ // set the new file array let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: oldAndNewFiles }); - this.setState({ filesToUpload }); + this.setState({ filesToUpload }, () => { + // when files have been dropped or selected by a user, we want to propagate that + // 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); + } + }); }, + // This method has been made promise-based to immediately afterwards + // call a callback function (instantly after this.setState went through) + // This is e.g. needed when showing/hiding the optional thumbnail upload + // field in the registration form setStatusOfFile(fileId, status) { - let changeSet = {}; + return Q.Promise((resolve) => { + let changeSet = {}; - if(status === 'deleted' || status === 'canceled') { - changeSet.progress = { $set: 0 }; - } + if(status === 'deleted' || status === 'canceled') { + changeSet.progress = { $set: 0 }; + } - changeSet.status = { $set: status }; + changeSet.status = { $set: status }; - let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet }); + let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet }); - this.setState({ filesToUpload }); + this.setState({ filesToUpload }, resolve); + }); }, isDropzoneInactive() { diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js index a58a8cc6..061eb9db 100644 --- a/js/constants/application_constants.js +++ b/js/constants/application_constants.js @@ -85,8 +85,11 @@ const constants = { 'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART', 'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV', 'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'], + 'searchThreshold': 500, + 'supportedThumbnailFileFormats': ['png', 'jpg', 'jpeg', 'gif', 'mp4'], + // in case of whitelabel customization, we store stuff here 'whitelabel': {}, diff --git a/sass/ascribe_panel.scss b/sass/ascribe_panel.scss index 09d773ad..0f675605 100644 --- a/sass/ascribe_panel.scss +++ b/sass/ascribe_panel.scss @@ -1,3 +1,8 @@ +.panel { + /* Here we are overriding bootstrap to show the is-focused background color */ + background-color: transparent; +} + .ascribe-panel-wrapper { border: 1px solid #ddd; height: 5em;