mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
Remove old uploaders
This commit is contained in:
parent
819b367896
commit
1069121e07
@ -1,119 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Property from './../ascribe_forms/property';
|
|
||||||
|
|
||||||
import ReactS3FineUploader from './../ascribe_uploader/react_s3_fine_uploader';
|
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
|
||||||
import { ValidationTypes } from '../../constants/uploader_constants';
|
|
||||||
|
|
||||||
import { makeCsrfHeader } from '../../utils/csrf';
|
|
||||||
import { getLangText } from '../../utils/lang';
|
|
||||||
import { resolveUrl } from '../../utils/url_resolver';
|
|
||||||
|
|
||||||
|
|
||||||
const { func, bool, number, object, string, arrayOf } = React.PropTypes;
|
|
||||||
|
|
||||||
let FurtherDetailsFileuploader = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
pieceId: number.isRequired,
|
|
||||||
|
|
||||||
editable: bool,
|
|
||||||
label: string,
|
|
||||||
otherData: arrayOf(object),
|
|
||||||
|
|
||||||
// Props for ReactS3FineUploader
|
|
||||||
areAssetsDownloadable: bool,
|
|
||||||
isReadyForFormSubmission: func,
|
|
||||||
submitFile: func, // TODO: rename to onSubmitFile
|
|
||||||
onValidationFailed: func,
|
|
||||||
multiple: bool,
|
|
||||||
setIsUploadReady: func, //TODO: rename to setIsUploaderValidated
|
|
||||||
showErrorPrompt: bool,
|
|
||||||
validation: ReactS3FineUploader.propTypes.validation
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
areAssetsDownloadable: true,
|
|
||||||
label: getLangText('Additional files'),
|
|
||||||
multiple: false,
|
|
||||||
validation: ValidationTypes.additionalData
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { editable,
|
|
||||||
isReadyForFormSubmission,
|
|
||||||
multiple,
|
|
||||||
onValidationFailed,
|
|
||||||
otherData,
|
|
||||||
pieceId,
|
|
||||||
setIsUploadReady,
|
|
||||||
showErrorPrompt,
|
|
||||||
submitFile,
|
|
||||||
validation } = this.props;
|
|
||||||
|
|
||||||
// Essentially there a three cases important to the fileuploader
|
|
||||||
//
|
|
||||||
// 1. there is no other_data => do not show the fileuploader at all (where otherData is now an array)
|
|
||||||
// 2. there is other_data, but user has no edit rights => show fileuploader but without action buttons
|
|
||||||
// 3. both other_data and editable are defined or true => show fileuploader with all action buttons
|
|
||||||
if (!editable && (!otherData || otherData.length === 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let otherDataIds = otherData ? otherData.map((data) => data.id).join() : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Property
|
|
||||||
name="other_data_key"
|
|
||||||
label={this.props.label}>
|
|
||||||
<ReactS3FineUploader
|
|
||||||
areAssetsDownloadable
|
|
||||||
areAssetsEditable={editable}
|
|
||||||
createBlobRoutine={{
|
|
||||||
url: resolveUrl('blob_otherdatas'),
|
|
||||||
pieceId: pieceId
|
|
||||||
}}
|
|
||||||
deleteFile={{
|
|
||||||
enabled: true,
|
|
||||||
method: 'DELETE',
|
|
||||||
endpoint: `${AppConstants.serverUrl}/s3/delete`,
|
|
||||||
customHeaders: makeCsrfHeader()
|
|
||||||
}}
|
|
||||||
isReadyForFormSubmission={isReadyForFormSubmission}
|
|
||||||
keyRoutine={{
|
|
||||||
url: `${AppConstants.serverUrl}/s3/key/`,
|
|
||||||
fileClass: 'otherdata',
|
|
||||||
pieceId: pieceId
|
|
||||||
}}
|
|
||||||
multiple={multiple}
|
|
||||||
onValidationFailed={onValidationFailed}
|
|
||||||
setIsUploadReady={setIsUploadReady}
|
|
||||||
session={{
|
|
||||||
endpoint: `${AppConstants.serverUrl}/api/blob/otherdatas/fineuploader_session/`,
|
|
||||||
customHeaders: makeCsrfHeader(),
|
|
||||||
params: {
|
|
||||||
'pk': otherDataIds
|
|
||||||
},
|
|
||||||
cors: {
|
|
||||||
expected: true,
|
|
||||||
sendCredentials: true
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
signature={{
|
|
||||||
endpoint: `${AppConstants.serverUrl}/s3/signature/`,
|
|
||||||
customHeaders: makeCsrfHeader()
|
|
||||||
}}
|
|
||||||
submitFile={submitFile}
|
|
||||||
showErrorPrompt={showErrorPrompt}
|
|
||||||
validation={validation} />
|
|
||||||
</Property>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FurtherDetailsFileuploader;
|
|
@ -1,141 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
|
|
||||||
import FileDragAndDrop from '../ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop';
|
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
|
||||||
|
|
||||||
import { makeCsrfHeader } from '../../utils/csrf';
|
|
||||||
|
|
||||||
|
|
||||||
const { func, bool, oneOf } = React.PropTypes;
|
|
||||||
|
|
||||||
const InputFineUploader = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
// isFineUploaderActive is used to lock react fine uploader in case
|
|
||||||
// a user is actually not logged in already to prevent him from droping files
|
|
||||||
// before login in
|
|
||||||
isFineUploaderActive: bool,
|
|
||||||
|
|
||||||
// provided by Property
|
|
||||||
disabled: bool,
|
|
||||||
onChange: func,
|
|
||||||
|
|
||||||
// Props for ReactS3FineUploader
|
|
||||||
areAssetsDownloadable: bool,
|
|
||||||
createBlobRoutine: ReactS3FineUploader.propTypes.createBlobRoutine,
|
|
||||||
enableLocalHashing: bool,
|
|
||||||
fileClassToUpload: ReactS3FineUploader.propTypes.fileClassToUpload,
|
|
||||||
fileInputElement: ReactS3FineUploader.propTypes.fileInputElement,
|
|
||||||
isReadyForFormSubmission: func,
|
|
||||||
keyRoutine: ReactS3FineUploader.propTypes.keyRoutine,
|
|
||||||
handleChangedFile: func, // TODO: rename to onChangedFile
|
|
||||||
submitFile: func, // TODO: rename to onSubmitFile
|
|
||||||
onValidationFailed: func,
|
|
||||||
setIsUploadReady: func, //TODO: rename to setIsUploaderValidated
|
|
||||||
setWarning: func,
|
|
||||||
showErrorPrompt: bool,
|
|
||||||
uploadMethod: oneOf(['hash', 'upload']),
|
|
||||||
validation: ReactS3FineUploader.propTypes.validation,
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
fileInputElement: FileDragAndDrop
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
value: null,
|
|
||||||
file: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
submitFile(file) {
|
|
||||||
this.setState({
|
|
||||||
file,
|
|
||||||
value: file.key
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.state.value && typeof this.props.onChange === 'function') {
|
|
||||||
this.props.onChange({ target: { value: this.state.value } });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.props.submitFile === 'function') {
|
|
||||||
this.props.submitFile(file);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.refs.fineuploader.reset();
|
|
||||||
},
|
|
||||||
|
|
||||||
createBlobRoutine() {
|
|
||||||
const { fineuploader } = this.refs;
|
|
||||||
const { file } = this.state;
|
|
||||||
|
|
||||||
fineuploader.createBlob(file);
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { areAssetsDownloadable,
|
|
||||||
createBlobRoutine,
|
|
||||||
enableLocalHashing,
|
|
||||||
disabled,
|
|
||||||
fileClassToUpload,
|
|
||||||
fileInputElement,
|
|
||||||
handleChangedFile,
|
|
||||||
isFineUploaderActive,
|
|
||||||
isReadyForFormSubmission,
|
|
||||||
keyRoutine,
|
|
||||||
onValidationFailed,
|
|
||||||
setIsUploadReady,
|
|
||||||
setWarning,
|
|
||||||
showErrorPrompt,
|
|
||||||
uploadMethod,
|
|
||||||
validation } = this.props;
|
|
||||||
let editable = isFineUploaderActive;
|
|
||||||
|
|
||||||
// if disabled is actually set by property, we want to override
|
|
||||||
// isFineUploaderActive
|
|
||||||
if (typeof disabled !== 'undefined') {
|
|
||||||
editable = !disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactS3FineUploader
|
|
||||||
ref="fineuploader"
|
|
||||||
fileInputElement={fileInputElement}
|
|
||||||
keyRoutine={keyRoutine}
|
|
||||||
createBlobRoutine={createBlobRoutine}
|
|
||||||
validation={validation}
|
|
||||||
submitFile={this.submitFile}
|
|
||||||
onValidationFailed={onValidationFailed}
|
|
||||||
setIsUploadReady={setIsUploadReady}
|
|
||||||
isReadyForFormSubmission={isReadyForFormSubmission}
|
|
||||||
areAssetsDownloadable={areAssetsDownloadable}
|
|
||||||
areAssetsEditable={editable}
|
|
||||||
setWarning={setWarning}
|
|
||||||
showErrorPrompt={showErrorPrompt}
|
|
||||||
signature={{
|
|
||||||
endpoint: `${AppConstants.serverUrl}/s3/signature/`,
|
|
||||||
customHeaders: makeCsrfHeader()
|
|
||||||
}}
|
|
||||||
deleteFile={{
|
|
||||||
enabled: true,
|
|
||||||
method: 'DELETE',
|
|
||||||
endpoint: `${AppConstants.serverUrl}/s3/delete`,
|
|
||||||
customHeaders: makeCsrfHeader()
|
|
||||||
}}
|
|
||||||
enableLocalHashing={enableLocalHashing}
|
|
||||||
uploadMethod={uploadMethod}
|
|
||||||
fileClassToUpload={fileClassToUpload}
|
|
||||||
handleChangedFile={handleChangedFile} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default InputFineUploader;
|
|
@ -1,253 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
|
||||||
|
|
||||||
import FileDragAndDropDialog from './file_drag_and_drop_dialog';
|
|
||||||
import FileDragAndDropErrorDialog from './file_drag_and_drop_error_dialog';
|
|
||||||
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
|
|
||||||
|
|
||||||
import { FileStatus } from '../react_s3_fine_uploader_utils';
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
|
|
||||||
|
|
||||||
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop
|
|
||||||
let FileDragAndDrop = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
areAssetsDownloadable: React.PropTypes.bool,
|
|
||||||
areAssetsEditable: React.PropTypes.bool,
|
|
||||||
multiple: React.PropTypes.bool,
|
|
||||||
dropzoneInactive: React.PropTypes.bool,
|
|
||||||
filesToUpload: React.PropTypes.array,
|
|
||||||
|
|
||||||
onDrop: React.PropTypes.func.isRequired,
|
|
||||||
onDragOver: React.PropTypes.func,
|
|
||||||
handleDeleteFile: React.PropTypes.func,
|
|
||||||
handleCancelFile: React.PropTypes.func,
|
|
||||||
handlePauseFile: React.PropTypes.func,
|
|
||||||
handleResumeFile: React.PropTypes.func,
|
|
||||||
handleRetryFiles: React.PropTypes.func,
|
|
||||||
|
|
||||||
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,
|
|
||||||
|
|
||||||
showError: React.PropTypes.bool,
|
|
||||||
errorClass: React.PropTypes.shape({
|
|
||||||
name: React.PropTypes.string,
|
|
||||||
prettifiedText: React.PropTypes.string
|
|
||||||
}),
|
|
||||||
|
|
||||||
// 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
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSelection() {
|
|
||||||
this.refs.fileSelector.value = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDragOver(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (typeof this.props.onDragOver === 'function') {
|
|
||||||
this.props.onDragOver(event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDrop(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
if (!this.props.dropzoneInactive) {
|
|
||||||
let files;
|
|
||||||
|
|
||||||
// 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 changed the second time someone
|
|
||||||
// inputs the same file again, therefore we need to reset its value
|
|
||||||
this.clearSelection();
|
|
||||||
this.props.handleDeleteFile(fileId);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCancelFile(fileId) {
|
|
||||||
// input's value is not changed the second time someone
|
|
||||||
// inputs the same file again, therefore we need to reset its value
|
|
||||||
this.clearSelection();
|
|
||||||
this.props.handleCancelFile(fileId);
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePauseFile(fileId) {
|
|
||||||
// input's value is not changed the second time someone
|
|
||||||
// inputs the same file again, therefore we need to reset its value
|
|
||||||
this.clearSelection();
|
|
||||||
this.props.handlePauseFile(fileId);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleResumeFile(fileId) {
|
|
||||||
// input's value is not changed the second time someone
|
|
||||||
// inputs the same file again, therefore we need to reset its value
|
|
||||||
this.clearSelection();
|
|
||||||
this.props.handleResumeFile(fileId);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleOnClick() {
|
|
||||||
// do not propagate event if the drop zone's inactive,
|
|
||||||
// for example when multiple is set to false and the user already uploaded a piece
|
|
||||||
if (!this.props.dropzoneInactive) {
|
|
||||||
let evt;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
evt.stopPropagation();
|
|
||||||
this.refs.fileSelector.dispatchEvent(evt);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getErrorDialog(failedFiles) {
|
|
||||||
const { errorClass } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FileDragAndDropErrorDialog
|
|
||||||
errorClass={errorClass}
|
|
||||||
files={failedFiles}
|
|
||||||
handleRetryFiles={this.props.handleRetryFiles} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getPreviewIterator() {
|
|
||||||
const { areAssetsDownloadable, areAssetsEditable, filesToUpload } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FileDragAndDropPreviewIterator
|
|
||||||
files={filesToUpload}
|
|
||||||
handleDeleteFile={this.handleDeleteFile}
|
|
||||||
handleCancelFile={this.handleCancelFile}
|
|
||||||
handlePauseFile={this.handlePauseFile}
|
|
||||||
handleResumeFile={this.handleResumeFile}
|
|
||||||
areAssetsDownloadable={areAssetsDownloadable}
|
|
||||||
areAssetsEditable={areAssetsEditable} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getUploadDialog() {
|
|
||||||
const { enableLocalHashing, fileClassToUpload, multiple, uploadMethod } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FileDragAndDropDialog
|
|
||||||
multipleFiles={multiple}
|
|
||||||
onClick={this.handleOnClick}
|
|
||||||
enableLocalHashing={enableLocalHashing}
|
|
||||||
uploadMethod={uploadMethod}
|
|
||||||
fileClassToUpload={fileClassToUpload} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function () {
|
|
||||||
const { allowedExtensions,
|
|
||||||
dropzoneInactive,
|
|
||||||
errorClass,
|
|
||||||
filesToUpload,
|
|
||||||
handleCancelHashing,
|
|
||||||
hashingProgress,
|
|
||||||
multiple,
|
|
||||||
showError } = this.props;
|
|
||||||
|
|
||||||
// has files only is true if there are files that do not have the status deleted, canceled, or failed
|
|
||||||
const hasFiles = filesToUpload
|
|
||||||
.filter((file) => {
|
|
||||||
return file.status !== FileStatus.DELETED &&
|
|
||||||
file.status !== FileStatus.CANCELED &&
|
|
||||||
file.status !== FileStatus.UPLOAD_FAILED &&
|
|
||||||
file.size !== -1;
|
|
||||||
})
|
|
||||||
.length > 0;
|
|
||||||
|
|
||||||
const failedFiles = filesToUpload.filter((file) => file.status === FileStatus.UPLOAD_FAILED);
|
|
||||||
let hasError = showError && errorClass && failedFiles.length > 0;
|
|
||||||
|
|
||||||
// if !== -2: triggers a FileDragAndDrop-global spinner
|
|
||||||
if (hashingProgress !== -2) {
|
|
||||||
return (
|
|
||||||
<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>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames('file-drag-and-drop', dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone', { 'has-files': hasFiles })}
|
|
||||||
onDrag={this.handleDrop}
|
|
||||||
onDragOver={this.handleDragOver}
|
|
||||||
onDrop={this.handleDrop}>
|
|
||||||
{hasError ? this.getErrorDialog(failedFiles) : this.getPreviewIterator()}
|
|
||||||
{!hasFiles && !hasError ? this.getUploadDialog() : null}
|
|
||||||
{/*
|
|
||||||
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="fileSelector"
|
|
||||||
type="file"
|
|
||||||
style={{
|
|
||||||
visibility: 'hidden',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
height: 0,
|
|
||||||
width: 0
|
|
||||||
}}
|
|
||||||
onChange={this.handleDrop}
|
|
||||||
accept={allowedExtensions}/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDrop;
|
|
@ -1,123 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Link } from 'react-router';
|
|
||||||
|
|
||||||
import { dragAndDropAvailable } from 'js-utility-belt/es6/feature_detection';
|
|
||||||
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
import { getCurrentQueryParams } from '../../../utils/url';
|
|
||||||
|
|
||||||
let FileDragAndDropDialog = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
multipleFiles: React.PropTypes.bool,
|
|
||||||
enableLocalHashing: React.PropTypes.bool,
|
|
||||||
uploadMethod: React.PropTypes.string,
|
|
||||||
onClick: 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
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getDragDialog(fileClass) {
|
|
||||||
if (dragAndDropAvailable) {
|
|
||||||
return [
|
|
||||||
<p className="file-drag-and-drop-dialog-title">{getLangText('Drag %s here', fileClass)}</p>,
|
|
||||||
<p>{getLangText('or')}</p>
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
enableLocalHashing,
|
|
||||||
fileClassToUpload,
|
|
||||||
multipleFiles,
|
|
||||||
onClick,
|
|
||||||
uploadMethod
|
|
||||||
} = this.props;
|
|
||||||
let dialogElement;
|
|
||||||
|
|
||||||
if (enableLocalHashing && !uploadMethod) {
|
|
||||||
const currentQueryParams = getCurrentQueryParams();
|
|
||||||
|
|
||||||
const queryParamsHash = Object.assign({}, currentQueryParams);
|
|
||||||
queryParamsHash.method = 'hash';
|
|
||||||
|
|
||||||
const queryParamsUpload = Object.assign({}, currentQueryParams);
|
|
||||||
queryParamsUpload.method = 'upload';
|
|
||||||
|
|
||||||
dialogElement = (
|
|
||||||
<div className="present-options">
|
|
||||||
<p className="file-drag-and-drop-dialog-title">{getLangText('Would you rather')}</p>
|
|
||||||
{/*
|
|
||||||
The frontend in live is hosted under /app,
|
|
||||||
Since `Link` is appending that base url, if its defined
|
|
||||||
by itself, we need to make sure to not set it at this point.
|
|
||||||
Otherwise it will be appended twice.
|
|
||||||
*/}
|
|
||||||
<Link
|
|
||||||
to={`/${window.location.pathname.split('/').pop()}`}
|
|
||||||
query={queryParamsHash}>
|
|
||||||
<span className="btn btn-default btn-sm">
|
|
||||||
{getLangText('Hash your work')}
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
<span> {getLangText('or')} </span>
|
|
||||||
<Link
|
|
||||||
to={`/${window.location.pathname.split('/').pop()}`}
|
|
||||||
query={queryParamsUpload}>
|
|
||||||
<span className="btn btn-default btn-sm">
|
|
||||||
{getLangText('Upload and hash your work')}
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (multipleFiles) {
|
|
||||||
dialogElement = [
|
|
||||||
this.getDragDialog(fileClassToUpload.plural),
|
|
||||||
(<span
|
|
||||||
key='mutlipleFilesBtn'
|
|
||||||
className="btn btn-default"
|
|
||||||
onClick={onClick}>
|
|
||||||
{getLangText('choose %s to upload', fileClassToUpload.plural)}
|
|
||||||
</span>)
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
|
|
||||||
: getLangText('choose a %s to upload', fileClassToUpload.singular);
|
|
||||||
|
|
||||||
dialogElement = [
|
|
||||||
this.getDragDialog(fileClassToUpload.singular),
|
|
||||||
(<span
|
|
||||||
key='singleFileBtn'
|
|
||||||
className="btn btn-default"
|
|
||||||
onClick={onClick}>
|
|
||||||
{dialog}
|
|
||||||
</span>)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="file-drag-and-drop-dialog">
|
|
||||||
<div className="hidden-print">
|
|
||||||
{dialogElement}
|
|
||||||
</div>
|
|
||||||
{/* Hide the uploader and just show that there's been on files uploaded yet when printing */}
|
|
||||||
<p className="text-align-center visible-print">
|
|
||||||
{getLangText('No files uploaded')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropDialog;
|
|
@ -1,86 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { ErrorClasses } from '../../../constants/error_constants';
|
|
||||||
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
|
|
||||||
let FileDragAndDropErrorDialog = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
errorClass: React.PropTypes.shape({
|
|
||||||
name: React.PropTypes.string,
|
|
||||||
prettifiedText: React.PropTypes.string
|
|
||||||
}).isRequired,
|
|
||||||
files: React.PropTypes.array.isRequired,
|
|
||||||
handleRetryFiles: React.PropTypes.func.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
getRetryButton(text, openIntercom) {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className='btn btn-default'
|
|
||||||
onClick={() => {
|
|
||||||
if (openIntercom) {
|
|
||||||
window.Intercom('showNewMessage', getLangText("I'm having trouble uploading my file."));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.retryAllFiles()
|
|
||||||
}}>
|
|
||||||
{getLangText(text)}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getContactUsDetail() {
|
|
||||||
return (
|
|
||||||
<div className='file-drag-and-drop-error'>
|
|
||||||
<h4>{getLangText('Let us help you')}</h4>
|
|
||||||
<p>{getLangText('Still having problems? Send us a message.')}</p>
|
|
||||||
{this.getRetryButton('Contact us', true)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getErrorDetail(multipleFiles) {
|
|
||||||
const { errorClass: { prettifiedText }, files } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='file-drag-and-drop-error'>
|
|
||||||
<div className={classNames('file-drag-and-drop-error-detail', { 'file-drag-and-drop-error-detail-multiple-files': multipleFiles })}>
|
|
||||||
<h4>{getLangText(multipleFiles ? 'Some files did not upload correctly'
|
|
||||||
: 'Error uploading the file!')}
|
|
||||||
</h4>
|
|
||||||
<p>{prettifiedText}</p>
|
|
||||||
{this.getRetryButton('Retry')}
|
|
||||||
</div>
|
|
||||||
<span className={classNames('file-drag-and-drop-error-icon-container', { 'file-drag-and-drop-error-icon-container-multiple-files': multipleFiles })}>
|
|
||||||
<span className='ascribe-icon icon-ascribe-thin-cross'></span>
|
|
||||||
</span>
|
|
||||||
<div className='file-drag-and-drop-error-file-names'>
|
|
||||||
<ul>
|
|
||||||
{files.map((file) => (<li key={file.id} className='file-name'>{file.originalName}</li>))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
retryAllFiles() {
|
|
||||||
const { files, handleRetryFiles } = this.props;
|
|
||||||
handleRetryFiles(files.map(file => file.id));
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { errorClass: { name: errorName }, files } = this.props;
|
|
||||||
|
|
||||||
const multipleFiles = files.length > 1;
|
|
||||||
const contactUs = errorName === ErrorClasses.upload.contactUs.name;
|
|
||||||
|
|
||||||
return contactUs ? this.getContactUsDetail() : this.getErrorDetail(multipleFiles);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropErrorDialog;
|
|
@ -1,152 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image';
|
|
||||||
import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other';
|
|
||||||
|
|
||||||
import { FileStatus } from '../react_s3_fine_uploader_utils';
|
|
||||||
import { extractFileExtensionFromString } from '../../../utils/file';
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
import { truncateText } from '../../../utils/text';
|
|
||||||
|
|
||||||
|
|
||||||
const { shape, string, number, func, bool } = React.PropTypes;
|
|
||||||
|
|
||||||
const FileDragAndDropPreview = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
file: shape({
|
|
||||||
url: string,
|
|
||||||
type: string,
|
|
||||||
progress: number,
|
|
||||||
id: number,
|
|
||||||
status: string,
|
|
||||||
s3Url: string,
|
|
||||||
s3UrlSafe: string
|
|
||||||
}).isRequired,
|
|
||||||
|
|
||||||
areAssetsDownloadable: bool,
|
|
||||||
areAssetsEditable: bool,
|
|
||||||
handleDeleteFile: func,
|
|
||||||
handleCancelFile: func,
|
|
||||||
handlePauseFile: func,
|
|
||||||
handleResumeFile: func,
|
|
||||||
numberOfDisplayedFiles: number
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleUploadProcess() {
|
|
||||||
const { file, handlePauseFile, handleResumeFile } = this.props;
|
|
||||||
|
|
||||||
if (file.status === FileStatus.UPLOADING) {
|
|
||||||
handlePauseFile(file.id);
|
|
||||||
} else if (file.status === FileStatus.PAUSED) {
|
|
||||||
handleResumeFile(file.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDeleteFile() {
|
|
||||||
const { file,
|
|
||||||
handleDeleteFile,
|
|
||||||
handleCancelFile } = this.props;
|
|
||||||
// `handleDeleteFile` is optional, so if its not submitted, don't run it
|
|
||||||
//
|
|
||||||
// For delete though, we only want to trigger it, when we're sure that
|
|
||||||
// the file has *completely* been uploaded to S3 and call now also be
|
|
||||||
// deleted using an HTTP DELETE request.
|
|
||||||
if (handleDeleteFile &&
|
|
||||||
file.progress === 100 &&
|
|
||||||
(file.status === FileStatus.UPLOAD_SUCCESSFUL || file.status === FileStatus.ONLINE) &&
|
|
||||||
file.s3UrlSafe) {
|
|
||||||
handleDeleteFile(file.id);
|
|
||||||
} else if (handleCancelFile) {
|
|
||||||
handleCancelFile(file.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDownloadFile() {
|
|
||||||
if (this.props.file.s3Url) {
|
|
||||||
// This simply opens a new browser tab with the url provided
|
|
||||||
open(this.props.file.s3Url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getFileName() {
|
|
||||||
const { numberOfDisplayedFiles, file } = this.props;
|
|
||||||
|
|
||||||
if (numberOfDisplayedFiles === 1) {
|
|
||||||
return (
|
|
||||||
<span className="file-name">
|
|
||||||
{truncateText(file.name, 30, `(...).${extractFileExtensionFromString(file.name)}`)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getRemoveButton() {
|
|
||||||
if (this.props.areAssetsEditable) {
|
|
||||||
return (
|
|
||||||
<div className="delete-file">
|
|
||||||
<span
|
|
||||||
className="glyphicon glyphicon-remove text-center"
|
|
||||||
aria-hidden="true"
|
|
||||||
title={getLangText('Remove file')}
|
|
||||||
onClick={this.handleDeleteFile}/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { file,
|
|
||||||
areAssetsDownloadable,
|
|
||||||
numberOfDisplayedFiles } = this.props;
|
|
||||||
const innerStyle = numberOfDisplayedFiles === 1 ? { verticalAlign: 'middle' } : null;
|
|
||||||
const outerStyle = numberOfDisplayedFiles !== 1 ? { display: 'inline-block' } : null;
|
|
||||||
|
|
||||||
let previewElement;
|
|
||||||
|
|
||||||
// Decide whether an image or a placeholder picture should be displayed
|
|
||||||
// If a file has its `thumbnailUrl` defined, then we display it also as an image
|
|
||||||
if (file.type.split('/')[0] === 'image' || file.thumbnailUrl) {
|
|
||||||
previewElement = (
|
|
||||||
<FileDragAndDropPreviewImage
|
|
||||||
onClick={this.handleDeleteFile}
|
|
||||||
progress={file.progress}
|
|
||||||
url={file.thumbnailUrl || file.url}
|
|
||||||
toggleUploadProcess={this.toggleUploadProcess}
|
|
||||||
areAssetsDownloadable={areAssetsDownloadable}
|
|
||||||
downloadUrl={file.s3UrlSafe}
|
|
||||||
showProgress={numberOfDisplayedFiles > 1} />
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
previewElement = (
|
|
||||||
<FileDragAndDropPreviewOther
|
|
||||||
onClick={this.handleDeleteFile}
|
|
||||||
progress={file.progress}
|
|
||||||
type={extractFileExtensionFromString(file.name)}
|
|
||||||
toggleUploadProcess={this.toggleUploadProcess}
|
|
||||||
areAssetsDownloadable={areAssetsDownloadable}
|
|
||||||
downloadUrl={file.s3UrlSafe}
|
|
||||||
showProgress={numberOfDisplayedFiles > 1} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={outerStyle}>
|
|
||||||
<div
|
|
||||||
style={innerStyle}
|
|
||||||
className="file-drag-and-drop-position">
|
|
||||||
{this.getRemoveButton()}
|
|
||||||
{previewElement}
|
|
||||||
</div>
|
|
||||||
{this.getFileName()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropPreview;
|
|
@ -1,85 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
|
||||||
|
|
||||||
import AclProxy from '../../acl_proxy';
|
|
||||||
import AscribeSpinner from '../../ascribe_spinner';
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
|
|
||||||
|
|
||||||
const { number, string, func, bool } = React.PropTypes;
|
|
||||||
|
|
||||||
const FileDragAndDropPreviewImage = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
progress: number,
|
|
||||||
url: string,
|
|
||||||
toggleUploadProcess: func,
|
|
||||||
downloadUrl: string,
|
|
||||||
areAssetsDownloadable: bool,
|
|
||||||
showProgress: bool
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
paused: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleUploadProcess(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
paused: !this.state.paused
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.toggleUploadProcess();
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { url,
|
|
||||||
progress,
|
|
||||||
areAssetsDownloadable,
|
|
||||||
downloadUrl,
|
|
||||||
showProgress } = this.props;
|
|
||||||
const imageStyle = {
|
|
||||||
backgroundImage: 'url("' + url + '")',
|
|
||||||
backgroundSize: 'cover'
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionSymbol;
|
|
||||||
|
|
||||||
// only if assets are actually downloadable, there should be a download icon if the process is already at
|
|
||||||
// 100%. If not, no actionSymbol should be displayed
|
|
||||||
if(progress === 100 && areAssetsDownloadable) {
|
|
||||||
actionSymbol = <a href={downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title={getLangText('Download file')}/>;
|
|
||||||
} else if(progress >= 0 && progress < 100) {
|
|
||||||
actionSymbol = (
|
|
||||||
<div className="spinner-file">
|
|
||||||
<AscribeSpinner color='dark-blue' size='md' />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
actionSymbol = (
|
|
||||||
<span className='ascribe-icon icon-ascribe-ok action-file'/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="file-drag-and-drop-preview-image hidden-print"
|
|
||||||
style={imageStyle}>
|
|
||||||
<AclProxy
|
|
||||||
show={showProgress}>
|
|
||||||
<ProgressBar
|
|
||||||
now={Math.ceil(progress)}
|
|
||||||
className="ascribe-progress-bar ascribe-progress-bar-xs"/>
|
|
||||||
</AclProxy>
|
|
||||||
{actionSymbol}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropPreviewImage;
|
|
@ -1,60 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import FileDragAndDropPreview from './file_drag_and_drop_preview';
|
|
||||||
import FileDragAndDropPreviewProgress from './file_drag_and_drop_preview_progress';
|
|
||||||
|
|
||||||
import { displayValidFilesFilter } from '../react_s3_fine_uploader_utils';
|
|
||||||
|
|
||||||
|
|
||||||
let FileDragAndDropPreviewIterator = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
files: React.PropTypes.array,
|
|
||||||
handleDeleteFile: React.PropTypes.func,
|
|
||||||
handleCancelFile: React.PropTypes.func,
|
|
||||||
handlePauseFile: React.PropTypes.func,
|
|
||||||
handleResumeFile: React.PropTypes.func,
|
|
||||||
areAssetsDownloadable: React.PropTypes.bool,
|
|
||||||
areAssetsEditable: React.PropTypes.bool
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let {
|
|
||||||
files,
|
|
||||||
handleDeleteFile,
|
|
||||||
handleCancelFile,
|
|
||||||
handlePauseFile,
|
|
||||||
handleResumeFile,
|
|
||||||
areAssetsDownloadable,
|
|
||||||
areAssetsEditable
|
|
||||||
} = this.props;
|
|
||||||
files = files.filter(displayValidFilesFilter);
|
|
||||||
|
|
||||||
if(files && files.length > 0) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{files.map((file, i) => {
|
|
||||||
return (
|
|
||||||
<FileDragAndDropPreview
|
|
||||||
key={i}
|
|
||||||
file={file}
|
|
||||||
handleDeleteFile={handleDeleteFile}
|
|
||||||
handleCancelFile={handleCancelFile}
|
|
||||||
handlePauseFile={handlePauseFile}
|
|
||||||
handleResumeFile={handleResumeFile}
|
|
||||||
areAssetsDownloadable={areAssetsDownloadable}
|
|
||||||
areAssetsEditable={areAssetsEditable}
|
|
||||||
numberOfDisplayedFiles={files.length}/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<FileDragAndDropPreviewProgress files={files} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropPreviewIterator;
|
|
@ -1,87 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
|
||||||
|
|
||||||
import AscribeSpinner from '../../ascribe_spinner';
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
|
|
||||||
|
|
||||||
const { string, number, bool, func } = React.PropTypes;
|
|
||||||
|
|
||||||
const FileDragAndDropPreviewOther = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
type: string,
|
|
||||||
progress: number,
|
|
||||||
areAssetsDownloadable: bool,
|
|
||||||
toggleUploadProcess: func,
|
|
||||||
downloadUrl: string,
|
|
||||||
showProgress: bool
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
paused: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleUploadProcess(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
paused: !this.state.paused
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.toggleUploadProcess();
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { progress,
|
|
||||||
areAssetsDownloadable,
|
|
||||||
downloadUrl,
|
|
||||||
type,
|
|
||||||
showProgress } = this.props;
|
|
||||||
const style = !showProgress ? { visibility: 'hidden' } : null;
|
|
||||||
let actionSymbol;
|
|
||||||
|
|
||||||
// only if assets are actually downloadable, there should be a
|
|
||||||
// download icon if the process is already at 100%.
|
|
||||||
// If not, no actionSymbol should be displayed
|
|
||||||
if (progress === 100 && areAssetsDownloadable) {
|
|
||||||
actionSymbol = (
|
|
||||||
<a
|
|
||||||
href={downloadUrl}
|
|
||||||
target="_blank"
|
|
||||||
className="glyphicon glyphicon-download action-file"
|
|
||||||
aria-hidden="true"
|
|
||||||
title={getLangText('Download file')} />
|
|
||||||
);
|
|
||||||
} else if (progress >= 0 && progress < 100) {
|
|
||||||
actionSymbol = (
|
|
||||||
<div className="spinner-file">
|
|
||||||
<AscribeSpinner color='dark-blue' size='md' />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
actionSymbol = (
|
|
||||||
<span className='ascribe-icon icon-ascribe-ok action-file' />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="file-drag-and-drop-preview">
|
|
||||||
<ProgressBar
|
|
||||||
now={Math.ceil(progress)}
|
|
||||||
style={style}
|
|
||||||
className="ascribe-progress-bar ascribe-progress-bar-xs" />
|
|
||||||
<div className="file-drag-and-drop-preview-other">
|
|
||||||
{actionSymbol}
|
|
||||||
<p style={style}>{'.' + (type ? type : 'file')}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropPreviewOther;
|
|
@ -1,58 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
|
||||||
|
|
||||||
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
|
|
||||||
|
|
||||||
|
|
||||||
const FileDragAndDropPreviewProgress = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
files: React.PropTypes.array
|
|
||||||
},
|
|
||||||
|
|
||||||
calcOverallFileSize() {
|
|
||||||
let overallFileSize = 0;
|
|
||||||
let files = this.props.files.filter(displayValidProgressFilesFilter);
|
|
||||||
|
|
||||||
// We just sum up all files' sizes
|
|
||||||
for(let i = 0; i < files.length; i++) {
|
|
||||||
overallFileSize += files[i].size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return overallFileSize;
|
|
||||||
},
|
|
||||||
|
|
||||||
calcOverallProgress() {
|
|
||||||
let overallProgress = 0;
|
|
||||||
let overallFileSize = this.calcOverallFileSize();
|
|
||||||
let files = this.props.files.filter(displayValidProgressFilesFilter);
|
|
||||||
|
|
||||||
// We calculate the overall progress by summing the individuals
|
|
||||||
// files' progresses in relation to their size
|
|
||||||
for(let i = 0; i < files.length; i++) {
|
|
||||||
overallProgress += files[i].size / overallFileSize * files[i].progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
return overallProgress;
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const files = this.props.files.filter(displayValidProgressFilesFilter);
|
|
||||||
const style = !files.length ? { display: 'none' } : null;
|
|
||||||
let overallProgress = this.calcOverallProgress();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{marginTop: '1.3em'}}>
|
|
||||||
<ProgressBar
|
|
||||||
now={Math.ceil(overallProgress)}
|
|
||||||
label={'%(percent)s%'}
|
|
||||||
className="ascribe-progress-bar"
|
|
||||||
style={style} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FileDragAndDropPreviewProgress;
|
|
@ -1,188 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { displayValidProgressFilesFilter, FileStatus } from '../react_s3_fine_uploader_utils';
|
|
||||||
import { getLangText } from '../../../utils/lang';
|
|
||||||
import { truncateText } from '../../../utils/text';
|
|
||||||
|
|
||||||
const { func, array, bool, shape, string } = React.PropTypes;
|
|
||||||
|
|
||||||
|
|
||||||
export default function UploadButton({ className = 'btn btn-default btn-sm', showLabel = true } = {}) {
|
|
||||||
return React.createClass({
|
|
||||||
displayName: 'UploadButton',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onDrop: func.isRequired,
|
|
||||||
filesToUpload: array,
|
|
||||||
multiple: bool,
|
|
||||||
|
|
||||||
// For simplification purposes we're just going to use this prop as a
|
|
||||||
// label for the upload button
|
|
||||||
fileClassToUpload: shape({
|
|
||||||
singular: string,
|
|
||||||
plural: string
|
|
||||||
}),
|
|
||||||
|
|
||||||
allowedExtensions: string,
|
|
||||||
|
|
||||||
// provided by ReactS3FineUploader
|
|
||||||
handleCancelFile: func,
|
|
||||||
handleDeleteFile: func
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
disabled: this.getUploadingFiles().length !== 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
if(this.props.filesToUpload !== nextProps.filesToUpload) {
|
|
||||||
this.setState({
|
|
||||||
disabled: this.getUploadingFiles(nextProps.filesToUpload).length !== 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleDrop(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
let files = event.target.files;
|
|
||||||
|
|
||||||
if(typeof this.props.onDrop === 'function' && files) {
|
|
||||||
this.props.onDrop(files);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getUploadingFiles(filesToUpload = this.props.filesToUpload) {
|
|
||||||
return filesToUpload.filter((file) => file.status === FileStatus.UPLOADING);
|
|
||||||
},
|
|
||||||
|
|
||||||
getUploadedFile() {
|
|
||||||
return this.props.filesToUpload.filter((file) => file.status === FileStatus.UPLOAD_SUCCESSFUL)[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
clearSelection() {
|
|
||||||
this.refs.fileSelector.value = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
handleOnClick() {
|
|
||||||
if(!this.state.disabled) {
|
|
||||||
let evt;
|
|
||||||
|
|
||||||
// First, remove any currently uploading or uploaded items
|
|
||||||
this.onClickRemove();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
evt.stopPropagation();
|
|
||||||
this.refs.fileSelector.dispatchEvent(evt);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onClickRemove() {
|
|
||||||
const uploadingFiles = this.getUploadingFiles();
|
|
||||||
const uploadedFile = this.getUploadedFile();
|
|
||||||
|
|
||||||
this.clearSelection();
|
|
||||||
if(uploadingFiles.length) {
|
|
||||||
this.props.handleCancelFile(uploadingFiles[0].id);
|
|
||||||
} else if(uploadedFile && !uploadedFile.s3UrlSafe) {
|
|
||||||
this.props.handleCancelFile(uploadedFile.id);
|
|
||||||
} else if(uploadedFile && uploadedFile.s3UrlSafe) {
|
|
||||||
this.props.handleDeleteFile(uploadedFile.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getButtonLabel() {
|
|
||||||
let { filesToUpload, fileClassToUpload } = this.props;
|
|
||||||
|
|
||||||
// filter invalid files that might have been deleted or canceled...
|
|
||||||
filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter);
|
|
||||||
|
|
||||||
if(this.getUploadingFiles().length !== 0) {
|
|
||||||
return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%';
|
|
||||||
} else {
|
|
||||||
return fileClassToUpload.singular;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getUploadedFileLabel() {
|
|
||||||
if (showLabel) {
|
|
||||||
const uploadedFile = this.getUploadedFile();
|
|
||||||
const uploadingFiles = this.getUploadingFiles();
|
|
||||||
|
|
||||||
if (uploadingFiles.length) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{` ${truncateText(uploadingFiles[0].name, 40)} `}
|
|
||||||
[<a onClick={this.onClickRemove}>{getLangText('cancel upload')}</a>]
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else if (uploadedFile) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<span className='ascribe-icon icon-ascribe-ok'/>
|
|
||||||
{` ${truncateText(uploadedFile.name, 40)} `}
|
|
||||||
[<a onClick={this.onClickRemove}>{getLangText('remove')}</a>]
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <span>{getLangText('No file chosen')}</span>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { allowedExtensions, multiple } = this.props;
|
|
||||||
const { disabled } = this.state;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We do not want a button that submits here.
|
|
||||||
* As UploadButton could be used in forms that want to be submitted independent
|
|
||||||
* of clicking the selector.
|
|
||||||
* Therefore the wrapping component needs to be an `anchor` tag instead of a `button`
|
|
||||||
*/
|
|
||||||
return (
|
|
||||||
<div className={classNames('ascribe-upload-button', {'ascribe-upload-button-has-label': showLabel})}>
|
|
||||||
{/*
|
|
||||||
The button needs to be of `type="button"` as it would
|
|
||||||
otherwise submit the form its in.
|
|
||||||
*/}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={this.handleOnClick}
|
|
||||||
className={className}
|
|
||||||
disabled={disabled}>
|
|
||||||
{this.getButtonLabel()}
|
|
||||||
<input
|
|
||||||
multiple={multiple}
|
|
||||||
ref="fileSelector"
|
|
||||||
type="file"
|
|
||||||
style={{
|
|
||||||
display: 'none',
|
|
||||||
height: 0,
|
|
||||||
width: 0
|
|
||||||
}}
|
|
||||||
onChange={this.handleDrop}
|
|
||||||
accept={allowedExtensions}/>
|
|
||||||
</button>
|
|
||||||
{this.getUploadedFileLabel()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,6 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import request from '../utils/request';
|
import request from '../utils/request';
|
||||||
|
|
||||||
let S3Fetcher = {
|
const S3Fetcher = {
|
||||||
/**
|
|
||||||
* Fetch the registered applications of a user from the API.
|
|
||||||
*/
|
|
||||||
deleteFile(key, bucket) {
|
deleteFile(key, bucket) {
|
||||||
return request('s3_delete_file', {
|
return request('s3_delete_file', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
Loading…
Reference in New Issue
Block a user