mirror of
https://github.com/ascribe/onion.git
synced 2025-02-14 21:10:27 +01:00
Strip app down to just be an upload test
This commit is contained in:
parent
60bf4b12f5
commit
40555e3386
@ -69,7 +69,7 @@ class AppGateway {
|
||||
load(settings) {
|
||||
let type = 'default';
|
||||
let subdomain = 'www';
|
||||
let redirectRoute = (<Redirect from="/" to="/collection" />);
|
||||
let redirectRoute = (<Redirect from="/" to="/register_piece" />);
|
||||
|
||||
if (settings) {
|
||||
type = settings.type;
|
||||
|
@ -34,7 +34,11 @@ let RegisterPieceForm = React.createClass({
|
||||
children: React.PropTypes.oneOfType([
|
||||
React.PropTypes.arrayOf(React.PropTypes.element),
|
||||
React.PropTypes.element
|
||||
])
|
||||
]),
|
||||
|
||||
onSingleTestComplete: React.PropTypes.func,
|
||||
onTestsStart: React.PropTypes.func,
|
||||
onTestsComplete: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -73,36 +77,51 @@ let RegisterPieceForm = React.createClass({
|
||||
});
|
||||
},
|
||||
|
||||
getUploadTests() {
|
||||
return [
|
||||
{
|
||||
'name': 'Direct to S3',
|
||||
'endpoint': 'https://ascribe0.s3.amazonaws.com'
|
||||
},
|
||||
{
|
||||
'name': 'Direct to S3 with large chunking',
|
||||
'endpoint': 'https://ascribe0.s3.amazonaws.com',
|
||||
'chunkSize': 52428800
|
||||
},
|
||||
{
|
||||
'name': 'Fastly to S3',
|
||||
'endpoint': 'http://www.ascribe.io.global.prod.fastly.net'
|
||||
},
|
||||
{
|
||||
'name': 'Fastly to S3 with large chunking',
|
||||
'endpoint': 'http://www.ascribe.io.global.prod.fastly.net',
|
||||
'chunkSize': 52428800
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
onTestComplete(testInfo) {
|
||||
this.props.onSingleTestComplete(testInfo);
|
||||
|
||||
const uploadTests = this.getUploadTests();
|
||||
if (testInfo.name === uploadTests[uploadTests.length - 1].name) {
|
||||
this.props.onTestsComplete();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
let currentUser = this.state.currentUser;
|
||||
let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false;
|
||||
enableLocalHashing = enableLocalHashing && this.props.enableLocalHashing;
|
||||
|
||||
return (
|
||||
<Form
|
||||
disabled={this.props.disabled}
|
||||
className="ascribe-form-bordered"
|
||||
ref='form'
|
||||
url={ApiUrls.pieces_list}
|
||||
handleSuccess={this.props.handleSuccess}
|
||||
buttons={
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-default btn-wide"
|
||||
disabled={!this.state.isUploadReady || this.props.disabled}>
|
||||
{this.props.submitMessage}
|
||||
</button>
|
||||
}
|
||||
spinner={
|
||||
<span className="btn btn-default btn-wide btn-spinner">
|
||||
<AscribeSpinner color="dark-blue" size="md" />
|
||||
</span>
|
||||
}>
|
||||
<div>
|
||||
<div className="ascribe-form-header">
|
||||
<h3>{this.props.headerMessage}</h3>
|
||||
<h3>Upload test</h3>
|
||||
</div>
|
||||
<Property
|
||||
name="digital_work_key"
|
||||
editable={this.props.isFineUploaderEditable}
|
||||
ignoreFocus={true}>
|
||||
<InputFineUploader
|
||||
keyRoutine={{
|
||||
@ -114,40 +133,14 @@ let RegisterPieceForm = React.createClass({
|
||||
}}
|
||||
validation={AppConstants.fineUploader.validation.registerWork}
|
||||
setIsUploadReady={this.setIsUploadReady}
|
||||
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
|
||||
isFineUploaderActive={this.props.isFineUploaderActive}
|
||||
onLoggedOut={this.props.onLoggedOut}
|
||||
disabled={!this.props.isFineUploaderEditable}
|
||||
enableLocalHashing={enableLocalHashing}
|
||||
uploadMethod={this.props.location.query.method} />
|
||||
|
||||
uploadTests={this.getUploadTests()}
|
||||
onTestsStart={this.props.onTestsStart}
|
||||
onTestComplete={this.onTestComplete} />
|
||||
</Property>
|
||||
<Property
|
||||
name='artist_name'
|
||||
label={getLangText('Artist Name')}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="(e.g. Andy Warhol)"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='title'
|
||||
label={getLangText('Title')}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="(e.g. 32 Campbell's Soup Cans)"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='date_created'
|
||||
label={getLangText('Year Created')}>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="(e.g. 1962)"
|
||||
min={1}
|
||||
required/>
|
||||
</Property>
|
||||
{this.props.children}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -51,7 +51,12 @@ const InputFineUploader = React.createClass({
|
||||
fileClassToUpload: shape({
|
||||
singular: string,
|
||||
plural: string
|
||||
})
|
||||
}),
|
||||
|
||||
|
||||
|
||||
uploadTests: React.PropTypes.array,
|
||||
onTestComplete: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -142,7 +147,10 @@ const InputFineUploader = React.createClass({
|
||||
onInactive={this.props.onLoggedOut}
|
||||
enableLocalHashing={this.props.enableLocalHashing}
|
||||
uploadMethod={this.props.uploadMethod}
|
||||
fileClassToUpload={this.props.fileClassToUpload} />
|
||||
fileClassToUpload={this.props.fileClassToUpload}
|
||||
uploadTests={this.props.uploadTests}
|
||||
onTestsStart={this.props.onTestsStart}
|
||||
onTestComplete={this.props.onTestComplete} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -46,65 +46,19 @@ let FileDragAndDropDialog = React.createClass({
|
||||
if (hasFiles) {
|
||||
return null;
|
||||
} else {
|
||||
if (enableLocalHashing && !uploadMethod) {
|
||||
const currentQueryParams = getCurrentQueryParams();
|
||||
const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
|
||||
: getLangText('choose a %s to upload', fileClassToUpload.singular);
|
||||
|
||||
const queryParamsHash = Object.assign({}, currentQueryParams);
|
||||
queryParamsHash.method = 'hash';
|
||||
|
||||
const queryParamsUpload = Object.assign({}, currentQueryParams);
|
||||
queryParamsUpload.method = 'upload';
|
||||
|
||||
return (
|
||||
<div className="file-drag-and-drop-dialog present-options">
|
||||
<p>{getLangText('Would you rather')}</p>
|
||||
<Link
|
||||
to={window.location.pathname}
|
||||
query={queryParamsHash}>
|
||||
<span className="btn btn-default btn-sm">
|
||||
{getLangText('Hash your work')}
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<span> or </span>
|
||||
|
||||
<Link
|
||||
to={window.location.pathname}
|
||||
query={queryParamsUpload}>
|
||||
<span className="btn btn-default btn-sm">
|
||||
{getLangText('Upload and hash your work')}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
if (multipleFiles) {
|
||||
return (
|
||||
<span className="file-drag-and-drop-dialog">
|
||||
{this.getDragDialog(fileClassToUpload.plural)}
|
||||
<span
|
||||
className="btn btn-default"
|
||||
onClick={onClick}>
|
||||
{getLangText('choose %s to upload', fileClassToUpload.plural)}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
|
||||
: getLangText('choose a %s to upload', fileClassToUpload.singular);
|
||||
|
||||
return (
|
||||
<span className="file-drag-and-drop-dialog">
|
||||
{this.getDragDialog(fileClassToUpload.singular)}
|
||||
<span
|
||||
className="btn btn-default"
|
||||
onClick={onClick}>
|
||||
{dialog}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span className="file-drag-and-drop-dialog">
|
||||
{this.getDragDialog(fileClassToUpload.singular)}
|
||||
<span
|
||||
className="btn btn-default"
|
||||
onClick={onClick}>
|
||||
{dialog}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -17,6 +17,7 @@ import { computeHashOfFile } from '../../utils/file_utils';
|
||||
import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp } from './react_s3_fine_uploader_utils';
|
||||
import { getCookie } from '../../utils/fetch_api_utils';
|
||||
import { getLangText } from '../../utils/lang_utils';
|
||||
import { startTimer, endTimer } from '../../utils/timer_utils';
|
||||
|
||||
let ReactS3FineUploader = React.createClass({
|
||||
propTypes: {
|
||||
@ -128,7 +129,14 @@ let ReactS3FineUploader = React.createClass({
|
||||
fileInputElement: React.PropTypes.oneOfType([
|
||||
React.PropTypes.func,
|
||||
React.PropTypes.element
|
||||
])
|
||||
]),
|
||||
|
||||
|
||||
|
||||
|
||||
uploadTests: React.PropTypes.array,
|
||||
onTestsStart: React.PropTypes.func,
|
||||
onTestComplete: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
@ -188,6 +196,11 @@ let ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
const uploadTestDeferreds = [];
|
||||
this.props.uploadTests.forEach(() => {
|
||||
uploadTestDeferreds.push(Q.defer());
|
||||
});
|
||||
|
||||
return {
|
||||
filesToUpload: [],
|
||||
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()),
|
||||
@ -198,7 +211,11 @@ let ReactS3FineUploader = React.createClass({
|
||||
hashingProgress: -2,
|
||||
|
||||
// this is for logging
|
||||
chunks: {}
|
||||
chunks: {},
|
||||
|
||||
|
||||
curUploadTest: 0,
|
||||
uploadTestDeferreds
|
||||
};
|
||||
},
|
||||
|
||||
@ -226,11 +243,13 @@ let ReactS3FineUploader = React.createClass({
|
||||
let objectProperties = this.props.objectProperties;
|
||||
objectProperties.key = this.requestKey;
|
||||
|
||||
let request = this.props.request;
|
||||
|
||||
return {
|
||||
autoUpload: this.props.autoUpload,
|
||||
debug: this.props.debug,
|
||||
objectProperties: objectProperties, // do a special key handling here
|
||||
request: this.props.request,
|
||||
request: request,
|
||||
signature: this.props.signature,
|
||||
uploadSuccess: this.props.uploadSuccess,
|
||||
cors: this.props.cors,
|
||||
@ -274,10 +293,6 @@ let ReactS3FineUploader = React.createClass({
|
||||
// Cancel uploads and clear previously selected files on the input element
|
||||
cancelUploads(id) {
|
||||
!!id ? this.state.uploader.cancel(id) : this.state.uploader.cancelAll();
|
||||
|
||||
// Reset the file input element to clear the previously selected files so that
|
||||
// the user can reselect them again.
|
||||
this.clearFileSelection();
|
||||
},
|
||||
|
||||
clearFileSelection() {
|
||||
@ -419,6 +434,13 @@ let ReactS3FineUploader = React.createClass({
|
||||
});
|
||||
// onError will catch any errors, so we can ignore them here
|
||||
} else if (!res.error || res.success) {
|
||||
const completedTime = endTimer() / 1000;
|
||||
this.props.onTestComplete({
|
||||
'name': this.props.uploadTests[this.state.curUploadTest].name,
|
||||
'time': completedTime
|
||||
});
|
||||
|
||||
|
||||
let files = this.state.filesToUpload;
|
||||
|
||||
// Set the state of the completed file to 'upload successful' in order to
|
||||
@ -432,27 +454,7 @@ let 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) {
|
||||
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');
|
||||
}
|
||||
this.handleDeleteFile(id);
|
||||
})
|
||||
.catch(this.onErrorPromiseProxy);
|
||||
}
|
||||
@ -470,6 +472,14 @@ let ReactS3FineUploader = React.createClass({
|
||||
},
|
||||
|
||||
onError(id, name, errorReason, xhr) {
|
||||
const errorTime = endTimer() / 1000;
|
||||
this.props.onTestComplete({
|
||||
'name': this.props.uploadTests[this.state.curUploadTest].name,
|
||||
'time': errorTime,
|
||||
'progress': this.state.filesToUpload[id] ? this.state.filesToUpload[id].progress : 0,
|
||||
'error': errorReason
|
||||
});
|
||||
|
||||
console.logGlobal(errorReason, false, {
|
||||
files: this.state.filesToUpload,
|
||||
chunks: this.state.chunks,
|
||||
@ -477,10 +487,12 @@ let ReactS3FineUploader = React.createClass({
|
||||
});
|
||||
|
||||
this.props.setIsUploadReady(true);
|
||||
this.cancelUploads();
|
||||
this.cancelUploads(id);
|
||||
|
||||
let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
this.state.uploadTestDeferreds[this.state.curUploadTest].resolve();
|
||||
},
|
||||
|
||||
getXhrErrorComment(xhr) {
|
||||
@ -515,19 +527,6 @@ let 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');
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -574,28 +573,9 @@ let ReactS3FineUploader = React.createClass({
|
||||
onDeleteComplete(id, xhr, isError) {
|
||||
if(isError) {
|
||||
this.setStatusOfFile(id, 'online');
|
||||
|
||||
let notification = new GlobalNotificationModel(getLangText('There was an error deleting your file.'), 'danger', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
} else {
|
||||
let notification = new GlobalNotificationModel(getLangText('File deleted'), '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) {
|
||||
// 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.state.uploadTestDeferreds[this.state.curUploadTest].resolve();
|
||||
},
|
||||
|
||||
handleDeleteFile(fileId) {
|
||||
@ -655,13 +635,6 @@ let ReactS3FineUploader = React.createClass({
|
||||
// for submission
|
||||
this.props.setIsUploadReady(false);
|
||||
|
||||
// If multiple set and user already uploaded its work,
|
||||
// cancel upload
|
||||
if(!this.props.multiple && this.state.filesToUpload.filter(displayValidFilesFilter).length > 0) {
|
||||
this.clearFileSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
// validate each submitted file if it fits the file size
|
||||
let validFiles = [];
|
||||
for(let i = 0; i < files.length; i++) {
|
||||
@ -690,99 +663,8 @@ let ReactS3FineUploader = React.createClass({
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
}
|
||||
|
||||
// As mentioned already in the propTypes declaration, in some instances we need to calculate the
|
||||
// md5 hash of a file locally and just upload a txt file containing that hash.
|
||||
//
|
||||
// In the view this only happens when the user is allowed to do local hashing as well
|
||||
// as when the correct method prop is present ('hash' and not 'upload')
|
||||
if (this.props.enableLocalHashing && this.props.uploadMethod === 'hash') {
|
||||
const convertedFilePromises = [];
|
||||
let overallFileSize = 0;
|
||||
|
||||
// "files" is not a classical Javascript array but a Javascript FileList, therefore
|
||||
// we can not use map to convert values
|
||||
for(let i = 0; i < files.length; i++) {
|
||||
// for calculating the overall progress of all submitted files
|
||||
// we'll need to calculate the overall sum of all files' sizes
|
||||
overallFileSize += files[i].size;
|
||||
|
||||
// also, we need to set the files' initial progress value
|
||||
files[i].progress = 0;
|
||||
|
||||
// since the actual computation of a file's hash is an async task ,
|
||||
// we're using promises to handle that
|
||||
let hashedFilePromise = computeHashOfFile(files[i]);
|
||||
convertedFilePromises.push(hashedFilePromise);
|
||||
}
|
||||
|
||||
// To react after the computation of all files, we define the resolvement
|
||||
// with the all function for iterables and essentially replace all original files
|
||||
// with their txt representative
|
||||
Q.all(convertedFilePromises)
|
||||
.progress(({index, value: {progress, reject}}) => {
|
||||
// hashing progress has been aborted from outside
|
||||
// To get out of the executing, we need to call reject from the
|
||||
// inside of the promise's execution.
|
||||
// This is why we're passing (along with value) a function that essentially
|
||||
// just does that (calling reject(err))
|
||||
//
|
||||
// In the promises catch method, we're then checking if the interruption
|
||||
// was due to that error or another generic one.
|
||||
if(this.state.hashingProgress === -1) {
|
||||
reject(new Error(getLangText('Hashing canceled')));
|
||||
}
|
||||
|
||||
// update file's progress
|
||||
files[index].progress = progress;
|
||||
|
||||
// calculate weighted average for overall progress of all
|
||||
// currently hashing files
|
||||
let overallHashingProgress = 0;
|
||||
for(let i = 0; i < files.length; i++) {
|
||||
let filesSliceOfOverall = files[i].size / overallFileSize;
|
||||
overallHashingProgress += filesSliceOfOverall * files[i].progress;
|
||||
}
|
||||
|
||||
// Multiply by 100, since react-progressbar expects decimal numbers
|
||||
this.setState({ hashingProgress: overallHashingProgress * 100});
|
||||
})
|
||||
.then((convertedFiles) => {
|
||||
// clear hashing progress, since its done
|
||||
this.setState({ hashingProgress: -2});
|
||||
|
||||
// actually replacing all files with their txt-hash representative
|
||||
files = convertedFiles;
|
||||
|
||||
// routine for adding all the files submitted to fineuploader for actual uploading them
|
||||
// to the server
|
||||
this.state.uploader.addFiles(files);
|
||||
this.synchronizeFileLists(files);
|
||||
|
||||
})
|
||||
.catch((err) => {
|
||||
// If the error is that hashing has been canceled, we want to display a success
|
||||
// message instead of a danger message
|
||||
let typeOfMessage = 'danger';
|
||||
|
||||
if(err.message === getLangText('Hashing canceled')) {
|
||||
typeOfMessage = 'success';
|
||||
this.setState({ hashingProgress: -2 });
|
||||
} else {
|
||||
// if there was a more generic error, we also log it
|
||||
console.logGlobal(err);
|
||||
}
|
||||
|
||||
let notification = new GlobalNotificationModel(err.message, typeOfMessage, 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
});
|
||||
|
||||
// if we're not hashing the files locally, we're just going to hand them over to fineuploader
|
||||
// to upload them to the server
|
||||
} else {
|
||||
if(files.length > 0) {
|
||||
this.state.uploader.addFiles(files);
|
||||
this.synchronizeFileLists(files);
|
||||
}
|
||||
if (files.length > 0) {
|
||||
this.startTests(files);
|
||||
}
|
||||
},
|
||||
|
||||
@ -886,6 +768,40 @@ let ReactS3FineUploader = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
startTests(files) {
|
||||
this.props.onTestsStart(files);
|
||||
|
||||
this.props.uploadTests.reduce((deferred, test, testIndex) => {
|
||||
return deferred.then(() => {
|
||||
this.startTest(test, files, testIndex);
|
||||
return this.state.uploadTestDeferreds[testIndex].promise;
|
||||
});
|
||||
}, Q.when());
|
||||
},
|
||||
|
||||
startTest(test, files, testIndex) {
|
||||
const uploaderConfig = this.propsToConfig();
|
||||
uploaderConfig.request = Object.assign({}, uploaderConfig.request);
|
||||
uploaderConfig.chunking = Object.assign({}, uploaderConfig.chunking);
|
||||
|
||||
if (!!test.endpoint) {
|
||||
uploaderConfig.request.endpoint = test.endpoint;
|
||||
}
|
||||
if (!!test.chunkSize) {
|
||||
uploaderConfig.chunking.partSize = test.chunkSize;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
uploader: new fineUploader.s3.FineUploaderBasic(uploaderConfig),
|
||||
curUploadTest: testIndex
|
||||
}, () => {
|
||||
startTimer();
|
||||
this.state.uploader.addFiles(files);
|
||||
this.synchronizeFileLists(files);
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
const {
|
||||
multiple,
|
||||
|
@ -82,7 +82,7 @@ let Header = React.createClass({
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Link className="icon-ascribe-logo" to="/collection"/>
|
||||
<a className="icon-ascribe-logo" href="http://www.ascribe.io"/>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
@ -203,16 +203,15 @@ let Header = React.createClass({
|
||||
toggleNavKey={0}
|
||||
fixedTop={true}>
|
||||
<CollapsibleNav eventKey={0}>
|
||||
<Nav navbar left>
|
||||
{this.getPoweredBy()}
|
||||
</Nav>
|
||||
<Nav navbar right>
|
||||
<HeaderNotificationDebug show = {false}/>
|
||||
{account}
|
||||
{signup}
|
||||
<LinkContainer
|
||||
to="/logout">
|
||||
<MenuItem
|
||||
eventKey="1">
|
||||
{getLangText('Log out')}
|
||||
</MenuItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
<HeaderNotifications />
|
||||
{navRoutesLinks}
|
||||
</CollapsibleNav>
|
||||
</Navbar>
|
||||
</div>
|
||||
|
@ -37,10 +37,6 @@ let LoginContainer = React.createClass({
|
||||
message={this.props.message}
|
||||
onLogin={this.props.onLogin}
|
||||
location={this.props.location}/>
|
||||
<div className="ascribe-login-text">
|
||||
{getLangText('Not an ascribe user')}? <Link to="/signup">{getLangText('Sign up')}...</Link><br/>
|
||||
{getLangText('Forgot my password')}? <Link to="/password_reset">{getLangText('Rescue me')}...</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -6,18 +6,8 @@ import { History } from 'react-router';
|
||||
import Col from 'react-bootstrap/lib/Col';
|
||||
import Row from 'react-bootstrap/lib/Row';
|
||||
|
||||
import WhitelabelActions from '../actions/whitelabel_actions';
|
||||
import WhitelabelStore from '../stores/whitelabel_store';
|
||||
|
||||
import PieceListStore from '../stores/piece_list_store';
|
||||
import PieceListActions from '../actions/piece_list_actions';
|
||||
|
||||
import UserStore from '../stores/user_store';
|
||||
|
||||
import GlobalNotificationModel from '../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||
|
||||
import PropertyCollapsible from './ascribe_forms/property_collapsible';
|
||||
import RegisterPieceForm from './ascribe_forms/form_register_piece';
|
||||
|
||||
import { mergeOptions } from '../utils/general_utils';
|
||||
@ -43,25 +33,22 @@ let RegisterPiece = React.createClass( {
|
||||
getInitialState(){
|
||||
return mergeOptions(
|
||||
UserStore.getState(),
|
||||
WhitelabelStore.getState(),
|
||||
PieceListStore.getState(),
|
||||
{
|
||||
selectedLicense: 0,
|
||||
isFineUploaderActive: false
|
||||
isFineUploaderActive: false,
|
||||
uploadInfos: [],
|
||||
testFileSize: 0,
|
||||
testStarted: false,
|
||||
testComplete: false
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
PieceListStore.listen(this.onChange);
|
||||
UserStore.listen(this.onChange);
|
||||
WhitelabelStore.listen(this.onChange);
|
||||
WhitelabelActions.fetchWhitelabel();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
PieceListStore.unlisten(this.onChange);
|
||||
UserStore.unlisten(this.onChange);
|
||||
WhitelabelStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
@ -75,36 +62,44 @@ let RegisterPiece = React.createClass( {
|
||||
}
|
||||
},
|
||||
|
||||
handleSuccess(response){
|
||||
let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
|
||||
// once the user was able to register a piece successfully, we need to make sure to keep
|
||||
// the piece list up to date
|
||||
PieceListActions.fetchPieceList(
|
||||
this.state.page,
|
||||
this.state.pageSize,
|
||||
this.state.searchTerm,
|
||||
this.state.orderBy,
|
||||
this.state.orderAsc,
|
||||
this.state.filterBy
|
||||
);
|
||||
|
||||
this.history.pushState(null, `/pieces/${response.piece.id}`);
|
||||
onSingleTestComplete(uploadInfo) {
|
||||
this.setState({
|
||||
uploadInfos: this.state.uploadInfos.concat([uploadInfo])
|
||||
});
|
||||
},
|
||||
|
||||
getSpecifyEditions() {
|
||||
if(this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) {
|
||||
onTestsStart(files) {
|
||||
this.setState({
|
||||
testStarted: true,
|
||||
testFileSize: files[0].size
|
||||
});
|
||||
},
|
||||
|
||||
onTestsComplete() {
|
||||
this.setState({
|
||||
testComplete: true
|
||||
}, () => {
|
||||
alert('Tests are complete. Please send the results to brett@ascribe.io');
|
||||
});
|
||||
},
|
||||
|
||||
getUploadedInfo() {
|
||||
if (this.state.uploadInfos.length > 0 && this.state.testStarted) {
|
||||
return (
|
||||
<PropertyCollapsible
|
||||
name="num_editions"
|
||||
checkboxLabel={getLangText('Specify editions')}>
|
||||
<span>{getLangText('Editions')}</span>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="(e.g. 32)"
|
||||
min={0}/>
|
||||
</PropertyCollapsible>
|
||||
<div style={{'backgroundColor': '#FFF'}}>
|
||||
<h4>{this.state.testComplete? 'Results:' : 'Test in progress...'}</h4>
|
||||
For file of size: {this.state.testFileSize}
|
||||
<ul>
|
||||
{this.state.uploadInfos.map((uploadInfo) => {
|
||||
if (!uploadInfo.error) {
|
||||
return (<li key={uploadInfo.name}><strong>{uploadInfo.name}</strong>: {uploadInfo.time}s</li>);
|
||||
} else {
|
||||
return (<li key={uploadInfo.name}><strong style={{'color': 'red'}}>Error</strong>: {uploadInfo.error} after {uploadInfo.time}s on completing {uploadInfo.progress}%</li>);
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
<p>Please send these results by screenshot or by copying the values to <a href="mailto:brett@ascribe.io">brett@ascribe.io</a></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -118,11 +113,13 @@ let RegisterPiece = React.createClass( {
|
||||
<RegisterPieceForm
|
||||
{...this.props}
|
||||
isFineUploaderActive={this.state.isFineUploaderActive}
|
||||
handleSuccess={this.handleSuccess}
|
||||
location={this.props.location}>
|
||||
{this.props.children}
|
||||
{this.getSpecifyEditions()}
|
||||
</RegisterPieceForm>
|
||||
isFineUploaderEditable={!this.state.testComplete}
|
||||
location={this.props.location}
|
||||
|
||||
onSingleTestComplete={this.onSingleTestComplete}
|
||||
onTestsStart={this.onTestsStart}
|
||||
onTestsComplete={this.onTestsComplete} />
|
||||
{this.getUploadedInfo()}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
20
js/routes.js
20
js/routes.js
@ -32,33 +32,17 @@ let COMMON_ROUTES = (
|
||||
<Route path='/' component={App}>
|
||||
<Route
|
||||
path='login'
|
||||
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(LoginContainer)} />
|
||||
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(LoginContainer)} />
|
||||
<Route
|
||||
path='register_piece'
|
||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(RegisterPiece)}
|
||||
headerTitle='+ NEW WORK'/>
|
||||
<Route
|
||||
path='collection'
|
||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(PieceList)}
|
||||
headerTitle='COLLECTION'/>
|
||||
<Route
|
||||
path='signup'
|
||||
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(SignupContainer)} />
|
||||
component={AuthProxyHandler({to: '/register_piece', when: 'loggedIn'})(SignupContainer)} />
|
||||
<Route
|
||||
path='logout'
|
||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(LogoutContainer)}/>
|
||||
<Route path='pieces/:pieceId' component={PieceContainer} />
|
||||
<Route path='editions/:editionId' component={EditionContainer} />
|
||||
<Route
|
||||
path='password_reset'
|
||||
component={AuthProxyHandler({to: '/collection', when: 'loggedIn'})(PasswordResetContainer)} />
|
||||
<Route
|
||||
path='settings'
|
||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(SettingsContainer)}/>
|
||||
<Route
|
||||
path='contract_settings'
|
||||
component={AuthProxyHandler({to: '/login', when: 'loggedOut'})(ContractSettings)}/>
|
||||
<Route path='coa_verify' component={CoaVerifyContainer} />
|
||||
<Route path='*' component={ErrorNotFoundPage} />
|
||||
</Route>
|
||||
);
|
||||
|
15
js/utils/timer_utils.js
Normal file
15
js/utils/timer_utils.js
Normal file
@ -0,0 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
var startTime = null;
|
||||
|
||||
export function startTimer() {
|
||||
startTime = new Date();
|
||||
};
|
||||
|
||||
export function endTimer() {
|
||||
if (startTime) {
|
||||
const time = new Date() - startTime;
|
||||
startTime = null;
|
||||
return time;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user