From 12e7c2c89809c2882f3cbe3cbfba561bf88367bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 10 Sep 2015 15:47:27 +0200 Subject: [PATCH 01/20] add download button to contract list --- .../ascribe_settings/contract_settings.js | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index 278c4c1b..06584a8d 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -105,6 +105,12 @@ let ContractSettings = React.createClass({ + + DOWNLOAD + - + + + DOWNLOAD + + } leftColumnWidth="40%" From c8ece4c532fe18be6d126f1fca23f9da02368ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Fri, 11 Sep 2015 10:11:07 +0200 Subject: [PATCH 02/20] refactor contract creation form --- .../ascribe_forms/form_create_contract.js | 44 +++---------------- .../ascribe_forms/form_register_piece.js | 16 ++++++- .../ascribe_forms/input_fineuploader.js | 26 ++++++----- 3 files changed, 35 insertions(+), 51 deletions(-) diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index 9cf97db7..d0e160a3 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -11,38 +11,23 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import ContractListActions from '../../actions/contract_list_actions'; -import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; - import AppConstants from '../../constants/application_constants'; import ApiUrls from '../../constants/api_urls'; - +import InputFineUploader from './input_fineuploader'; import { getLangText } from '../../utils/lang_utils'; -import { getCookie } from '../../utils/fetch_api_utils'; import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; + let CreateContractForm = React.createClass({ getInitialState() { return { - contractKey: null, isUploadReady: false }; }, - getFormData(){ - return { - blob: this.state.contractKey - }; - }, - - submitKey(key) { - this.setState({ - contractKey: key - }); - }, - setIsUploadReady(isReady) { this.setState({ isUploadReady: isReady @@ -62,7 +47,6 @@ let CreateContractForm = React.createClass({
}> - + isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} /> - Make contract public (this will replace the current public contract) + {getLangText('Make contract public (this will replace the current public contract')} diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index 8f2666c0..dab0b251 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -10,10 +10,11 @@ import Property from './property'; import InputFineUploader from './input_fineuploader'; import ApiUrls from '../../constants/api_urls'; +import AppConstants from '../../constants/application_constants'; import { getLangText } from '../../utils/lang_utils'; import { mergeOptions } from '../../utils/general_utils'; -import { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils'; +import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; let RegisterPieceForm = React.createClass({ @@ -99,8 +100,19 @@ let RegisterPieceForm = React.createClass({ name="digital_work_key" ignoreFocus={true}> Date: Mon, 14 Sep 2015 14:46:03 +0200 Subject: [PATCH 03/20] rename fineuploader's submitKey method to submitFile --- js/components/ascribe_detail/further_details.js | 6 +++--- .../ascribe_detail/further_details_fileuploader.js | 4 ++-- .../ascribe_forms/form_create_contract.js | 2 +- js/components/ascribe_forms/input_fineuploader.js | 6 +++--- .../ascribe_uploader/react_s3_fine_uploader.js | 14 +++++++------- .../ascribe_forms/cyland_additional_data_form.js | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/js/components/ascribe_detail/further_details.js b/js/components/ascribe_detail/further_details.js index 863ed491..db241f3d 100644 --- a/js/components/ascribe_detail/further_details.js +++ b/js/components/ascribe_detail/further_details.js @@ -38,9 +38,9 @@ let FurtherDetails = React.createClass({ GlobalNotificationActions.appendGlobalNotification(notification); }, - submitKey(key){ + submitFile(file){ this.setState({ - otherDataKey: key + otherDataKey: file.key }); }, @@ -78,7 +78,7 @@ let FurtherDetails = React.createClass({ extraData={this.props.extraData} /> diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js index ebbaccfc..3bef7ccd 100644 --- a/js/components/ascribe_forms/input_fineuploader.js +++ b/js/components/ascribe_forms/input_fineuploader.js @@ -43,9 +43,9 @@ let InputFileUploader = React.createClass({ }; }, - submitKey(key){ + submitFile(file){ this.setState({ - value: key + value: file.key }); }, @@ -69,7 +69,7 @@ let InputFileUploader = React.createClass({ keyRoutine={this.props.keyRoutine} createBlobRoutine={this.props.createBlobRoutine} validation={this.props.validation} - submitKey={this.submitKey} + submitFile={this.submitFile} setIsUploadReady={this.props.setIsUploadReady} isReadyForFormSubmission={this.props.isReadyForFormSubmission} areAssetsDownloadable={false} diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index fe6f2611..5b27cb90 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -36,7 +36,7 @@ var ReactS3FineUploader = React.createClass({ React.PropTypes.number ]) }), - submitKey: React.PropTypes.func, + submitFile: React.PropTypes.func, autoUpload: React.PropTypes.bool, debug: React.PropTypes.bool, objectProperties: React.PropTypes.shape({ @@ -393,12 +393,12 @@ var ReactS3FineUploader = React.createClass({ // Only after the blob has been created server-side, we can make the form submittable. this.createBlob(files[id]) .then(() => { - // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey + // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitFile // are optional, we'll only trigger them when they're actually defined - if(this.props.submitKey) { - this.props.submitKey(files[id].key); + if(this.props.submitFile) { + this.props.submitFile(files[id]); } else { - console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader'); + console.warn('You didn\'t define submitFile in as a prop in react-s3-fine-uploader'); } // for explanation, check comment of if statement above @@ -458,7 +458,7 @@ var ReactS3FineUploader = React.createClass({ let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000); GlobalNotificationActions.appendGlobalNotification(notification); - // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey + // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitFile // are optional, we'll only trigger them when they're actually defined if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) { if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) { @@ -524,7 +524,7 @@ var ReactS3FineUploader = React.createClass({ GlobalNotificationActions.appendGlobalNotification(notification); } - // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey + // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitFile // are optional, we'll only trigger them when they're actually defined if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) { // also, lets check if after the completion of this upload, diff --git a/js/components/whitelabel/wallet/components/cyland/ascribe_forms/cyland_additional_data_form.js b/js/components/whitelabel/wallet/components/cyland/ascribe_forms/cyland_additional_data_form.js index 13e731ef..123ad2b7 100644 --- a/js/components/whitelabel/wallet/components/cyland/ascribe_forms/cyland_additional_data_form.js +++ b/js/components/whitelabel/wallet/components/cyland/ascribe_forms/cyland_additional_data_form.js @@ -112,7 +112,7 @@ let CylandAdditionalDataForm = React.createClass({ Date: Mon, 14 Sep 2015 16:15:01 +0200 Subject: [PATCH 04/20] make contract creation implicit --- .../ascribe_forms/form_create_contract.js | 47 +++++++------------ .../ascribe_forms/input_fineuploader.js | 9 +++- .../react_s3_fine_uploader.js | 5 +- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index 7b71d293..614480a7 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -24,7 +24,8 @@ let CreateContractForm = React.createClass({ getInitialState() { return { - isUploadReady: false + isUploadReady: false, + contractName: '' }; }, @@ -41,30 +42,25 @@ let CreateContractForm = React.createClass({ this.refs.form.reset(); }, + submitFileName(fileName) { + this.setState({ + contractName: fileName + }); + + this.refs.form.submit(); + }, render() { return ( - {getLangText('Create new contract')} - - } - spinner={ - - - - }> + handleSuccess={this.handleCreateSuccess}> + label="Contract file (*.pdf)"> + label={getLangText('Contract name')} + hidden={true}> - - - - - {getLangText('Make contract public (this will replace the current public contract')} - - + value={this.state.contractName}/> ); diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js index 3bef7ccd..d0c3410b 100644 --- a/js/components/ascribe_forms/input_fineuploader.js +++ b/js/components/ascribe_forms/input_fineuploader.js @@ -12,6 +12,8 @@ let InputFileUploader = React.createClass({ propTypes: { setIsUploadReady: React.PropTypes.func, isReadyForFormSubmission: React.PropTypes.func, + submitFileName: React.PropTypes.func, + onClick: React.PropTypes.func, keyRoutine: React.PropTypes.shape({ url: React.PropTypes.string, @@ -22,7 +24,8 @@ let InputFileUploader = React.createClass({ }), validation: React.PropTypes.shape({ itemLimit: React.PropTypes.number, - sizeLimit: React.PropTypes.string + sizeLimit: React.PropTypes.string, + allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) }), // isFineUploaderActive is used to lock react fine uploader in case @@ -47,6 +50,10 @@ let InputFileUploader = React.createClass({ this.setState({ value: file.key }); + + if(typeof this.props.submitFileName === 'function') { + this.props.submitFileName(file.originalName); + } }, reset() { diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 5b27cb90..e5609c60 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -83,7 +83,8 @@ var ReactS3FineUploader = React.createClass({ }), validation: React.PropTypes.shape({ itemLimit: React.PropTypes.number, - sizeLimit: React.PropTypes.string + sizeLimit: React.PropTypes.string, + allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) }), messages: React.PropTypes.shape({ unsupportedBrowser: React.PropTypes.string @@ -433,7 +434,7 @@ var ReactS3FineUploader = React.createClass({ }); this.state.uploader.cancelAll(); - let notification = new GlobalNotificationModel(this.props.defaultErrorMessage, 'danger', 5000); + let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000); GlobalNotificationActions.appendGlobalNotification(notification); }, From d47ead1bff917aebd87075f405757af23b200334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 14 Sep 2015 16:33:32 +0200 Subject: [PATCH 05/20] add fileclassToUpload to react fineuploader --- .../ascribe_forms/form_create_contract.js | 6 +++++- .../ascribe_forms/input_fineuploader.js | 12 ++++++++++-- .../ascribe_uploader/file_drag_and_drop.js | 12 ++++++++++-- .../file_drag_and_drop_dialog.js | 17 ++++++++++++----- .../ascribe_uploader/react_s3_fine_uploader.js | 18 +++++++++++++++--- 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index 614480a7..23916c67 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -77,7 +77,11 @@ let CreateContractForm = React.createClass({ areAssetsEditable={true} submitFile={this.submitFile} setIsUploadReady={this.setIsUploadReady} - isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} /> + isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} + fileClassToUpload={{ + singular: 'contract', + plural: 'contracts' + }}/> + enableLocalHashing={this.props.enableLocalHashing} + fileClassToUpload={this.props.fileClassToUpload}/> ); } }); diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index 7d6c8a66..9de22d3d 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -37,7 +37,14 @@ let FileDragAndDrop = React.createClass({ hashingProgress: React.PropTypes.number, // sets the value of this.state.hashingProgress in reactfineuploader // to -1 which is code for: aborted - handleCancelHashing: React.PropTypes.func + handleCancelHashing: React.PropTypes.func, + + // 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 + }) }, handleDragStart(event) { @@ -192,7 +199,8 @@ let FileDragAndDrop = React.createClass({ multipleFiles={this.props.multiple} hasFiles={hasFiles} onClick={this.handleOnClick} - enableLocalHashing={this.props.enableLocalHashing}/> + enableLocalHashing={this.props.enableLocalHashing} + fileClassToUpload={this.props.fileClassToUpload}/> -

{getLangText('Drag files here')}

+

{getLangText('Drag %s here', this.props.fileClassToUpload.plural)}

{getLangText('or')}

- {getLangText('choose files to upload')} + {getLangText('choose %s to upload', this.props.fileClassToUpload.plural)} ); } else { - let dialog = queryParams.method === 'hash' ? getLangText('choose a file to hash') : getLangText('choose a file to upload'); + let dialog = queryParams.method === 'hash' ? getLangText('choose a %s to hash', this.props.fileClassToUpload.singular) : getLangText('choose a file to upload'); return ( -

{getLangText('Drag a file here')}

+

{getLangText('Drag a %s here', this.props.fileClassToUpload.singular)}

{getLangText('or')}

+ enableLocalHashing={this.props.enableLocalHashing} + fileClassToUpload={this.props.fileClassToUpload}/> ); } From 209d141939e323312806fe0f381c03cf11998fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 14 Sep 2015 17:02:47 +0200 Subject: [PATCH 06/20] extend fileClassToUpload functionality and implement it in contract settings --- .../ascribe_forms/form_create_contract.js | 25 ++++++++++++---- .../ascribe_forms/input_fineuploader.js | 2 +- .../ascribe_settings/contract_settings.js | 29 ++++++++++++++----- .../file_drag_and_drop_dialog.js | 2 +- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index 23916c67..c91e97f0 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -4,7 +4,6 @@ import React from 'react'; import Form from '../ascribe_forms/form'; import Property from '../ascribe_forms/property'; -import InputCheckbox from '../ascribe_forms/input_checkbox'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; @@ -21,6 +20,16 @@ import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uplo let CreateContractForm = React.createClass({ + propTypes: { + isPublic: React.PropTypes.bool, + + // 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 + }) + }, getInitialState() { return { @@ -58,7 +67,7 @@ let CreateContractForm = React.createClass({ handleSuccess={this.handleCreateSuccess}> + label="Contract file (*.pdf only)"> + fileClassToUpload={this.props.fileClassToUpload}/> + ); } diff --git a/js/components/ascribe_forms/input_fineuploader.js b/js/components/ascribe_forms/input_fineuploader.js index 4fa89ef8..82d6bf92 100644 --- a/js/components/ascribe_forms/input_fineuploader.js +++ b/js/components/ascribe_forms/input_fineuploader.js @@ -103,7 +103,7 @@ let InputFileUploader = React.createClass({ } }} onInactive={this.props.onLoggedOut} - enableLocalHashing={this.props.enableLocalHashing} + enableLocalHashing={this.props.enableLocalHashing} fileClassToUpload={this.props.fileClassToUpload}/> ); } diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index 06584a8d..ed07e33f 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -80,6 +80,18 @@ let ContractSettings = React.createClass({ render() { let publicContracts = this.getPublicContracts(); let privateContracts = this.getPrivateContracts(); + let createPublicContractForm = null; + + if(publicContracts.length === 0) { + createPublicContractForm = ( + + ); + } return ( + {createPublicContractForm} {publicContracts.map((contract, i) => { return ( - DOWNLOAD + PREVIEW - - PREVIEW - - - - } - leftColumnWidth="40%" - rightColumnWidth="60%"/> - ); - })} - - - - {privateContracts.map((contract, i) => { - return ( - - - - - PREVIEW - - - - } - leftColumnWidth="40%" - rightColumnWidth="60%"/> - ); - })} - + {createPublicContractForm} + {publicContracts.map((contract, i) => { + return ( + + + + PREVIEW + + + + } + leftColumnWidth="40%" + rightColumnWidth="60%"/> + ); + })} + + + + {privateContracts.map((contract, i) => { + return ( + + + + + PREVIEW + + + + } + leftColumnWidth="40%" + rightColumnWidth="60%"/> + ); + })} ); diff --git a/js/components/ascribe_settings/settings_container.js b/js/components/ascribe_settings/settings_container.js index 485b26d7..2d2440bf 100644 --- a/js/components/ascribe_settings/settings_container.js +++ b/js/components/ascribe_settings/settings_container.js @@ -26,8 +26,6 @@ let SettingsContainer = React.createClass({ -
-
); } diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js index 1d948572..205eb177 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js @@ -59,7 +59,7 @@ let FileDragAndDropPreviewOther = React.createClass({
{actionSymbol} - {'.' + this.props.type} +

{'.' + this.props.type}

diff --git a/sass/ascribe_collapsible.scss b/sass/ascribe_collapsible.scss index 2b65567c..99b03f60 100644 --- a/sass/ascribe_collapsible.scss +++ b/sass/ascribe_collapsible.scss @@ -24,9 +24,8 @@ background: none; border: 0; width: 100%; - - /* Shrink the size of the headline for a nested element */ - .ascribe-collapsible-wrapper > .ascribe-collapsible-content { + + .ascribe-collapsible-wrapper { padding-left: 1em; font-size: 95%; } From 280f3bc73ac7b9a549ac349f006ccbb8ca14c196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 10:15:56 +0200 Subject: [PATCH 08/20] Add input filter for specific file extensions to react fineuploader --- .../ascribe_forms/form_create_contract.js | 2 +- .../ascribe_uploader/file_drag_and_drop.js | 70 ++++++++++++++----- .../react_s3_fine_uploader.js | 3 +- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index c91e97f0..f7f81e74 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -67,7 +67,7 @@ let CreateContractForm = React.createClass({ handleSuccess={this.handleCreateSuccess}> + label={getLangText('Contract file (*.pdf only, max. 50MB per contract)')}> file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; - let className = hasFiles ? 'has-files ' : ''; - className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; - className += this.props.className ? ' ' + this.props.className : ''; + let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; + let updatedClassName = hasFiles ? 'has-files ' : ''; + updatedClassName += dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; + updatedClassName += className ? ' ' + className : ''; // if !== -2: triggers a FileDragAndDrop-global spinner - if(this.props.hashingProgress !== -2) { + if(hashingProgress !== -2) { return (

{getLangText('Computing hash(es)... This may take a few minutes.')}

- {Math.ceil(this.props.hashingProgress)}% - {getLangText('Cancel hashing')} + {Math.ceil(hashingProgress)}% + {getLangText('Cancel hashing')}

- +
); } else { + let accept = ''; + + /** + * Fineuploader allows to specify the file extensions that are allowed to upload. + * For our self defined input, we can reuse those declarations to restrict which files + * the user can pick from his hard drive. + */ + if(validation && validation.allowedExtensions.length > 0) { + // add a dot in front of the extension + let prefixedAllowedExtensions = validation.allowedExtensions.map((ext) => '.' + ext); + + // generate a comma separated list to add them to the DOM element + // See: http://stackoverflow.com/questions/4328947/limit-file-format-when-using-input-type-file + accept = prefixedAllowedExtensions.join(', '); + } + return (
+ enableLocalHashing={enableLocalHashing} + fileClassToUpload={fileClassToUpload}/> + areAssetsDownloadable={areAssetsDownloadable} + areAssetsEditable={areAssetsEditable}/> + onChange={this.handleDrop} + accept={accept}/>
); } diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index f422b509..8429499f 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -840,7 +840,8 @@ var ReactS3FineUploader = React.createClass({ dropzoneInactive={this.isDropzoneInactive()} hashingProgress={this.state.hashingProgress} enableLocalHashing={this.props.enableLocalHashing} - fileClassToUpload={this.props.fileClassToUpload}/> + fileClassToUpload={this.props.fileClassToUpload} + validation={this.props.validation}/> ); } From 8b1193f05b9c39425db6068e241993792a6b75b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 10:43:45 +0200 Subject: [PATCH 09/20] implement template functionality for react fineuploader --- .../react_s3_fine_uploader.js | 64 ++++++++++++------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 8429499f..2bc28c09 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -118,7 +118,12 @@ var ReactS3FineUploader = React.createClass({ fileClassToUpload: React.PropTypes.shape({ singular: React.PropTypes.string, plural: React.PropTypes.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.func }, mixins: [Router.State], @@ -174,7 +179,8 @@ var ReactS3FineUploader = React.createClass({ fileClassToUpload: { singular: getLangText('file'), plural: getLangText('files') - } + }, + fileInputElement: FileDragAndDrop }; }, @@ -822,28 +828,38 @@ var ReactS3FineUploader = React.createClass({ }, render() { - return ( -
- -
- ); + let { + multiple, + areAssetsDownloadable, + areAssetsEditable, + onInactive, + enableLocalHashing, + fileClassToUpload, + validation, + fileInputElement + } = this.props; + + // Here we initialize the template that has been either provided from the outside + // or the default input that is FileDragAndDrop. + return React.createElement(fileInputElement, { + className: 'file-drag-and-drop', + onDrop: this.handleUploadFile, + filesToUpload: this.state.filesToUpload, + handleDeleteFile: this.handleDeleteFile, + handleCancelFile: this.handleCancelFile, + handlePauseFile: this.handlePauseFile, + handleResumeFile: this.handleResumeFile, + handleCancelHashing: this.handleCancelHashing, + multiple: multiple, + areAssetsDownloadable: areAssetsDownloadable, + areAssetsEditable: areAssetsEditable, + onInactive: onInactive, + dropzoneInactive: this.isDropzoneInactive(), + hashingProgress: this.state.hashingProgress, + enableLocalHashing: enableLocalHashing, + fileClassToUpload: fileClassToUpload, + validation: validation + }); } }); From c08f46bd69ada48d24f8961447014a1723e10a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 11:13:17 +0200 Subject: [PATCH 10/20] separate FileDragAndDrop component --- .../{ => ascribe_file_drag_and_drop}/file_drag_and_drop.js | 4 ++-- .../file_drag_and_drop_dialog.js | 2 +- .../file_drag_and_drop_preview.js | 2 +- .../file_drag_and_drop_preview_image.js | 4 ++-- .../file_drag_and_drop_preview_iterator.js | 0 .../file_drag_and_drop_preview_other.js | 4 ++-- js/components/ascribe_uploader/react_s3_fine_uploader.js | 3 +-- 7 files changed, 9 insertions(+), 10 deletions(-) rename js/components/ascribe_uploader/{ => ascribe_file_drag_and_drop}/file_drag_and_drop.js (98%) rename js/components/ascribe_uploader/{ => ascribe_file_drag_and_drop}/file_drag_and_drop_dialog.js (98%) rename js/components/ascribe_uploader/{ => ascribe_file_drag_and_drop}/file_drag_and_drop_preview.js (98%) rename js/components/ascribe_uploader/{ => ascribe_file_drag_and_drop}/file_drag_and_drop_preview_image.js (95%) rename js/components/ascribe_uploader/{ => ascribe_file_drag_and_drop}/file_drag_and_drop_preview_iterator.js (100%) rename js/components/ascribe_uploader/{ => ascribe_file_drag_and_drop}/file_drag_and_drop_preview_other.js (95%) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js similarity index 98% rename from js/components/ascribe_uploader/file_drag_and_drop.js rename to js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js index 478522a4..b6b5b218 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js @@ -6,7 +6,7 @@ import ProgressBar from 'react-progressbar'; import FileDragAndDropDialog from './file_drag_and_drop_dialog'; import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator'; -import { getLangText } from '../../utils/lang_utils'; +import { getLangText } from '../../../utils/lang_utils'; // Taken from: https://github.com/fedosejev/react-file-drag-and-drop let FileDragAndDrop = React.createClass({ @@ -189,7 +189,7 @@ let FileDragAndDrop = React.createClass({ let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; let updatedClassName = hasFiles ? 'has-files ' : ''; updatedClassName += dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; - updatedClassName += className ? ' ' + className : ''; + updatedClassName += ' file-drag-and-drop'; // if !== -2: triggers a FileDragAndDrop-global spinner if(hashingProgress !== -2) { diff --git a/js/components/ascribe_uploader/file_drag_and_drop_dialog.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js similarity index 98% rename from js/components/ascribe_uploader/file_drag_and_drop_dialog.js rename to js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js index c34e6bdc..769a0f0d 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_dialog.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_dialog.js @@ -3,7 +3,7 @@ import React from 'react'; import Router from 'react-router'; -import { getLangText } from '../../utils/lang_utils'; +import { getLangText } from '../../../utils/lang_utils'; let Link = Router.Link; diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js similarity index 98% rename from js/components/ascribe_uploader/file_drag_and_drop_preview.js rename to js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js index 9c6cdbdd..e08f02b0 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview.js @@ -4,7 +4,7 @@ import React from 'react'; import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image'; import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other'; -import { getLangText } from '../../utils/lang_utils.js'; +import { getLangText } from '../../../utils/lang_utils'; let FileDragAndDropPreview = React.createClass({ diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js similarity index 95% rename from js/components/ascribe_uploader/file_drag_and_drop_preview_image.js rename to js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js index f4b61760..8268f9ac 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_image.js @@ -3,8 +3,8 @@ import React from 'react'; import ProgressBar from 'react-progressbar'; -import AppConstants from '../../constants/application_constants'; -import { getLangText } from '../../utils/lang_utils.js'; +import AppConstants from '../../../constants/application_constants'; +import { getLangText } from '../../../utils/lang_utils'; let FileDragAndDropPreviewImage = React.createClass({ propTypes: { diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_iterator.js similarity index 100% rename from js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js rename to js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_iterator.js diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_other.js similarity index 95% rename from js/components/ascribe_uploader/file_drag_and_drop_preview_other.js rename to js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_other.js index 205eb177..24b33da7 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_other.js @@ -3,8 +3,8 @@ import React from 'react'; import ProgressBar from 'react-progressbar'; -import AppConstants from '../../constants/application_constants'; -import { getLangText } from '../../utils/lang_utils.js'; +import AppConstants from '../../../constants/application_constants'; +import { getLangText } from '../../../utils/lang_utils'; let FileDragAndDropPreviewOther = React.createClass({ propTypes: { diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 2bc28c09..5d7b69ff 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -10,7 +10,7 @@ import { getLangText } from '../../utils/lang_utils'; import S3Fetcher from '../../fetchers/s3_fetcher'; import fineUploader from 'fineUploader'; -import FileDragAndDrop from './file_drag_and_drop'; +import FileDragAndDrop from './ascribe_file_drag_and_drop/file_drag_and_drop'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; @@ -842,7 +842,6 @@ var ReactS3FineUploader = React.createClass({ // Here we initialize the template that has been either provided from the outside // or the default input that is FileDragAndDrop. return React.createElement(fileInputElement, { - className: 'file-drag-and-drop', onDrop: this.handleUploadFile, filesToUpload: this.state.filesToUpload, handleDeleteFile: this.handleDeleteFile, From c5ef3cacd51984450ef62e21d2504175ba45b1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 11:50:23 +0200 Subject: [PATCH 11/20] add ContractSettingsUpdateButton boilerplate code --- js/components/ascribe_forms/property.js | 2 +- .../ascribe_settings/contract_settings.js | 8 ++++---- .../contract_settings_update_button.js | 16 ++++++++++++++++ .../ascribe_uploader/react_s3_fine_uploader.js | 8 ++++---- 4 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 js/components/ascribe_settings/contract_settings_update_button.js diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index f446b64f..f3c26935 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -70,7 +70,7 @@ let Property = React.createClass({ // In order to set this.state.value from another component // the state of value should only be set if its not undefined and // actually references something - if(typeof childInput.getDOMNode().value !== 'undefined') { + if(childInput && typeof childInput.getDOMNode().value !== 'undefined') { this.setState({ value: childInput.getDOMNode().value }); diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index 0e53c13b..0df6526e 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -9,12 +9,14 @@ import ContractListStore from '../../stores/contract_list_store'; import ContractListActions from '../../actions/contract_list_actions'; import ActionPanel from '../ascribe_panel/action_panel'; +import ContractSettingsUpdateButton from './contract_settings_update_button'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; import { getLangText } from '../../utils/lang_utils'; + let ContractSettings = React.createClass({ propTypes: { defaultExpanded: React.PropTypes.bool @@ -97,7 +99,7 @@ let ContractSettings = React.createClass({ + defaultExpanded={true}> MAKE PUBLIC - + + UPDATE + + ); + } +}); + +export default ContractSettingsUpdateButton; \ No newline at end of file diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 5d7b69ff..e4361ea7 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -2,14 +2,11 @@ import React from 'react/addons'; import Router from 'react-router'; +import fineUploader from 'fineUploader'; import Q from 'q'; -import { getCookie } from '../../utils/fetch_api_utils'; -import { getLangText } from '../../utils/lang_utils'; - import S3Fetcher from '../../fetchers/s3_fetcher'; -import fineUploader from 'fineUploader'; import FileDragAndDrop from './ascribe_file_drag_and_drop/file_drag_and_drop'; import GlobalNotificationModel from '../../models/global_notification_model'; @@ -17,8 +14,11 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import AppConstants from '../../constants/application_constants'; +import { getCookie } from '../../utils/fetch_api_utils'; +import { getLangText } from '../../utils/lang_utils'; import { computeHashOfFile } from '../../utils/file_utils'; + var ReactS3FineUploader = React.createClass({ propTypes: { keyRoutine: React.PropTypes.shape({ From 6c8135d6aceb5783745bd73591ea5a62fb104427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 13:13:58 +0200 Subject: [PATCH 12/20] fix length of undefined bug in fineuploadeer --- .../ascribe_file_drag_and_drop/file_drag_and_drop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b6b5b218..aedfe1a3 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 @@ -211,7 +211,7 @@ let FileDragAndDrop = React.createClass({ * For our self defined input, we can reuse those declarations to restrict which files * the user can pick from his hard drive. */ - if(validation && validation.allowedExtensions.length > 0) { + if(validation && validation.allowedExtensions && validation.allowedExtensions.length > 0) { // add a dot in front of the extension let prefixedAllowedExtensions = validation.allowedExtensions.map((ext) => '.' + ext); From 8294016ebbf93508d33e93cdc06a6dca601245df Mon Sep 17 00:00:00 2001 From: Cevo Date: Fri, 4 Sep 2015 16:18:29 +0200 Subject: [PATCH 13/20] main.scss edited --- .../components/ikonotv/ikonotv_piece_list.js | 1 - sass/main.scss | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js index e4c83d97..4c8766e4 100644 --- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js +++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js @@ -8,7 +8,6 @@ import UserStore from '../../../../../stores/user_store'; import IkonotvAccordionListItem from './ascribe_accordion_list/ikonotv_accordion_list_item'; - let IkonotvPieceList = React.createClass({ getInitialState() { return UserStore.getState(); diff --git a/sass/main.scss b/sass/main.scss index e8d708e3..a1556cd3 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -103,6 +103,34 @@ hr { height: 60px; } +//http://stackoverflow.com/questions/22228239/bootstrap-navbar-static-top-menu-breaks-on-two-lines +@media (max-width: 990px) { + .navbar-header { + float: none; + } + .navbar-toggle { + display: block; + } + .navbar-collapse { + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255,255,255,0.1); + } + .navbar-collapse.collapse { + display: none!important; + } + .navbar-nav { + float: none!important; + margin: 7.5px -15px; + } + .navbar-nav>li { + float: none; + } + .navbar-nav>li>a { + padding-top: 10px; + padding-bottom: 10px; + } +} + .truncate { white-space: nowrap; width: 4em; From 33ec380f4cbaa8bf315138e774b7d963a31c2751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 14:20:30 +0200 Subject: [PATCH 14/20] fix minor merge conflict error --- .../ascribe_uploader/react_s3_fine_uploader.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index fac9f2b3..39ab42fc 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -1,15 +1,12 @@ 'use strict'; import React from 'react/addons'; +import fineUploader from 'fineUploader'; import Router from 'react-router'; import Q from 'q'; -import { getCookie } from '../../utils/fetch_api_utils'; -import { getLangText } from '../../utils/lang_utils'; - import S3Fetcher from '../../fetchers/s3_fetcher'; -import fineUploader from 'fineUploader'; import FileDragAndDrop from './file_drag_and_drop'; import GlobalNotificationModel from '../../models/global_notification_model'; @@ -17,7 +14,11 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import AppConstants from '../../constants/application_constants'; -import { computeHashOfFile, displayValidFilesFilter } from '../../utils/file_utils'; +import { computeHashOfFile } from '../../utils/file_utils'; +import { displayValidFilesFilter } from './react_s3_fine_uploader_utils'; +import { getCookie } from '../../utils/fetch_api_utils'; +import { getLangText } from '../../utils/lang_utils'; + var ReactS3FineUploader = React.createClass({ propTypes: { From 83b20e64722a908fce81739066d5712f6a84ce29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 15 Sep 2015 16:35:45 +0200 Subject: [PATCH 15/20] First cut: Upload button for contract settings --- .../ascribe_settings/contract_settings.js | 4 +- .../contract_settings_update_button.js | 58 ++++++++- .../file_drag_and_drop.js | 27 +---- .../ascribe_upload_button/upload_button.js | 114 ++++++++++++++++++ .../react_s3_fine_uploader.js | 19 ++- .../react_s3_fine_uploader_utils.js | 19 +++ sass/ascribe_uploader.scss | 1 + 7 files changed, 209 insertions(+), 33 deletions(-) create mode 100644 js/components/ascribe_uploader/ascribe_upload_button/upload_button.js diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index a3c40380..8a6700f1 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -113,9 +113,7 @@ let ContractSettings = React.createClass({ content={contract.name} buttons={
- + - UPDATE - + ); } }); 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 71f3a321..1e21aeb8 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 @@ -12,7 +12,6 @@ import { getLangText } from '../../../utils/lang_utils'; // Taken from: https://github.com/fedosejev/react-file-drag-and-drop let FileDragAndDrop = React.createClass({ propTypes: { - className: React.PropTypes.string, onDragStart: React.PropTypes.func, onDrop: React.PropTypes.func.isRequired, onDrag: React.PropTypes.func, @@ -47,11 +46,7 @@ let FileDragAndDrop = React.createClass({ plural: React.PropTypes.string }), - validation: React.PropTypes.shape({ - itemLimit: React.PropTypes.number, - sizeLimit: React.PropTypes.string, - allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) - }) + allowedExtensions: React.PropTypes.string }, handleDragStart(event) { @@ -183,7 +178,7 @@ let FileDragAndDrop = React.createClass({ fileClassToUpload, areAssetsDownloadable, areAssetsEditable, - validation + allowedExtensions } = this.props; // has files only is true if there are files that do not have the status deleted or canceled @@ -209,22 +204,6 @@ let FileDragAndDrop = React.createClass({
); } else { - let accept = ''; - - /** - * Fineuploader allows to specify the file extensions that are allowed to upload. - * For our self defined input, we can reuse those declarations to restrict which files - * the user can pick from his hard drive. - */ - if(validation && validation.allowedExtensions && validation.allowedExtensions.length > 0) { - // add a dot in front of the extension - let prefixedAllowedExtensions = validation.allowedExtensions.map((ext) => '.' + ext); - - // generate a comma separated list to add them to the DOM element - // See: http://stackoverflow.com/questions/4328947/limit-file-format-when-using-input-type-file - accept = prefixedAllowedExtensions.join(', '); - } - return (
+ accept={allowedExtensions}/>
); } diff --git a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js new file mode 100644 index 00000000..a3fa985c --- /dev/null +++ b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js @@ -0,0 +1,114 @@ +'use strict'; + +import React from 'react'; + +import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils'; + +let UploadButton = React.createClass({ + propTypes: { + onDragStart: React.PropTypes.func, + onDrop: React.PropTypes.func.isRequired, + onDrag: React.PropTypes.func, + onDragEnter: React.PropTypes.func, + onLeave: React.PropTypes.func, + onDragLeave: React.PropTypes.func, + onDragOver: React.PropTypes.func, + onDragEnd: React.PropTypes.func, + onInactive: React.PropTypes.func, + filesToUpload: React.PropTypes.array, + handleDeleteFile: React.PropTypes.func, + handleCancelFile: React.PropTypes.func, + handlePauseFile: React.PropTypes.func, + handleResumeFile: React.PropTypes.func, + multiple: React.PropTypes.bool, + + + // For simplification purposes we're just going to use this prop as a + // label for the upload button + fileClassToUpload: React.PropTypes.shape({ + singular: React.PropTypes.string, + plural: React.PropTypes.string + }), + + allowedExtensions: React.PropTypes.string + }, + + handleDrop(event) { + event.preventDefault(); + event.stopPropagation(); + let files = event.target.files; + + if(typeof this.props.onDrop === 'function' && files) { + this.props.onDrop(files); + } + + }, + + getUploadingFiles() { + return this.props.filesToUpload.filter((file) => file.status === 'uploading'); + }, + + handleOnClick() { + let uploadingFiles = this.getUploadingFiles(); + + // We only want the button to be clickable if there are no files currently uploading + if(uploadingFiles.length === 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 + }); + + evt.stopPropagation(); + this.refs.fileinput.getDOMNode().dispatchEvent(evt); + } + }, + + getButtonLabel() { + let { filesToUpload, fileClassToUpload } = this.props; + + // filter invalid files that might have been deleted or canceled... + filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter); + + // Depending on wether there is an upload going on or not we + // display the progress + if(filesToUpload.length > 0) { + return 'Upload progress: ' + Math.ceil(filesToUpload[0].progress) + '%'; + } else { + return fileClassToUpload.singular; + } + }, + + render() { + let { + multiple, + fileClassToUpload, + allowedExtensions + } = this.props; + + return ( + + ); + } +}); + +export default UploadButton; \ No newline at end of file diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 8ba53444..05709ad2 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -16,7 +16,7 @@ import AppConstants from '../../constants/application_constants'; import { computeHashOfFile } from '../../utils/file_utils'; -import { displayValidFilesFilter } from './react_s3_fine_uploader_utils'; +import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp } from './react_s3_fine_uploader_utils'; import { getCookie } from '../../utils/fetch_api_utils'; import { getLangText } from '../../utils/lang_utils'; @@ -125,7 +125,10 @@ let ReactS3FineUploader = React.createClass({ // 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.func + fileInputElement: React.PropTypes.oneOfType([ + React.PropTypes.func, + React.PropTypes.element + ]) }, mixins: [Router.State], @@ -838,6 +841,16 @@ let ReactS3FineUploader = React.createClass({ }, + getAllowedExtensions() { + let { validation } = this.props; + + if(validation && validation.allowedExtensions && validation.allowedExtensions.length > 0) { + return transformAllowedExtensionsToInputAcceptProp(validation.allowedExtensions); + } else { + return null; + } + }, + render() { let { multiple, @@ -868,7 +881,7 @@ let ReactS3FineUploader = React.createClass({ hashingProgress: this.state.hashingProgress, enableLocalHashing: enableLocalHashing, fileClassToUpload: fileClassToUpload, - validation: validation + allowedExtensions: this.getAllowedExtensions() }); } 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 92d5c2ba..cd1dbce2 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js @@ -52,3 +52,22 @@ export function displayValidProgressFilesFilter(file) { return file.status !== 'deleted' && file.status !== 'canceled' && file.status !== 'online'; } + +/** + * Fineuploader allows to specify the file extensions that are allowed to upload. + * For our self defined input, we can reuse those declarations to restrict which files + * the user can pick from his hard drive. + * + * Takes an array of file extensions (['pdf', 'png', ...]) and transforms them into a string + * that can be passed into an html5 input via its 'accept' prop. + * @param {array} allowedExtensions Array of strings without a dot prefixed + * @return {string} Joined string (comma-separated) of the passed-in array + */ +export function transformAllowedExtensionsToInputAcceptProp(allowedExtensions) { + // add a dot in front of the extension + let prefixedAllowedExtensions = allowedExtensions.map((ext) => '.' + ext); + + // generate a comma separated list to add them to the DOM element + // See: http://stackoverflow.com/questions/4328947/limit-file-format-when-using-input-type-file + return prefixedAllowedExtensions.join(', '); +} diff --git a/sass/ascribe_uploader.scss b/sass/ascribe_uploader.scss index 6e9eebd9..331b6b00 100644 --- a/sass/ascribe_uploader.scss +++ b/sass/ascribe_uploader.scss @@ -9,6 +9,7 @@ text-align: center; vertical-align: middle; cursor: default !important; + padding: 1.5em 0 1.5em 0; .file-drag-and-drop-dialog > p:first-child { font-size: 1.5em !important; From 17cb2223c2358a28782d8277e57117748eea514d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 16 Sep 2015 09:47:22 +0200 Subject: [PATCH 16/20] finalize contract update button --- js/actions/contract_list_actions.js | 2 +- .../ascribe_settings/contract_settings.js | 26 ++------------ .../contract_settings_update_button.js | 36 ++++++++++++++++--- .../ascribe_upload_button/upload_button.js | 5 +-- js/fetchers/ownership_fetcher.js | 2 +- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/js/actions/contract_list_actions.js b/js/actions/contract_list_actions.js index a856fb2b..5b874caf 100644 --- a/js/actions/contract_list_actions.js +++ b/js/actions/contract_list_actions.js @@ -30,7 +30,7 @@ class ContractListActions { changeContract(contract){ return Q.Promise((resolve, reject) => { - OwnershipFetcher.makeContractPublic(contract) + OwnershipFetcher.changeContract(contract) .then((res) => { resolve(res); }) diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index 00b37b78..278c0f52 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -39,23 +39,6 @@ let ContractSettings = React.createClass({ this.setState(state); }, - makeContractPublic(contract) { - return () => { - contract.is_public = true; - ContractListActions.changeContract(contract) - .then(() => { - ContractListActions.fetchContractList(true); - let notification = getLangText('Contract %s is now public', contract.name); - notification = new GlobalNotificationModel(notification, 'success', 4000); - GlobalNotificationActions.appendGlobalNotification(notification); - }) - .catch((err) => { - let notification = new GlobalNotificationModel(err, 'danger', 10000); - GlobalNotificationActions.appendGlobalNotification(notification); - }); - }; - }, - removeContract(contract) { return () => { ContractListActions.removeContract(contract.id) @@ -113,7 +96,7 @@ let ContractSettings = React.createClass({ content={contract.name} buttons={
- + - - + { + let notification = new GlobalNotificationModel(getLangText('Contract %s successfully updated', res.name), 'success', 5000); + GlobalNotificationActions.appendGlobalNotification(notification); + + return ContractListActions.fetchContractList(true); + }) + .then(() => { + this.refs.fineuploader.reset(); + }) + .catch((err) => { + console.logGlobal(err); + let notification = new GlobalNotificationModel(getLangText('Contract could not be updated'), 'success', 5000); + GlobalNotificationActions.appendGlobalNotification(notification); + }); }, render() { return ( {/* So that ReactS3FineUploader is not complaining */}} signature={{ endpoint: AppConstants.serverUrl + 's3/signature/', customHeaders: { diff --git a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js index a3fa985c..47347d46 100644 --- a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js +++ b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js @@ -3,6 +3,8 @@ import React from 'react'; import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils'; +import { getLangText } from '../../../utils/lang_utils'; + let UploadButton = React.createClass({ propTypes: { @@ -22,7 +24,6 @@ let UploadButton = React.createClass({ handleResumeFile: React.PropTypes.func, multiple: React.PropTypes.bool, - // For simplification purposes we're just going to use this prop as a // label for the upload button fileClassToUpload: React.PropTypes.shape({ @@ -76,7 +77,7 @@ let UploadButton = React.createClass({ // Depending on wether there is an upload going on or not we // display the progress if(filesToUpload.length > 0) { - return 'Upload progress: ' + Math.ceil(filesToUpload[0].progress) + '%'; + return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; } else { return fileClassToUpload.singular; } diff --git a/js/fetchers/ownership_fetcher.js b/js/fetchers/ownership_fetcher.js index fde66c7d..f9ce5f86 100644 --- a/js/fetchers/ownership_fetcher.js +++ b/js/fetchers/ownership_fetcher.js @@ -48,7 +48,7 @@ let OwnershipFetcher = { return requests.get(ApiUrls.ownership_loans_pieces_request); }, - makeContractPublic(contractObj){ + changeContract(contractObj){ return requests.put(ApiUrls.ownership_contract, { body: contractObj, contract_id: contractObj.id }); }, From b2a3c9545b5f6180125cb7847e6c306f49102536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 16 Sep 2015 14:07:55 +0200 Subject: [PATCH 17/20] minor corrections --- .../contract_settings_update_button.js | 4 ++++ .../file_drag_and_drop_preview.js | 1 + .../ascribe_upload_button/upload_button.js | 14 +------------- .../ascribe_uploader/react_s3_fine_uploader.js | 1 - 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/js/components/ascribe_settings/contract_settings_update_button.js b/js/components/ascribe_settings/contract_settings_update_button.js index 2fbfe15f..5712d441 100644 --- a/js/components/ascribe_settings/contract_settings_update_button.js +++ b/js/components/ascribe_settings/contract_settings_update_button.js @@ -33,12 +33,16 @@ let ContractSettingsUpdateButton = React.createClass({ ContractListActions .changeContract(contract) .then((res) => { + + // Display feedback to the user let notification = new GlobalNotificationModel(getLangText('Contract %s successfully updated', res.name), 'success', 5000); GlobalNotificationActions.appendGlobalNotification(notification); + // and refresh the contract list to get the updated contracs return ContractListActions.fetchContractList(true); }) .then(() => { + // Also, reset the fineuploader component so that the user can again 'update' his contract this.refs.fineuploader.reset(); }) .catch((err) => { 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 8f712061..86d4135e 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 @@ -45,6 +45,7 @@ let FileDragAndDropPreview = React.createClass({ handleDownloadFile() { if(this.props.file.s3Url) { + // This simply opens a new browser tab with the url provided open(this.props.file.s3Url); } }, diff --git a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js index 47347d46..1547272e 100644 --- a/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js +++ b/js/components/ascribe_uploader/ascribe_upload_button/upload_button.js @@ -8,20 +8,8 @@ import { getLangText } from '../../../utils/lang_utils'; let UploadButton = React.createClass({ propTypes: { - onDragStart: React.PropTypes.func, onDrop: React.PropTypes.func.isRequired, - onDrag: React.PropTypes.func, - onDragEnter: React.PropTypes.func, - onLeave: React.PropTypes.func, - onDragLeave: React.PropTypes.func, - onDragOver: React.PropTypes.func, - onDragEnd: React.PropTypes.func, - onInactive: React.PropTypes.func, filesToUpload: React.PropTypes.array, - handleDeleteFile: React.PropTypes.func, - handleCancelFile: React.PropTypes.func, - handlePauseFile: React.PropTypes.func, - handleResumeFile: React.PropTypes.func, multiple: React.PropTypes.bool, // For simplification purposes we're just going to use this prop as a @@ -77,7 +65,7 @@ let UploadButton = React.createClass({ // Depending on wether there is an upload going on or not we // display the progress if(filesToUpload.length > 0) { - return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; + return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%'; } else { return fileClassToUpload.singular; } diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 54434826..c7a5f9a7 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -3,7 +3,6 @@ import React from 'react/addons'; import fineUploader from 'fineUploader'; import Router from 'react-router'; -import fineUploader from 'fineUploader'; import Q from 'q'; import S3Fetcher from '../../fetchers/s3_fetcher'; From 7c376b7229694e942705ae13fbfd43cc10545395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 16 Sep 2015 15:14:08 +0200 Subject: [PATCH 18/20] PR Feedback: add getLangText and move itemLimit and sizeLimit to app constants --- .../ascribe_detail/further_details_fileuploader.js | 5 +---- js/components/ascribe_forms/form_create_contract.js | 5 ++--- js/components/ascribe_forms/form_register_piece.js | 5 +---- js/components/ascribe_settings/contract_settings.js | 12 ++++++------ .../contract_settings_update_button.js | 4 ++-- js/constants/application_constants.js | 13 +++++++++++++ 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/js/components/ascribe_detail/further_details_fileuploader.js b/js/components/ascribe_detail/further_details_fileuploader.js index bfbf9582..9bf0bd5b 100644 --- a/js/components/ascribe_detail/further_details_fileuploader.js +++ b/js/components/ascribe_detail/further_details_fileuploader.js @@ -55,10 +55,7 @@ let FurtherDetailsFileuploader = React.createClass({ url: ApiUrls.blob_otherdatas, pieceId: this.props.pieceId }} - validation={{ - itemLimit: 100000, - sizeLimit: '50000000' - }} + validation={AppConstants.fineUploader.validation.additionalData} submitFile={this.props.submitFile} setIsUploadReady={this.props.setIsUploadReady} isReadyForFormSubmission={this.props.isReadyForFormSubmission} diff --git a/js/components/ascribe_forms/form_create_contract.js b/js/components/ascribe_forms/form_create_contract.js index 26790269..b19cb050 100644 --- a/js/components/ascribe_forms/form_create_contract.js +++ b/js/components/ascribe_forms/form_create_contract.js @@ -78,13 +78,12 @@ let CreateContractForm = React.createClass({ url: ApiUrls.blob_contracts }} validation={{ - itemLimit: 100000, - sizeLimit: '50000000', + itemLimit: AppConstants.fineUploader.validation.additionalData.itemLimit, + sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit, allowedExtensions: ['pdf'] }} areAssetsDownloadable={true} areAssetsEditable={true} - submitFile={this.submitFile} setIsUploadReady={this.setIsUploadReady} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} fileClassToUpload={this.props.fileClassToUpload}/> diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index dab0b251..ed6fc701 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -107,10 +107,7 @@ let RegisterPieceForm = React.createClass({ createBlobRoutine={{ url: ApiUrls.blob_digitalworks }} - validation={{ - itemLimit: 100000, - sizeLimit: '25000000000' - }} + validation={AppConstants.fineUploader.validation.registerWork} setIsUploadReady={this.setIsUploadReady} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isFineUploaderActive={this.props.isFineUploaderActive} diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js index 278c0f52..c7abc682 100644 --- a/js/components/ascribe_settings/contract_settings.js +++ b/js/components/ascribe_settings/contract_settings.js @@ -101,12 +101,12 @@ let ContractSettings = React.createClass({ className="btn btn-default btn-sm margin-left-2px" href={contract.blob.url_safe} target="_blank"> - PREVIEW + {getLangText('PREVIEW')}
} @@ -122,8 +122,8 @@ let ContractSettings = React.createClass({ {privateContracts.map((contract, i) => { return ( @@ -138,12 +138,12 @@ let ContractSettings = React.createClass({ className="btn btn-default btn-sm margin-left-2px" href={contract.blob.url_safe} target="_blank"> - PREVIEW + {getLangText('PREVIEW')} } diff --git a/js/components/ascribe_settings/contract_settings_update_button.js b/js/components/ascribe_settings/contract_settings_update_button.js index 5712d441..f2e54c50 100644 --- a/js/components/ascribe_settings/contract_settings_update_button.js +++ b/js/components/ascribe_settings/contract_settings_update_button.js @@ -65,8 +65,8 @@ let ContractSettingsUpdateButton = React.createClass({ url: ApiUrls.blob_contracts }} validation={{ - itemLimit: 100000, - sizeLimit: '50000000', + itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit, + sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit, allowedExtensions: ['pdf'] }} setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}} diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js index f1455029..a2726d73 100644 --- a/js/constants/application_constants.js +++ b/js/constants/application_constants.js @@ -57,6 +57,19 @@ let constants = { // Source: http://www.w3schools.com/tags/att_input_type.asp 'possibleInputTypes': ['button', 'checkbox', 'color', 'date', 'datetime', 'datetime-local', 'email', 'file', 'hidden', 'image', 'month', 'number', 'password', 'radio', 'range', 'reset', 'search', 'submit', 'tel', 'text', 'time', 'url', 'week'], + 'fineUploader': { + 'validation': { + 'additionalData': { + 'itemLimit': 100, + 'sizeLimit': '50000000' + }, + 'registerWork': { + 'itemLimit': 1, + 'sizeLimit': '25000000000' + } + } + }, + // in case of whitelabel customization, we store stuff here 'whitelabel': {}, 'raven': { From 3c3e32980c501509a33e5a301f181ac55d71598a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 16 Sep 2015 15:23:01 +0200 Subject: [PATCH 19/20] minor getLangText edition --- .../file_drag_and_drop_preview_progress.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_progress.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_progress.js index 1427fcb6..1f1fd421 100644 --- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_progress.js +++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop_preview_progress.js @@ -5,6 +5,7 @@ import React from 'react'; import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils'; +import { getLangText } from '../../../utils/lang_utils'; let FileDragAndDropPreviewProgress = React.createClass({ @@ -54,7 +55,7 @@ let FileDragAndDropPreviewProgress = React.createClass({ return ( ); From d888bb8a5416f55eb3aeb863f4e2d81b9542b5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 16 Sep 2015 15:52:54 +0200 Subject: [PATCH 20/20] PR Feedback: remove unnecessary DnD functions --- .../file_drag_and_drop.js | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) 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 1e21aeb8..4c9211c5 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 @@ -12,14 +12,8 @@ import { getLangText } from '../../../utils/lang_utils'; // Taken from: https://github.com/fedosejev/react-file-drag-and-drop let FileDragAndDrop = React.createClass({ propTypes: { - onDragStart: React.PropTypes.func, onDrop: React.PropTypes.func.isRequired, - onDrag: React.PropTypes.func, - onDragEnter: React.PropTypes.func, - onLeave: React.PropTypes.func, - onDragLeave: React.PropTypes.func, onDragOver: React.PropTypes.func, - onDragEnd: React.PropTypes.func, onInactive: React.PropTypes.func, filesToUpload: React.PropTypes.array, handleDeleteFile: React.PropTypes.func, @@ -49,36 +43,6 @@ let FileDragAndDrop = React.createClass({ allowedExtensions: React.PropTypes.string }, - handleDragStart(event) { - if (typeof this.props.onDragStart === 'function') { - this.props.onDragStart(event); - } - }, - - handleDrag(event) { - if (typeof this.props.onDrag === 'function') { - this.props.onDrag(event); - } - }, - - handleDragEnd(event) { - if (typeof this.props.onDragEnd === 'function') { - this.props.onDragEnd(event); - } - }, - - handleDragEnter(event) { - if (typeof this.props.onDragEnter === 'function') { - this.props.onDragEnter(event); - } - }, - - handleDragLeave(event) { - if (typeof this.props.onDragLeave === 'function') { - this.props.onDragLeave(event); - } - }, - handleDragOver(event) { event.preventDefault(); @@ -207,13 +171,9 @@ let FileDragAndDrop = React.createClass({ return (
+ onDrop={this.handleDrop}>