mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
Add FileDragAndDropError
This commit is contained in:
parent
04d7e6951a
commit
79780cfb3a
@ -22,6 +22,7 @@ let FurtherDetailsFileuploader = React.createClass({
|
||||
|
||||
// Props for ReactS3FineUploader
|
||||
multiple: bool,
|
||||
showErrorPrompt: bool,
|
||||
submitFile: func, // TODO: rename to onSubmitFile
|
||||
|
||||
setIsUploadReady: func, //TODO: rename to setIsUploaderValidated
|
||||
@ -42,6 +43,7 @@ let FurtherDetailsFileuploader = React.createClass({
|
||||
otherData,
|
||||
pieceId,
|
||||
setIsUploadReady,
|
||||
showErrorPrompt,
|
||||
submitFile } = this.props;
|
||||
|
||||
// Essentially there a three cases important to the fileuploader
|
||||
@ -101,8 +103,9 @@ let FurtherDetailsFileuploader = React.createClass({
|
||||
}
|
||||
}}
|
||||
areAssetsDownloadable={true}
|
||||
areAssetsEditable={this.props.editable}
|
||||
multiple={this.props.multiple} />
|
||||
areAssetsEditable={editable}
|
||||
multiple={multiple}
|
||||
showErrorPrompt={showErrorPrompt} />
|
||||
</Property>
|
||||
);
|
||||
}
|
||||
|
@ -173,7 +173,8 @@ let RegisterPieceForm = React.createClass({
|
||||
disabled={!isFineUploaderEditable}
|
||||
enableLocalHashing={hashLocally}
|
||||
uploadMethod={location.query.method}
|
||||
handleChangedFile={this.handleChangedDigitalWork}/>
|
||||
handleChangedFile={this.handleChangedDigitalWork}
|
||||
showErrorPrompt />
|
||||
</Property>
|
||||
<Property
|
||||
name="thumbnail_file"
|
||||
@ -196,7 +197,6 @@ let RegisterPieceForm = React.createClass({
|
||||
allowedExtensions: ['png', 'jpg', 'jpeg', 'gif']
|
||||
}}
|
||||
setIsUploadReady={this.setIsUploadReady('thumbnailKeyReady')}
|
||||
location={location}
|
||||
fileClassToUpload={{
|
||||
singular: getLangText('Select representative image'),
|
||||
plural: getLangText('Select representative images')
|
||||
|
@ -25,6 +25,7 @@ const InputFineUploader = React.createClass({
|
||||
|
||||
// Props for ReactS3FineUploader
|
||||
areAssetsDownloadable: bool,
|
||||
showErrorPrompt: bool,
|
||||
|
||||
handleChangedFile: func, // TODO: rename to onChangedFile
|
||||
submitFile: func, // TODO: rename to onSubmitFile
|
||||
@ -121,6 +122,7 @@ const InputFineUploader = React.createClass({
|
||||
fileClassToUpload,
|
||||
uploadMethod,
|
||||
handleChangedFile,
|
||||
showErrorPrompt,
|
||||
disabled } = this.props;
|
||||
let editable = isFineUploaderActive;
|
||||
|
||||
@ -142,6 +144,7 @@ const InputFineUploader = React.createClass({
|
||||
isReadyForFormSubmission={isReadyForFormSubmission}
|
||||
areAssetsDownloadable={areAssetsDownloadable}
|
||||
areAssetsEditable={editable}
|
||||
showErrorPrompt={showErrorPrompt}
|
||||
signature={{
|
||||
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||
customHeaders: {
|
||||
|
@ -39,6 +39,12 @@ let FileDragAndDrop = React.createClass({
|
||||
// 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({
|
||||
@ -144,6 +150,17 @@ let FileDragAndDrop = React.createClass({
|
||||
this.refs.fileSelector.getDOMNode().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;
|
||||
|
||||
@ -179,6 +196,8 @@ let FileDragAndDrop = React.createClass({
|
||||
hashingProgress,
|
||||
handleCancelHashing,
|
||||
multiple,
|
||||
showError,
|
||||
errorClass,
|
||||
fileClassToUpload,
|
||||
allowedExtensions } = this.props;
|
||||
|
||||
@ -216,6 +235,7 @@ let FileDragAndDrop = React.createClass({
|
||||
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
|
||||
|
@ -0,0 +1,86 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ErrorClasses } from '../../../constants/error_constants';
|
||||
|
||||
import { getLangText } from '../../../utils/lang_utils';
|
||||
|
||||
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 with uploading my file: "));
|
||||
}
|
||||
|
||||
this.retryAllFiles()
|
||||
}}>
|
||||
{getLangText(text)}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
getContactUsDetail() {
|
||||
return (
|
||||
<div className='file-drag-and-drop-error'>
|
||||
<h4>Let us help you</h4>
|
||||
<p>{getLangText('Still having problems? Give us a call!')}</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='file-drag-and-drop-error-icon'></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;
|
@ -35,6 +35,8 @@ const ReactS3FineUploader = React.createClass({
|
||||
propTypes: {
|
||||
areAssetsDownloadable: bool,
|
||||
areAssetsEditable: bool,
|
||||
errorNotificationMessage: string,
|
||||
showErrorPrompt: bool,
|
||||
|
||||
handleChangedFile: func, // for when a file is dropped or selected, TODO: rename to onChangedFile
|
||||
submitFile: func, // for when a file has been successfully uploaded, TODO: rename to onSubmitFile
|
||||
@ -152,8 +154,18 @@ const ReactS3FineUploader = React.createClass({
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
errorNotificationMessage: getLangText('Oops, we had a problem uploading your file. Please contact us if this happens repeatedly.'),
|
||||
showErrorPrompt: false,
|
||||
fileClassToUpload: {
|
||||
singular: getLangText('file'),
|
||||
plural: getLangText('files')
|
||||
},
|
||||
fileInputElement: FileDragAndDrop,
|
||||
|
||||
// FineUploader options
|
||||
autoUpload: true,
|
||||
debug: false,
|
||||
multiple: false,
|
||||
objectProperties: {
|
||||
acl: 'public-read',
|
||||
bucket: 'ascribe0'
|
||||
@ -195,22 +207,20 @@ const ReactS3FineUploader = React.createClass({
|
||||
name = name.slice(0, 15) + '...' + name.slice(-15);
|
||||
}
|
||||
return name;
|
||||
},
|
||||
multiple: false,
|
||||
defaultErrorMessage: getLangText('Unexpected error. Please contact us if this happens repeatedly.'),
|
||||
fileClassToUpload: {
|
||||
singular: getLangText('file'),
|
||||
plural: getLangText('files')
|
||||
},
|
||||
fileInputElement: FileDragAndDrop
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
filesToUpload: [],
|
||||
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()),
|
||||
uploader: this.createNewFineUploader(),
|
||||
csrfToken: getCookie(AppConstants.csrftoken),
|
||||
errorState: {
|
||||
manualRetryAttempt: 0,
|
||||
errorClass: undefined
|
||||
},
|
||||
uploadInProgress: false,
|
||||
|
||||
// -1: aborted
|
||||
// -2: uninitialized
|
||||
@ -228,7 +238,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
let potentiallyNewCSRFToken = getCookie(AppConstants.csrftoken);
|
||||
if(this.state.csrfToken !== potentiallyNewCSRFToken) {
|
||||
this.setState({
|
||||
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()),
|
||||
uploader: this.createNewFineUploader(),
|
||||
csrfToken: potentiallyNewCSRFToken
|
||||
});
|
||||
}
|
||||
@ -241,8 +251,12 @@ const ReactS3FineUploader = React.createClass({
|
||||
this.state.uploader.cancelAll();
|
||||
},
|
||||
|
||||
createNewFineUploader() {
|
||||
return new fineUploader.s3.FineUploaderBasic(this.propsToConfig());
|
||||
},
|
||||
|
||||
propsToConfig() {
|
||||
let objectProperties = this.props.objectProperties;
|
||||
const objectProperties = Object.assign({}, this.props.objectProperties);
|
||||
objectProperties.key = this.requestKey;
|
||||
|
||||
return {
|
||||
@ -263,6 +277,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
multiple: this.props.multiple,
|
||||
retry: this.props.retry,
|
||||
callbacks: {
|
||||
onAllComplete: this.onAllComplete,
|
||||
onComplete: this.onComplete,
|
||||
onCancel: this.onCancel,
|
||||
onProgress: this.onProgress,
|
||||
@ -408,6 +423,65 @@ const ReactS3FineUploader = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
checkFormSubmissionReady() {
|
||||
const { isReadyForFormSubmission, setIsUploadReady } = this.props;
|
||||
|
||||
// since the form validation props isReadyForFormSubmission and setIsUploadReady
|
||||
// are optional, we'll only trigger them when they're actually defined
|
||||
if (typeof isReadyForFormSubmission === 'function' && typeof setIsUploadReady === 'function') {
|
||||
// set uploadReady to true if the uploader's ready for submission
|
||||
setIsUploadReady(isReadyForFormSubmission(this.state.filesToUpload));
|
||||
} else {
|
||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
},
|
||||
|
||||
isFileValid(file) {
|
||||
const { validation } = this.props;
|
||||
|
||||
if (validation && file.size > validation.sizeLimit) {
|
||||
const fileSizeInMegaBytes = validation.sizeLimit / 1000000;
|
||||
|
||||
const notification = new GlobalNotificationModel(getLangText('A file you submitted is bigger than ' + fileSizeInMegaBytes + 'MB.'), 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
getUploadErrorClass({ type = 'upload', reason, xhr }) {
|
||||
const { manualRetryAttempt } = this.state.errorState;
|
||||
let matchedErrorClass;
|
||||
|
||||
// Use the contact us error class if they've retried a number of times
|
||||
// and are still unsuccessful
|
||||
if (manualRetryAttempt === RETRY_ATTEMPT_TO_SHOW_CONTACT_US) {
|
||||
matchedErrorClass = ErrorClasses.upload.contactUs;
|
||||
} else {
|
||||
matchedErrorClass = testErrorAgainstAll({ type, reason, xhr });
|
||||
|
||||
// If none found, show the next error message
|
||||
if (!matchedErrorClass) {
|
||||
matchedErrorClass = ErrorQueueStore.getNextError('upload');
|
||||
}
|
||||
}
|
||||
|
||||
return matchedErrorClass;
|
||||
},
|
||||
|
||||
getXhrErrorComment(xhr) {
|
||||
if (xhr) {
|
||||
return {
|
||||
response: xhr.response,
|
||||
url: xhr.responseURL,
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/* FineUploader specific callback function handlers */
|
||||
|
||||
onUploadChunk(id, name, chunkData) {
|
||||
@ -438,7 +512,14 @@ const ReactS3FineUploader = React.createClass({
|
||||
|
||||
this.setState({ startedChunks });
|
||||
}
|
||||
},
|
||||
|
||||
onAllComplete(succeed, failed) {
|
||||
if (this.state.uploadInProgress) {
|
||||
this.setState({
|
||||
uploadInProgress: false
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onComplete(id, name, res, xhr) {
|
||||
@ -464,29 +545,14 @@ const 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 submitFile
|
||||
// are optional, we'll only trigger them when they're actually defined
|
||||
if(this.props.submitFile) {
|
||||
if (typeof this.props.submitFile === 'function') {
|
||||
this.props.submitFile(files[id]);
|
||||
} else {
|
||||
console.warn('You didn\'t define submitFile as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
|
||||
// for explanation, check comment of if statement above
|
||||
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
|
||||
// also, lets check if after the completion of this upload,
|
||||
// the form is ready for submission or not
|
||||
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
||||
// if so, set uploadstatus to true
|
||||
this.props.setIsUploadReady(true);
|
||||
} else {
|
||||
this.props.setIsUploadReady(false);
|
||||
}
|
||||
} else {
|
||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
})
|
||||
.catch(this.onErrorPromiseProxy);
|
||||
this.checkFormSubmissionReady();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@ -502,42 +568,43 @@ const ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
onError(id, name, errorReason, xhr) {
|
||||
const { errorNotificationMessage, showErrorPrompt } = this.props;
|
||||
const { chunks, filesToUpload } = this.state;
|
||||
|
||||
console.logGlobal(errorReason, false, {
|
||||
files: this.state.filesToUpload,
|
||||
chunks: this.state.chunks,
|
||||
files: filesToUpload,
|
||||
chunks: chunks,
|
||||
xhr: this.getXhrErrorComment(xhr)
|
||||
});
|
||||
|
||||
this.props.setIsUploadReady(true);
|
||||
this.cancelUploads();
|
||||
let notificationMessage;
|
||||
|
||||
let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
if (showErrorPrompt) {
|
||||
notificationMessage = errorNotificationMessage;
|
||||
|
||||
getXhrErrorComment(xhr) {
|
||||
if (xhr) {
|
||||
return {
|
||||
response: xhr.response,
|
||||
url: xhr.responseURL,
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText
|
||||
};
|
||||
this.setStatusOfFile(id, FileStatus.UPLOAD_FAILED);
|
||||
|
||||
// If we've already found an error on this upload, just ignore other errors
|
||||
// that pop up. They'll likely pop up again when the user retries.
|
||||
if (!this.state.errorState.errorClass) {
|
||||
const errorState = React.addons.update(this.state.errorState, {
|
||||
errorClass: {
|
||||
$set: this.getUploadErrorClass({
|
||||
reason: errorReason,
|
||||
xhr
|
||||
})
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
isFileValid(file) {
|
||||
if(file.size > this.props.validation.sizeLimit) {
|
||||
|
||||
let fileSizeInMegaBytes = this.props.validation.sizeLimit / 1000000;
|
||||
|
||||
let notification = new GlobalNotificationModel(getLangText('A file you submitted is bigger than ' + fileSizeInMegaBytes + 'MB.'), 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
return false;
|
||||
this.setState({ errorState });
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
notificationMessage = errorReason || errorNotificationMessage;
|
||||
this.cancelUploads();
|
||||
}
|
||||
|
||||
const notification = new GlobalNotificationModel(notificationMessage, 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
onCancel(id) {
|
||||
@ -552,17 +619,18 @@ const ReactS3FineUploader = React.createClass({
|
||||
let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
// 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)) {
|
||||
// if so, set uploadstatus to true
|
||||
this.props.setIsUploadReady(true);
|
||||
} else {
|
||||
this.props.setIsUploadReady(false);
|
||||
}
|
||||
} else {
|
||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||
this.checkFormSubmissionReady();
|
||||
|
||||
// FineUploader's onAllComplete event doesn't fire if all files are cancelled
|
||||
// so we need to double check if this is the last file getting cancelled.
|
||||
//
|
||||
// Because we're calling FineUploader.getInProgress() in a cancel callback,
|
||||
// the current file getting cancelled is still considered to be in progress
|
||||
// so there will be one file left in progress when we're cancelling the last file.
|
||||
if (this.state.uploader.getInProgress() === 1) {
|
||||
this.setState({
|
||||
uploadInProgress: false
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -619,20 +687,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
}
|
||||
|
||||
// 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,
|
||||
// the form is ready for submission or not
|
||||
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
||||
// if so, set uploadstatus to true
|
||||
this.props.setIsUploadReady(true);
|
||||
} else {
|
||||
this.props.setIsUploadReady(false);
|
||||
}
|
||||
} else {
|
||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||
}
|
||||
this.checkFormSubmissionReady();
|
||||
},
|
||||
|
||||
handleDeleteFile(fileId) {
|
||||
@ -692,6 +747,27 @@ const ReactS3FineUploader = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
handleRetryFiles(fileIds) {
|
||||
let filesToUpload = this.state.filesToUpload;
|
||||
|
||||
if (fileIds.constructor !== Array) {
|
||||
fileIds = [ fileIds ];
|
||||
}
|
||||
|
||||
fileIds.forEach((fileId) => {
|
||||
this.state.uploader.retry(fileId);
|
||||
filesToUpload = React.addons.update(filesToUpload, { [fileId]: { status: { $set: FileStatus.UPLOADING } } });
|
||||
});
|
||||
|
||||
this.setState({
|
||||
// Reset the error class along with the retry
|
||||
errorState: {
|
||||
manualRetryAttempt: this.state.errorState.manualRetryAttempt + 1
|
||||
},
|
||||
filesToUpload
|
||||
});
|
||||
},
|
||||
|
||||
handleUploadFile(files) {
|
||||
// While files are being uploaded, the form cannot be ready
|
||||
// for submission
|
||||
@ -819,6 +895,9 @@ const ReactS3FineUploader = React.createClass({
|
||||
if(files.length > 0) {
|
||||
this.state.uploader.addFiles(files);
|
||||
this.synchronizeFileLists(files);
|
||||
this.setState({
|
||||
uploadInProgress: true
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -920,7 +999,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
isDropzoneInactive() {
|
||||
const { areAssetsEditable, enableLocalHashing, multiple, showErrorStates, uploadMethod } = this.props;
|
||||
const { areAssetsEditable, enableLocalHashing, multiple, showErrorPrompt, uploadMethod } = this.props;
|
||||
const { errorState, filesToUpload } = this.state;
|
||||
|
||||
const filesToDisplay = filesToUpload.filter((file) => {
|
||||
@ -931,7 +1010,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
});
|
||||
|
||||
if ((enableLocalHashing && !uploadMethod) || !areAssetsEditable ||
|
||||
(showErrorStates && errorState.errorClass) ||
|
||||
(showErrorPrompt && errorState.errorClass) ||
|
||||
(!multiple && filesToDisplay.length > 0)) {
|
||||
return true;
|
||||
} else {
|
||||
@ -940,7 +1019,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
getAllowedExtensions() {
|
||||
let { validation } = this.props;
|
||||
const { validation } = this.props;
|
||||
|
||||
if(validation && validation.allowedExtensions && validation.allowedExtensions.length > 0) {
|
||||
return transformAllowedExtensionsToInputAcceptProp(validation.allowedExtensions);
|
||||
@ -950,6 +1029,7 @@ const ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
render() {
|
||||
const { errorState: { errorClass }, filesToUpload, uploadInProgress } = this.state;
|
||||
const {
|
||||
multiple,
|
||||
areAssetsDownloadable,
|
||||
@ -973,11 +1053,15 @@ const ReactS3FineUploader = React.createClass({
|
||||
uploadMethod,
|
||||
fileClassToUpload,
|
||||
filesToUpload,
|
||||
uploadInProgress,
|
||||
errorClass,
|
||||
showError,
|
||||
onDrop: this.handleUploadFile,
|
||||
handleDeleteFile: this.handleDeleteFile,
|
||||
handleCancelFile: this.handleCancelFile,
|
||||
handlePauseFile: this.handlePauseFile,
|
||||
handleResumeFile: this.handleResumeFile,
|
||||
handleRetryFiles: this.handleRetryFiles,
|
||||
handleCancelHashing: this.handleCancelHashing,
|
||||
dropzoneInactive: this.isDropzoneInactive(),
|
||||
hashingProgress: this.state.hashingProgress,
|
||||
|
@ -135,7 +135,7 @@ Object.keys(ErrorClasses).forEach((errorGroupKey) => {
|
||||
const errorGroup = ErrorClasses[errorGroupKey];
|
||||
Object.keys(errorGroup).forEach((errorClassKey) => {
|
||||
const errorClass = errorGroup[errorClassKey];
|
||||
errorClass.name = errorClassKey;
|
||||
errorClass.name = errorGroupKey + '-' + errorClassKey;
|
||||
errorClass.group = errorGroupKey;
|
||||
});
|
||||
});
|
||||
|
@ -145,7 +145,7 @@ export function escapeHTML(s) {
|
||||
* Returns a copy of the given object's own and inherited enumerable
|
||||
* properties, omitting any keys that pass the given filter function.
|
||||
*/
|
||||
function filterObjOnFn(obj, filterFn) {
|
||||
function applyFilterOnObject(obj, filterFn) {
|
||||
const filteredObj = {};
|
||||
|
||||
for (let key in obj) {
|
||||
@ -158,6 +158,37 @@ function filterObjOnFn(obj, filterFn) {
|
||||
return filteredObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction for selectFromObject and omitFromObject
|
||||
* for DRYness
|
||||
* @param {boolean} isInclusion True if the filter should be for including the filtered items
|
||||
* (ie. selecting only them vs omitting only them)
|
||||
*/
|
||||
function filterFromObject(obj, filter, { isInclusion = true } = {}) {
|
||||
if (filter && filter.constructor === Array) {
|
||||
return applyFilterOnObject(obj, isInclusion ? ((_, key) => filter.indexOf(key) < 0)
|
||||
: ((_, key) => filter.indexOf(key) >= 0));
|
||||
} else if (filter && typeof filter === 'function') {
|
||||
// Flip the filter fn's return if it's for inclusion
|
||||
return applyFilterOnObject(obj, isInclusion ? (...args) => !filter(...args)
|
||||
: filter);
|
||||
} else {
|
||||
throw new Error('The given filter is not an array or function. Exclude aborted');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to lodash's _.pick(), this returns a copy of the given object's
|
||||
* own and inherited enumerable properties, selecting only the keys in
|
||||
* the given array or whose value pass the given filter function.
|
||||
* @param {object} obj Source object
|
||||
* @param {array|function} filter Array of key names to select or function to invoke per iteration
|
||||
* @return {object} The new object
|
||||
*/
|
||||
export function selectFromObject(obj, filter) {
|
||||
return filterFromObject(obj, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to lodash's _.omit(), this returns a copy of the given object's
|
||||
* own and inherited enumerable properties, omitting any keys that are
|
||||
@ -167,15 +198,7 @@ function filterObjOnFn(obj, filterFn) {
|
||||
* @return {object} The new object
|
||||
*/
|
||||
export function omitFromObject(obj, filter) {
|
||||
if (filter && filter.constructor === Array) {
|
||||
return filterObjOnFn(obj, (_, key) => {
|
||||
return filter.indexOf(key) >= 0;
|
||||
});
|
||||
} else if (filter && typeof filter === 'function') {
|
||||
return filterObjOnFn(obj, filter);
|
||||
} else {
|
||||
throw new Error('The given filter is not an array or function. Exclude aborted');
|
||||
}
|
||||
return filterFromObject(obj, filter, { isInclusion: false });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,6 +169,158 @@
|
||||
}
|
||||
}
|
||||
|
||||
.file-drag-and-drop-error {
|
||||
margin-bottom: 25px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
color: $ascribe-pink;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding-left: 45px;
|
||||
padding-right: 45px;
|
||||
}
|
||||
|
||||
/* Make button larger on mobile */
|
||||
@media screen and (max-width: 625px) {
|
||||
.btn {
|
||||
padding: 10px 100px 10px 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-drag-and-drop-error-detail {
|
||||
float: left;
|
||||
padding-right: 25px;
|
||||
text-align: left;
|
||||
width: 40%;
|
||||
|
||||
&.file-drag-and-drop-error-detail-multiple-files {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
/* Have detail fill up entire width on mobile */
|
||||
@media screen and (max-width: 625px) {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&.file-drag-and-drop-error-detail-multiple-files {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-drag-and-drop-error-file-names {
|
||||
float: left;
|
||||
height: 104px;
|
||||
max-width: 35%;
|
||||
padding-left: 25px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
|
||||
li {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop down file names under the retry button on mobile */
|
||||
@media screen and (max-width: 625px) {
|
||||
height: auto;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
ul {
|
||||
margin-bottom: 0;
|
||||
margin-top: 10px;
|
||||
top: 0;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
max-width: 25%;
|
||||
padding-right: 10px;
|
||||
|
||||
&:not(:last-child)::after {
|
||||
content: ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-drag-and-drop-error-icon-container {
|
||||
background-color: #eeeeee;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 104px;
|
||||
position: relative;
|
||||
width: 104px;
|
||||
vertical-align: top;
|
||||
|
||||
.file-drag-and-drop-error-icon {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&.file-drag-and-drop-error-icon-container-multiple-files {
|
||||
background-color: #d7d7d7;
|
||||
left: -15px;
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background-color: #e9e9e9;
|
||||
display: block;
|
||||
height: 104px;
|
||||
left: 15px;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
width: 104px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-color: #f5f5f5;
|
||||
display: block;
|
||||
height: 104px;
|
||||
left: 30px;
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
width: 104px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.file-drag-and-drop-error-icon {
|
||||
z-index: 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide the icon when the screen is too small */
|
||||
@media screen and (max-width: 625px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ascribe-progress-bar {
|
||||
margin-bottom: 0;
|
||||
> .progress-bar {
|
||||
|
Loading…
Reference in New Issue
Block a user