From 4f0c0fe65a3910a999f3a8a1a9dfb3497fac8a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 17 Nov 2015 10:38:10 +0100 Subject: [PATCH 01/29] Add static version of thumbnail uploader in register piece form --- .../ascribe_forms/form_register_piece.js | 85 ++++++++++++++----- js/components/ascribe_forms/property.js | 18 +++- 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index b09d29ac..da19ea96 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -8,6 +8,7 @@ import UserActions from '../../actions/user_actions'; import Form from './form'; import Property from './property'; import InputFineUploader from './input_fineuploader'; +import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button'; import ApiUrls from '../../constants/api_urls'; import AppConstants from '../../constants/application_constants'; @@ -48,7 +49,8 @@ let RegisterPieceForm = React.createClass({ getInitialState(){ return mergeOptions( { - isUploadReady: false + digitalWorkKeyReady: false, + thumbnailKeyReady: true }, UserStore.getState() ); @@ -67,30 +69,49 @@ let RegisterPieceForm = React.createClass({ this.setState(state); }, - setIsUploadReady(isReady) { - this.setState({ - isUploadReady: isReady - }); + /** + * This method is overloaded so that we can track the ready-state + * of each uploader in the component + * @param {string} uploaderKey Name of the uploader's key to track + */ + setIsUploadReady(uploaderKey) { + return (isUploadReady) => { + this.setState({ + [uploaderKey]: isUploadReady + }); + }; }, render() { - let currentUser = this.state.currentUser; - let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; - enableLocalHashing = enableLocalHashing && this.props.enableLocalHashing; + const { disabled, + handleSuccess, + submitMessage, + headerMessage, + isFineUploaderActive, + onLoggedOut, + isFineUploaderEditable, + location, + children, + enableLocalHashing } = this.props; + const { currentUser, + isUploadReady } = this.state; + + const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; + const hashLocally = profileHashLocally && enableLocalHashing; return (
- {this.props.submitMessage} + disabled={!isUploadReady || disabled}> + {submitMessage} } spinner={ @@ -99,7 +120,7 @@ let RegisterPieceForm = React.createClass({ }>
-

{this.props.headerMessage}

+

{headerMessage}

+ isFineUploaderActive={isFineUploaderActive} + onLoggedOut={onLoggedOut} + disabled={!isFineUploaderEditable} + enableLocalHashing={hashLocally} + uploadMethod={location.query.method} /> + + + - {this.props.children} + {children}
); } diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index ac272988..d0c22dc2 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -220,6 +220,19 @@ let Property = React.createClass({ }); }, + getLabelAndErrors() { + if(this.props.label || this.state.errors) { + return ( +

+ {this.props.label} + {this.state.errors} +

+ ); + } else { + return null; + } + }, + render() { let footer = null; let tooltip = ; @@ -253,10 +266,7 @@ let Property = React.createClass({ placement="top" overlay={tooltip}>
-

- {this.props.label} - {this.state.errors} -

+ {this.getLabelAndErrors()} {this.renderChildren(style)} {footer}
From 14621b5b388532e447511b277aed93102e37132a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 17 Nov 2015 11:22:24 +0100 Subject: [PATCH 02/29] Generalize UploadButton styling --- .../ascribe_forms/form_register_piece.js | 2 +- .../contract_settings_update_button.js | 2 +- .../ascribe_upload_button/upload_button.js | 217 +++++++++--------- .../pr_forms/pr_register_piece_form.js | 8 +- 4 files changed, 116 insertions(+), 113 deletions(-) diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index da19ea96..37712c16 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -145,7 +145,7 @@ let RegisterPieceForm = React.createClass({ file.status === 'uploading'); - }, + if(typeof this.props.onDrop === 'function' && files) { + this.props.onDrop(files); + } - getUploadedFile() { - return this.props.filesToUpload.filter((file) => file.status === 'upload successful')[0]; - }, + }, - handleOnClick() { - const uploadingFiles = this.getUploadingFiles(); - const uploadedFile = this.getUploadedFile(); + getUploadingFiles() { + return this.props.filesToUpload.filter((file) => file.status === 'uploading'); + }, - if(uploadedFile) { - this.props.handleCancelFile(uploadedFile.id); - } - if(uploadingFiles.length === 0) { - // We only want the button to be clickable if there are no files currently uploading + getUploadedFile() { + return this.props.filesToUpload.filter((file) => file.status === 'upload successful')[0]; + }, - // Firefox only recognizes the simulated mouse click if bubbles is set to true, - // but since Google Chrome propagates the event much further than needed, we - // need to stop propagation as soon as the event is created - var evt = new MouseEvent('click', { - view: window, - bubbles: true, - cancelable: true - }); + handleOnClick() { + const uploadingFiles = this.getUploadingFiles(); + const uploadedFile = this.getUploadedFile(); - evt.stopPropagation(); - this.refs.fileinput.getDOMNode().dispatchEvent(evt); - } - }, + if(uploadedFile) { + this.props.handleCancelFile(uploadedFile.id); + } + if(uploadingFiles.length === 0) { + // We only want the button to be clickable if there are no files currently uploading - getButtonLabel() { - let { filesToUpload, fileClassToUpload } = this.props; + // Firefox only recognizes the simulated mouse click if bubbles is set to true, + // but since Google Chrome propagates the event much further than needed, we + // need to stop propagation as soon as the event is created + var evt = new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: true + }); - // filter invalid files that might have been deleted or canceled... - filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter); + evt.stopPropagation(); + this.refs.fileinput.getDOMNode().dispatchEvent(evt); + } + }, - if(this.getUploadingFiles().length !== 0) { - return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; - } else { - return fileClassToUpload.singular; - } - }, + getButtonLabel() { + let { filesToUpload, fileClassToUpload } = this.props; - getUploadedFileLabel() { - const uploadedFile = this.getUploadedFile(); + // filter invalid files that might have been deleted or canceled... + filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter); - if(uploadedFile) { + if(this.getUploadingFiles().length !== 0) { + return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; + } else { + return fileClassToUpload.singular; + } + }, + + getUploadedFileLabel() { + const uploadedFile = this.getUploadedFile(); + + if(uploadedFile) { + return ( + + + {' ' + truncateTextAtCharIndex(uploadedFile.name, 40)} + + ); + } else { + return ( + {getLangText('No file chosen')} + ); + } + }, + + render() { + let { multiple, + allowedExtensions } = this.props; + + /* + * We do not want a button that submits here. + * As UploadButton could be used in forms that want to be submitted independent + * of clicking the selector. + * Therefore the wrapping component needs to be an `anchor` tag instead of a `button` + */ return ( - - - {' ' + truncateTextAtCharIndex(uploadedFile.name, 40)} - - ); - } else { - return ( - {getLangText('No file chosen')} +
+ + {this.getButtonLabel()} + + + {this.getUploadedFileLabel()} +
); } - }, - - render() { - let { multiple, - allowedExtensions } = this.props; - - /* - * We do not want a button that submits here. - * As UploadButton could be used in forms that want to be submitted independent - * of clicking the selector. - * Therefore the wrapping component needs to be an `anchor` tag instead of a `button` - */ - return ( -
- - {this.getButtonLabel()} - - - {this.getUploadedFileLabel()} -
- ); - } -}); - -export default UploadButton; \ No newline at end of file + }); +} \ No newline at end of file diff --git a/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js index 0c293b15..41f2c25a 100644 --- a/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js +++ b/js/components/whitelabel/prize/portfolioreview/components/pr_forms/pr_register_piece_form.js @@ -253,7 +253,7 @@ const PRRegisterPieceForm = React.createClass({ name="digitalWorkKey" label={getLangText('Select the PDF with your work')}> Date: Tue, 17 Nov 2015 11:57:18 +0100 Subject: [PATCH 03/29] Generalize InputFineuploader's submitFileName method to submitFile --- .../ascribe_forms/form_create_contract.js | 6 +++--- .../ascribe_forms/form_register_piece.js | 5 +++-- .../ascribe_forms/input_fineuploader.js | 18 +++++++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index aac4c5ea..a409efd4 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -51,9 +51,9 @@ let CreateContractForm = React.createClass({ this.refs.form.reset(); }, - submitFileName(fileName) { + submitFile({ originalName }) { this.setState({ - contractName: fileName + contractName: originalName }); this.refs.form.submit(); @@ -69,7 +69,7 @@ let CreateContractForm = React.createClass({ name="blob" label={getLangText('Contract file (*.pdf only, max. 50MB per contract)')}> + disabled={!(digitalWorkKeyReady && thumbnailKeyReady)}> {submitMessage} } diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js index 948521c0..cb9995fd 100644 --- a/js/components/ascribe_forms/input_fineuploader.js +++ b/js/components/ascribe_forms/input_fineuploader.js @@ -10,13 +10,13 @@ import AppConstants from '../../constants/application_constants'; import { getCookie } from '../../utils/fetch_api_utils'; -const { func, bool, object, shape, string, number, arrayOf } = React.PropTypes; +const { func, bool, shape, string, number, arrayOf } = React.PropTypes; const InputFineUploader = React.createClass({ propTypes: { setIsUploadReady: func, isReadyForFormSubmission: func, - submitFileName: func, + submitFile: func, fileInputElement: func, areAssetsDownloadable: bool, @@ -77,8 +77,8 @@ const InputFineUploader = React.createClass({ this.props.onChange({ target: { value: this.state.value } }); } - if(typeof this.props.submitFileName === 'function') { - this.props.submitFileName(file.originalName); + if(typeof this.props.submitFile === 'function') { + this.props.submitFile(file); } }, @@ -104,7 +104,7 @@ const InputFineUploader = React.createClass({ onLoggedOut, enableLocalHashing, fileClassToUpload, - location } = this.props; + uploadMethod } = this.props; let editable = this.props.isFineUploaderActive; // if disabled is actually set by property, we want to override @@ -139,10 +139,10 @@ const InputFineUploader = React.createClass({ 'X-CSRFToken': getCookie(AppConstants.csrftoken) } }} - onInactive={this.props.onLoggedOut} - enableLocalHashing={this.props.enableLocalHashing} - uploadMethod={this.props.uploadMethod} - fileClassToUpload={this.props.fileClassToUpload} /> + onInactive={onLoggedOut} + enableLocalHashing={enableLocalHashing} + uploadMethod={uploadMethod} + fileClassToUpload={fileClassToUpload} /> ); } }); From a8ccf13f917563dd8ed32a6b8c3c47b5c3970477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 17 Nov 2015 12:03:22 +0100 Subject: [PATCH 04/29] Remove uploadStarted prop from ReactS3Fineuploader as it was replaced with setIsUploadReady --- .../ascribe_detail/further_details_fileuploader.js | 2 -- js/components/ascribe_uploader/react_s3_fine_uploader.js | 6 ------ .../cyland/cyland_forms/cyland_additional_data_form.js | 7 ------- 3 files changed, 15 deletions(-) diff --git a/js/components/ascribe_detail/further_details_fileuploader.js b/js/components/ascribe_detail/further_details_fileuploader.js index 9a1f091c..5c9a70d0 100644 --- a/js/components/ascribe_detail/further_details_fileuploader.js +++ b/js/components/ascribe_detail/further_details_fileuploader.js @@ -13,7 +13,6 @@ import { getCookie } from '../../utils/fetch_api_utils'; let FurtherDetailsFileuploader = React.createClass({ propTypes: { - uploadStarted: React.PropTypes.func, pieceId: React.PropTypes.number, otherData: React.PropTypes.arrayOf(React.PropTypes.object), setIsUploadReady: React.PropTypes.func, @@ -46,7 +45,6 @@ let FurtherDetailsFileuploader = React.createClass({ name="other_data_key" label="Additional files"> 0) { - this.props.uploadStarted(); - } - // if multiple is set to false and user drops multiple files into the dropzone, // take the first one and notify user that only one file can be submitted if(!this.props.multiple && files.length > 1) { diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js b/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js index a7631d95..7ef74caa 100644 --- a/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js +++ b/js/components/whitelabel/wallet/components/cyland/cyland_forms/cyland_additional_data_form.js @@ -64,12 +64,6 @@ let CylandAdditionalDataForm = React.createClass({ }, - uploadStarted() { - this.setState({ - isUploadReady: false - }); - }, - setIsUploadReady(isReady) { this.setState({ isUploadReady: isReady @@ -184,7 +178,6 @@ let CylandAdditionalDataForm = React.createClass({
Date: Tue, 17 Nov 2015 13:37:01 +0100 Subject: [PATCH 05/29] Use destructuring for ReactS3Fineuploader props --- .../react_s3_fine_uploader.js | 153 ++++++++++-------- 1 file changed, 83 insertions(+), 70 deletions(-) diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index fc4bc05b..0c440908 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -18,87 +18,100 @@ import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp } import { getCookie } from '../../utils/fetch_api_utils'; import { getLangText } from '../../utils/lang_utils'; -let ReactS3FineUploader = React.createClass({ + +const { shape, + string, + oneOfType, + number, + func, + bool, + any, + object, + oneOf, + element, + arrayOf } = React.PropTypes; + +const ReactS3FineUploader = React.createClass({ propTypes: { - keyRoutine: React.PropTypes.shape({ - url: React.PropTypes.string, - fileClass: React.PropTypes.string, - pieceId: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number + keyRoutine: shape({ + url: string, + fileClass: string, + pieceId: oneOfType([ + string, + number ]) }), - createBlobRoutine: React.PropTypes.shape({ - url: React.PropTypes.string, - pieceId: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number + createBlobRoutine: shape({ + url: string, + pieceId: oneOfType([ + string, + number ]) }), - submitFile: React.PropTypes.func, - autoUpload: React.PropTypes.bool, - debug: React.PropTypes.bool, - objectProperties: React.PropTypes.shape({ - acl: React.PropTypes.string + submitFile: func, + autoUpload: bool, + debug: bool, + objectProperties: shape({ + acl: string }), - request: React.PropTypes.shape({ - endpoint: React.PropTypes.string, - accessKey: React.PropTypes.string, - params: React.PropTypes.shape({ - csrfmiddlewaretoken: React.PropTypes.string + request: shape({ + endpoint: string, + accessKey: string, + params: shape({ + csrfmiddlewaretoken: string }) }), - signature: React.PropTypes.shape({ - endpoint: React.PropTypes.string + signature: shape({ + endpoint: string }).isRequired, - uploadSuccess: React.PropTypes.shape({ - method: React.PropTypes.string, - endpoint: React.PropTypes.string, - params: React.PropTypes.shape({ - isBrowserPreviewCapable: React.PropTypes.any, // maybe fix this later - bitcoin_ID_noPrefix: React.PropTypes.string + uploadSuccess: shape({ + method: string, + endpoint: string, + params: shape({ + isBrowserPreviewCapable: any, // maybe fix this later + bitcoin_ID_noPrefix: string }) }), - cors: React.PropTypes.shape({ - expected: React.PropTypes.bool + cors: shape({ + expected: bool }), - chunking: React.PropTypes.shape({ - enabled: React.PropTypes.bool + chunking: shape({ + enabled: bool }), - resume: React.PropTypes.shape({ - enabled: React.PropTypes.bool + resume: shape({ + enabled: bool }), - deleteFile: React.PropTypes.shape({ - enabled: React.PropTypes.bool, - method: React.PropTypes.string, - endpoint: React.PropTypes.string, - customHeaders: React.PropTypes.object + deleteFile: shape({ + enabled: bool, + method: string, + endpoint: string, + customHeaders: object }).isRequired, - session: React.PropTypes.shape({ - customHeaders: React.PropTypes.object, - endpoint: React.PropTypes.string, - params: React.PropTypes.object, - refreshOnRequests: React.PropTypes.bool + session: shape({ + customHeaders: object, + endpoint: string, + params: object, + refreshOnRequests: bool }), - validation: React.PropTypes.shape({ - itemLimit: React.PropTypes.number, - sizeLimit: React.PropTypes.string, - allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) + validation: shape({ + itemLimit: number, + sizeLimit: string, + allowedExtensions: arrayOf(string) }), - messages: React.PropTypes.shape({ - unsupportedBrowser: React.PropTypes.string + messages: shape({ + unsupportedBrowser: string }), - formatFileName: React.PropTypes.func, - multiple: React.PropTypes.bool, - retry: React.PropTypes.shape({ - enableAuto: React.PropTypes.bool + formatFileName: func, + multiple: bool, + retry: shape({ + enableAuto: bool }), - setIsUploadReady: React.PropTypes.func, - isReadyForFormSubmission: React.PropTypes.func, - areAssetsDownloadable: React.PropTypes.bool, - areAssetsEditable: React.PropTypes.bool, - defaultErrorMessage: React.PropTypes.string, - onInactive: React.PropTypes.func, + setIsUploadReady: func, + isReadyForFormSubmission: func, + areAssetsDownloadable: bool, + areAssetsEditable: bool, + defaultErrorMessage: string, + onInactive: func, // We encountered some cases where people had difficulties to upload their // works to ascribe due to a slow internet connection. @@ -111,22 +124,22 @@ let ReactS3FineUploader = React.createClass({ // which should be passed into 'uploadMethod': // 'hash': upload using the hash // 'upload': upload full file (default if not specified) - enableLocalHashing: React.PropTypes.bool, - uploadMethod: React.PropTypes.oneOf(['hash', 'upload']), + enableLocalHashing: bool, + uploadMethod: oneOf(['hash', 'upload']), // A class of a file the user has to upload // Needs to be defined both in singular as well as in plural - fileClassToUpload: React.PropTypes.shape({ - singular: React.PropTypes.string, - plural: React.PropTypes.string + fileClassToUpload: shape({ + singular: string, + plural: string }), // Uploading functionality of react fineuploader is disconnected from its UI // layer, which means that literally every (properly adjusted) react element // can handle the UI handling. - fileInputElement: React.PropTypes.oneOfType([ - React.PropTypes.func, - React.PropTypes.element + fileInputElement: oneOfType([ + func, + element ]) }, From 971d42b36d59359ea8eee7bed71d0c0692e01b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 17 Nov 2015 15:46:46 +0100 Subject: [PATCH 06/29] Add file-format-specific call to action for thumbnail generation --- .../ascribe_forms/form_register_piece.js | 29 ++++++-- .../ascribe_forms/input_fineuploader.js | 9 ++- js/components/ascribe_forms/property.js | 70 +++++++++---------- .../react_s3_fine_uploader.js | 47 ++++++++++--- js/constants/application_constants.js | 3 + sass/ascribe_panel.scss | 5 ++ 6 files changed, 106 insertions(+), 57 deletions(-) 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; From 1ac2fe7f9694254c75f0cff46222d895d97dd791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 17 Nov 2015 15:52:40 +0100 Subject: [PATCH 07/29] Remove all occurences of 'hidden' prop usage for Property --- .../ascribe_detail/edition_action_panel.js | 4 ++-- .../ascribe_forms/form_create_contract.js | 4 ++-- js/components/ascribe_forms/form_loan.js | 12 ++++++------ .../cyland_forms/cyland_additional_data_form.js | 14 +++++++------- .../ikonotv_forms/ikonotv_artist_details_form.js | 8 ++++---- .../ikonotv_forms/ikonotv_artwork_details_form.js | 12 ++++++------ 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/js/components/ascribe_detail/edition_action_panel.js b/js/components/ascribe_detail/edition_action_panel.js index a2ad1e58..2dda618a 100644 --- a/js/components/ascribe_detail/edition_action_panel.js +++ b/js/components/ascribe_detail/edition_action_panel.js @@ -119,7 +119,7 @@ let EditionActionPanel = React.createClass({ isInline={true}>