mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 09:23:13 +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}
|
||||
buttons={
|
||||
<div className="pull-right">
|
||||
<button className="btn btn-default btn-sm margin-left-2px">
|
||||
UPDATE
|
||||
</button>
|
||||
<ContractSettingsUpdateButton />
|
||||
<a
|
||||
className="btn btn-default btn-sm margin-left-2px"
|
||||
href={contract.blob.url_safe}
|
||||
|
@ -2,13 +2,65 @@
|
||||
|
||||
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({
|
||||
|
||||
setIsUploadReady() {
|
||||
console.log('upload done');
|
||||
},
|
||||
|
||||
submitFile(file) {
|
||||
console.log(file);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<button className="btn btn-default btn-sm margin-left-2px">
|
||||
UPDATE
|
||||
</button>
|
||||
<ReactS3FineUploader
|
||||
fileInputElement={UploadButton}
|
||||
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
|
||||
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({
|
||||
</div>
|
||||
);
|
||||
} 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 (
|
||||
<div
|
||||
className={updatedClassName}
|
||||
@ -259,7 +238,7 @@ let FileDragAndDrop = React.createClass({
|
||||
width: 0
|
||||
}}
|
||||
onChange={this.handleDrop}
|
||||
accept={accept}/>
|
||||
accept={allowedExtensions}/>
|
||||
</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 { 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()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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(', ');
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user