mirror of
https://github.com/ascribe/onion.git
synced 2025-01-03 18:35:09 +01:00
First cut: Upload button for contract settings
This commit is contained in:
parent
497a330e1a
commit
83b20e6472
@ -113,9 +113,7 @@ let ContractSettings = React.createClass({
|
|||||||
content={contract.name}
|
content={contract.name}
|
||||||
buttons={
|
buttons={
|
||||||
<div className="pull-right">
|
<div className="pull-right">
|
||||||
<button className="btn btn-default btn-sm margin-left-2px">
|
<ContractSettingsUpdateButton />
|
||||||
UPDATE
|
|
||||||
</button>
|
|
||||||
<a
|
<a
|
||||||
className="btn btn-default btn-sm margin-left-2px"
|
className="btn btn-default btn-sm margin-left-2px"
|
||||||
href={contract.blob.url_safe}
|
href={contract.blob.url_safe}
|
||||||
|
@ -2,13 +2,65 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
||||||
|
import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button';
|
||||||
|
|
||||||
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
|
||||||
|
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
|
||||||
|
import { getCookie } from '../../utils/fetch_api_utils';
|
||||||
|
import { getLangText } from '../../utils/lang_utils';
|
||||||
|
|
||||||
|
|
||||||
let ContractSettingsUpdateButton = React.createClass({
|
let ContractSettingsUpdateButton = React.createClass({
|
||||||
|
|
||||||
|
setIsUploadReady() {
|
||||||
|
console.log('upload done');
|
||||||
|
},
|
||||||
|
|
||||||
|
submitFile(file) {
|
||||||
|
console.log(file);
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<button className="btn btn-default btn-sm margin-left-2px">
|
<ReactS3FineUploader
|
||||||
UPDATE
|
fileInputElement={UploadButton}
|
||||||
</button>
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'contract'
|
||||||
|
}}
|
||||||
|
createBlobRoutine={{
|
||||||
|
url: ApiUrls.blob_contracts
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: 100000,
|
||||||
|
sizeLimit: '50000000',
|
||||||
|
allowedExtensions: ['pdf']
|
||||||
|
}}
|
||||||
|
setIsUploadReady={this.setIsUploadReady}
|
||||||
|
signature={{
|
||||||
|
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||||
|
customHeaders: {
|
||||||
|
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
deleteFile={{
|
||||||
|
enabled: true,
|
||||||
|
method: 'DELETE',
|
||||||
|
endpoint: AppConstants.serverUrl + 's3/delete',
|
||||||
|
customHeaders: {
|
||||||
|
'X-CSRFToken': getCookie(AppConstants.csrftoken)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
fileClassToUpload={{
|
||||||
|
singular: getLangText('UPDATE'),
|
||||||
|
plural: getLangText('UPDATE')
|
||||||
|
}}
|
||||||
|
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||||
|
submitFile={this.submitFile}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -12,7 +12,6 @@ import { getLangText } from '../../../utils/lang_utils';
|
|||||||
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop
|
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop
|
||||||
let FileDragAndDrop = React.createClass({
|
let FileDragAndDrop = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
className: React.PropTypes.string,
|
|
||||||
onDragStart: React.PropTypes.func,
|
onDragStart: React.PropTypes.func,
|
||||||
onDrop: React.PropTypes.func.isRequired,
|
onDrop: React.PropTypes.func.isRequired,
|
||||||
onDrag: React.PropTypes.func,
|
onDrag: React.PropTypes.func,
|
||||||
@ -47,11 +46,7 @@ let FileDragAndDrop = React.createClass({
|
|||||||
plural: React.PropTypes.string
|
plural: React.PropTypes.string
|
||||||
}),
|
}),
|
||||||
|
|
||||||
validation: React.PropTypes.shape({
|
allowedExtensions: React.PropTypes.string
|
||||||
itemLimit: React.PropTypes.number,
|
|
||||||
sizeLimit: React.PropTypes.string,
|
|
||||||
allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDragStart(event) {
|
handleDragStart(event) {
|
||||||
@ -183,7 +178,7 @@ let FileDragAndDrop = React.createClass({
|
|||||||
fileClassToUpload,
|
fileClassToUpload,
|
||||||
areAssetsDownloadable,
|
areAssetsDownloadable,
|
||||||
areAssetsEditable,
|
areAssetsEditable,
|
||||||
validation
|
allowedExtensions
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// has files only is true if there are files that do not have the status deleted or canceled
|
// 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({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} 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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={updatedClassName}
|
className={updatedClassName}
|
||||||
@ -259,7 +238,7 @@ let FileDragAndDrop = React.createClass({
|
|||||||
width: 0
|
width: 0
|
||||||
}}
|
}}
|
||||||
onChange={this.handleDrop}
|
onChange={this.handleDrop}
|
||||||
accept={accept}/>
|
accept={allowedExtensions}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 (
|
||||||
|
<button
|
||||||
|
onClick={this.handleOnClick}
|
||||||
|
className="btn btn-default btn-sm margin-left-2px"
|
||||||
|
disabled={this.getUploadingFiles().length !== 0}>
|
||||||
|
{this.getButtonLabel()}
|
||||||
|
<input
|
||||||
|
multiple={multiple}
|
||||||
|
ref="fileinput"
|
||||||
|
type="file"
|
||||||
|
style={{
|
||||||
|
display: 'none',
|
||||||
|
height: 0,
|
||||||
|
width: 0
|
||||||
|
}}
|
||||||
|
onChange={this.handleDrop}
|
||||||
|
accept={allowedExtensions}/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UploadButton;
|
@ -16,7 +16,7 @@ import AppConstants from '../../constants/application_constants';
|
|||||||
|
|
||||||
|
|
||||||
import { computeHashOfFile } from '../../utils/file_utils';
|
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 { getCookie } from '../../utils/fetch_api_utils';
|
||||||
import { getLangText } from '../../utils/lang_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
|
// Uploading functionality of react fineuploader is disconnected from its UI
|
||||||
// layer, which means that literally every (properly adjusted) react element
|
// layer, which means that literally every (properly adjusted) react element
|
||||||
// can handle the UI handling.
|
// can handle the UI handling.
|
||||||
fileInputElement: React.PropTypes.func
|
fileInputElement: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.func,
|
||||||
|
React.PropTypes.element
|
||||||
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Router.State],
|
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() {
|
render() {
|
||||||
let {
|
let {
|
||||||
multiple,
|
multiple,
|
||||||
@ -868,7 +881,7 @@ let ReactS3FineUploader = React.createClass({
|
|||||||
hashingProgress: this.state.hashingProgress,
|
hashingProgress: this.state.hashingProgress,
|
||||||
enableLocalHashing: enableLocalHashing,
|
enableLocalHashing: enableLocalHashing,
|
||||||
fileClassToUpload: fileClassToUpload,
|
fileClassToUpload: fileClassToUpload,
|
||||||
validation: validation
|
allowedExtensions: this.getAllowedExtensions()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,3 +52,22 @@ export function displayValidProgressFilesFilter(file) {
|
|||||||
return file.status !== 'deleted' && file.status !== 'canceled' && file.status !== 'online';
|
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(', ');
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: default !important;
|
cursor: default !important;
|
||||||
|
padding: 1.5em 0 1.5em 0;
|
||||||
|
|
||||||
.file-drag-and-drop-dialog > p:first-child {
|
.file-drag-and-drop-dialog > p:first-child {
|
||||||
font-size: 1.5em !important;
|
font-size: 1.5em !important;
|
||||||
|
Loading…
Reference in New Issue
Block a user