mirror of
https://github.com/ascribe/onion.git
synced 2024-06-30 13:41:57 +02:00
ReactS3FineUploader used to check the current url’s query params to determine which method it should use to upload, but this decision means the component is tightly coupled with react-router and history.js. A major pain point is having to propagate the location prop all the way down to this component even when it’s not necessary. Now, ReactS3FineUploader’s parent elements can either parse the current query params themselves or, if they have a location from react-router, simply use the location. Added a few utils to help parse url params.
227 lines
8.5 KiB
JavaScript
227 lines
8.5 KiB
JavaScript
'use strict';
|
|
|
|
import React from 'react';
|
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
|
|
|
import FileDragAndDropDialog from './file_drag_and_drop_dialog';
|
|
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
|
|
|
|
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,
|
|
onDrop: React.PropTypes.func.isRequired,
|
|
onDragOver: 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,
|
|
dropzoneInactive: React.PropTypes.bool,
|
|
areAssetsDownloadable: React.PropTypes.bool,
|
|
areAssetsEditable: React.PropTypes.bool,
|
|
|
|
enableLocalHashing: React.PropTypes.bool,
|
|
uploadMethod: React.PropTypes.string,
|
|
|
|
// triggers a FileDragAndDrop-global spinner
|
|
hashingProgress: React.PropTypes.number,
|
|
// sets the value of this.state.hashingProgress in reactfineuploader
|
|
// to -1 which is code for: aborted
|
|
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
|
|
}),
|
|
|
|
allowedExtensions: React.PropTypes.string
|
|
},
|
|
|
|
handleDragOver(event) {
|
|
event.preventDefault();
|
|
|
|
if (typeof this.props.onDragOver === 'function') {
|
|
this.props.onDragOver(event);
|
|
}
|
|
},
|
|
|
|
handleDrop(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
let files;
|
|
|
|
if(this.props.dropzoneInactive) {
|
|
// if there is a handle function for doing stuff
|
|
// when the dropzone is inactive, then call it
|
|
if(this.props.onInactive) {
|
|
this.props.onInactive();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// handle Drag and Drop
|
|
if(event.dataTransfer && event.dataTransfer.files.length > 0) {
|
|
files = event.dataTransfer.files;
|
|
} else if(event.target.files) { // handle input type file
|
|
files = event.target.files;
|
|
}
|
|
|
|
if(typeof this.props.onDrop === 'function' && files) {
|
|
this.props.onDrop(files);
|
|
}
|
|
|
|
},
|
|
|
|
handleDeleteFile(fileId) {
|
|
// input's value is not change the second time someone
|
|
// inputs the same file again, therefore we need to reset its value
|
|
this.refs.fileinput.getDOMNode().value = '';
|
|
this.props.handleDeleteFile(fileId);
|
|
},
|
|
|
|
handleCancelFile(fileId) {
|
|
// input's value is not change the second time someone
|
|
// inputs the same file again, therefore we need to reset its value
|
|
this.refs.fileinput.getDOMNode().value = '';
|
|
this.props.handleCancelFile(fileId);
|
|
},
|
|
|
|
handlePauseFile(fileId) {
|
|
// input's value is not change the second time someone
|
|
// inputs the same file again, therefore we need to reset its value
|
|
this.refs.fileinput.getDOMNode().value = '';
|
|
this.props.handlePauseFile(fileId);
|
|
},
|
|
|
|
handleResumeFile(fileId) {
|
|
// input's value is not change the second time someone
|
|
// inputs the same file again, therefore we need to reset its value
|
|
this.refs.fileinput.getDOMNode().value = '';
|
|
this.props.handleResumeFile(fileId);
|
|
},
|
|
|
|
handleOnClick() {
|
|
let evt;
|
|
// when multiple is set to false and the user already uploaded a piece,
|
|
// do not propagate event
|
|
if(this.props.dropzoneInactive) {
|
|
// if there is a handle function for doing stuff
|
|
// when the dropzone is inactive, then call it
|
|
if(this.props.onInactive) {
|
|
this.props.onInactive();
|
|
}
|
|
return;
|
|
}
|
|
|
|
try {
|
|
evt = new MouseEvent('click', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
} catch(e) {
|
|
// For browsers that do not support the new MouseEvent syntax
|
|
evt = document.createEvent('MouseEvents');
|
|
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
|
|
}
|
|
|
|
this.refs.fileinput.getDOMNode().dispatchEvent(evt);
|
|
},
|
|
|
|
render: function () {
|
|
const {
|
|
filesToUpload,
|
|
dropzoneInactive,
|
|
className,
|
|
hashingProgress,
|
|
handleCancelHashing,
|
|
multiple,
|
|
enableLocalHashing,
|
|
uploadMethod,
|
|
fileClassToUpload,
|
|
areAssetsDownloadable,
|
|
areAssetsEditable,
|
|
allowedExtensions } = this.props;
|
|
|
|
// has files only is true if there are files that do not have the status deleted or canceled
|
|
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 += ' file-drag-and-drop';
|
|
|
|
// if !== -2: triggers a FileDragAndDrop-global spinner
|
|
if(hashingProgress !== -2) {
|
|
return (
|
|
<div className={className}>
|
|
<div className="file-drag-and-drop-hashing-dialog">
|
|
<p>{getLangText('Computing hash(es)... This may take a few minutes.')}</p>
|
|
<p>
|
|
<a onClick={handleCancelHashing}> {getLangText('Cancel hashing')}</a>
|
|
</p>
|
|
<ProgressBar
|
|
now={Math.ceil(hashingProgress)}
|
|
label="%(percent)s%"
|
|
className="ascribe-progress-bar"/>
|
|
</div>
|
|
</div>
|
|
);
|
|
} else {
|
|
return (
|
|
<div
|
|
className={updatedClassName}
|
|
onDrag={this.handleDrop}
|
|
onDragOver={this.handleDragOver}
|
|
onDrop={this.handleDrop}>
|
|
<FileDragAndDropDialog
|
|
multipleFiles={multiple}
|
|
hasFiles={hasFiles}
|
|
onClick={this.handleOnClick}
|
|
enableLocalHashing={enableLocalHashing}
|
|
uploadMethod={uploadMethod}
|
|
fileClassToUpload={fileClassToUpload} />
|
|
<FileDragAndDropPreviewIterator
|
|
files={filesToUpload}
|
|
handleDeleteFile={this.handleDeleteFile}
|
|
handleCancelFile={this.handleCancelFile}
|
|
handlePauseFile={this.handlePauseFile}
|
|
handleResumeFile={this.handleResumeFile}
|
|
areAssetsDownloadable={areAssetsDownloadable}
|
|
areAssetsEditable={areAssetsEditable}/>
|
|
{/*
|
|
Opera doesn't trigger simulated click events
|
|
if the targeted input has `display:none` set.
|
|
Which means we need to set its visibility to hidden
|
|
instead of using `display:none`.
|
|
|
|
See:
|
|
- http://stackoverflow.com/questions/12880604/jquery-triggerclick-not-working-on-opera-if-the-element-is-not-displayed
|
|
*/}
|
|
<input
|
|
multiple={multiple}
|
|
ref="fileinput"
|
|
type="file"
|
|
style={{
|
|
visibility: 'hidden',
|
|
position: 'absolute',
|
|
top: 0,
|
|
height: 0,
|
|
width: 0
|
|
}}
|
|
onChange={this.handleDrop}
|
|
accept={allowedExtensions}/>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
export default FileDragAndDrop;
|