1
0
mirror of https://github.com/ascribe/onion.git synced 2024-11-15 09:35:10 +01:00

Merge remote-tracking branch 'remotes/origin/AD-496-add-control-buttons-to-fineupload' into AD-368-harmonize-functionality-of-ascrib

This commit is contained in:
diminator 2015-06-30 15:43:48 +02:00
commit fe4d4c6a72
17 changed files with 330 additions and 95 deletions

View File

@ -15,7 +15,6 @@ class CoaActions {
CoaFetcher.fetchOne(id) CoaFetcher.fetchOne(id)
.then((res) => { .then((res) => {
this.actions.updateCoa(res.coa); this.actions.updateCoa(res.coa);
console.log(res.coa);
}) })
.catch((err) => { .catch((err) => {
console.log(err); console.log(err);
@ -25,7 +24,6 @@ class CoaActions {
CoaFetcher.create(edition.bitcoin_id) CoaFetcher.create(edition.bitcoin_id)
.then((res) => { .then((res) => {
this.actions.updateCoa(res.coa); this.actions.updateCoa(res.coa);
console.log(res.coa);
}) })
.catch((err) => { .catch((err) => {
console.log(err); console.log(err);

View File

@ -16,6 +16,10 @@ let AccordionList = React.createClass({
{this.props.children} {this.props.children}
</div> </div>
); );
} else if(this.props.itemList.length === 0) {
return (
<p className="text-center">You don't have any works yet...</p>
);
} else { } else {
return ( return (
<div className={this.props.className + ' ascribe-accordion-list-loading'}> <div className={this.props.className + ' ascribe-accordion-list-loading'}>

View File

@ -4,8 +4,10 @@ import React from 'react';
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator'; import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
let ReactTestUtils = React.addons.TestUtils;
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop // Taken from: https://github.com/fedosejev/react-file-drag-and-drop
var FileDragAndDrop = React.createClass({ let FileDragAndDrop = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
onDragStart: React.PropTypes.func, onDragStart: React.PropTypes.func,
@ -19,8 +21,11 @@ var FileDragAndDrop = React.createClass({
filesToUpload: React.PropTypes.array, filesToUpload: React.PropTypes.array,
handleDeleteFile: React.PropTypes.func, handleDeleteFile: React.PropTypes.func,
handleCancelFile: React.PropTypes.func, handleCancelFile: React.PropTypes.func,
handlePauseFile: React.PropTypes.func,
handleResumeFile: React.PropTypes.func,
multiple: React.PropTypes.bool, multiple: React.PropTypes.bool,
dropzoneInactive: React.PropTypes.bool dropzoneInactive: React.PropTypes.bool,
areAssetsDownloadable: React.PropTypes.bool
}, },
handleDragStart(event) { handleDragStart(event) {
@ -93,6 +98,20 @@ var FileDragAndDrop = React.createClass({
this.props.handleCancelFile(fileId); this.props.handleCancelFile(fileId);
}, },
handlePauseFile(fileId) {
// input's value is not change the second time someone
// inputs the same file again, therefore we need to reset its value
this.refs.fileinput.getDOMNode().value = '';
this.props.handlePauseFile(fileId);
},
handleResumeFile(fileId) {
// input's value is not change the second time someone
// inputs the same file again, therefore we need to reset its value
this.refs.fileinput.getDOMNode().value = '';
this.props.handleResumeFile(fileId);
},
handleOnClick() { handleOnClick() {
// when multiple is set to false and the user already uploaded a piece, // when multiple is set to false and the user already uploaded a piece,
// do not propagate event // do not propagate event
@ -100,10 +119,16 @@ var FileDragAndDrop = React.createClass({
return; return;
} }
// Simulate click on hidden file input // Firefox only recognizes the simulated mouse click if bubbles is set to true,
var event = document.createEvent('HTMLEvents'); // but since Google Chrome propagates the event much further than needed, we
event.initEvent('click', false, true); // need to stop propagation as soon as the event is created
this.refs.fileinput.getDOMNode().dispatchEvent(event); var evt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
evt.stopPropagation();
this.refs.fileinput.getDOMNode().dispatchEvent(evt);
}, },
render: function () { render: function () {
@ -128,7 +153,10 @@ var FileDragAndDrop = React.createClass({
<FileDragAndDropPreviewIterator <FileDragAndDropPreviewIterator
files={this.props.filesToUpload} files={this.props.filesToUpload}
handleDeleteFile={this.handleDeleteFile} handleDeleteFile={this.handleDeleteFile}
handleCancelFile={this.handleCancelFile}/> handleCancelFile={this.handleCancelFile}
handlePauseFile={this.handlePauseFile}
handleResumeFile={this.handleResumeFile}
areAssetsDownloadable={this.props.areAssetsDownloadable}/>
<input <input
multiple={this.props.multiple} multiple={this.props.multiple}
ref="fileinput" ref="fileinput"

View File

@ -14,11 +14,18 @@ let FileDragAndDropPreview = React.createClass({
type: React.PropTypes.string type: React.PropTypes.string
}).isRequired, }).isRequired,
handleDeleteFile: React.PropTypes.func, handleDeleteFile: React.PropTypes.func,
handleCancelFile: React.PropTypes.func handleCancelFile: React.PropTypes.func,
handlePauseFile: React.PropTypes.func,
handleResumeFile: React.PropTypes.func,
areAssetsDownloadable: React.PropTypes.bool
}, },
toggleUploadProcess() { toggleUploadProcess() {
if(this.props.file.status === 'uploading') {
this.props.handlePauseFile(this.props.file.id);
} else if(this.props.file.status === 'paused') {
this.props.handleResumeFile(this.props.file.id);
}
}, },
handleDeleteFile() { handleDeleteFile() {
@ -45,18 +52,23 @@ let FileDragAndDropPreview = React.createClass({
onClick={this.handleDeleteFile} onClick={this.handleDeleteFile}
progress={this.props.file.progress} progress={this.props.file.progress}
url={this.props.file.url} url={this.props.file.url}
toggleUploadProcess={this.toggleUploadProcess}/>); toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={this.props.areAssetsDownloadable}/>);
} else { } else {
previewElement = (<FileDragAndDropPreviewOther previewElement = (<FileDragAndDropPreviewOther
onClick={this.handleDeleteFile} onClick={this.handleDeleteFile}
progress={this.props.file.progress} progress={this.props.file.progress}
type={this.props.file.type.split('/')[1]} type={this.props.file.type.split('/')[1]}
toggleUploadProcess={this.toggleUploadProcess}/>); toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={this.props.areAssetsDownloadable}/>);
} }
return ( return (
<div <div
className="file-drag-and-drop-position"> className="file-drag-and-drop-position">
<div className="delete-file">
<span className="glyphicon glyphicon-remove text-center" aria-hidden="true" title="Remove file" onClick={this.handleDeleteFile}/>
</div>
{previewElement} {previewElement}
</div> </div>
); );

View File

@ -3,12 +3,15 @@
import React from 'react'; import React from 'react';
import ProgressBar from 'react-progressbar'; import ProgressBar from 'react-progressbar';
import AppConstants from '../../constants/application_constants';
let FileDragAndDropPreviewImage = React.createClass({ let FileDragAndDropPreviewImage = React.createClass({
propTypes: { propTypes: {
progress: React.PropTypes.number, progress: React.PropTypes.number,
url: React.PropTypes.string, url: React.PropTypes.string,
toggleUploadProcess: React.PropTypes.func, toggleUploadProcess: React.PropTypes.func,
downloadFile: React.PropTypes.func downloadFile: React.PropTypes.func,
areAssetsDownloadable: React.PropTypes.bool
}, },
getInitialState() { getInitialState() {
@ -17,28 +20,19 @@ let FileDragAndDropPreviewImage = React.createClass({
}; };
}, },
/*onClick(e) {
e.preventDefault();
e.stopPropagation();
this.setState({
loading: true
});
this.props.onClick(e);
},*/
toggleUploadProcess(e) { toggleUploadProcess(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.setState({ this.setState({
paused: true paused: !this.state.paused
}); });
this.props.toggleUploadProcess();
}, },
downloadFile() { downloadFile() {
console.log('implement this');
}, },
render() { render() {
@ -49,14 +43,22 @@ let FileDragAndDropPreviewImage = React.createClass({
let actionSymbol; let actionSymbol;
if(this.props.progress !== 100 && this.state.paused) { if(this.props.progress > 0 && this.props.progress < 99 && this.state.paused) {
actionSymbol = <span className="glyphicon glyphicon-pause action-file" aria-hidden="true" title="Pause upload" onClick={this.toggleUploadProcess}/>; actionSymbol = <span className="glyphicon glyphicon-pause action-file" aria-hidden="true" title="Pause upload" onClick={this.toggleUploadProcess}/>;
} else if(this.props.progress !== 100 && !this.state.paused) { } else if(this.props.progress > 0 && this.props.progress < 99 && !this.state.paused) {
actionSymbol = <span className="glyphicon glyphicon-play action-file" aria-hidden="true" title="Resume uploading" onClick={this.toggleUploadProcess}/>; actionSymbol = <span className="glyphicon glyphicon-play action-file" aria-hidden="true" title="Resume uploading" onClick={this.toggleUploadProcess}/>;
} else { } else if(this.props.progress === 100) {
// 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(this.props.areAssetsDownloadable) {
actionSymbol = <span className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file" onClick={this.props.downloadFile}/>; actionSymbol = <span className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file" onClick={this.props.downloadFile}/>;
} }
} else {
actionSymbol = <img height={35} className="action-file" src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
}
return ( return (
<div <div
className="file-drag-and-drop-preview-image" className="file-drag-and-drop-preview-image"

View File

@ -8,7 +8,10 @@ let FileDragAndDropPreviewIterator = React.createClass({
propTypes: { propTypes: {
files: React.PropTypes.array, files: React.PropTypes.array,
handleDeleteFile: React.PropTypes.func, handleDeleteFile: React.PropTypes.func,
handleCancelFile: React.PropTypes.func handleCancelFile: React.PropTypes.func,
handlePauseFile: React.PropTypes.func,
handleResumeFile: React.PropTypes.func,
areAssetsDownloadable: React.PropTypes.bool
}, },
render() { render() {
@ -22,7 +25,10 @@ let FileDragAndDropPreviewIterator = React.createClass({
key={i} key={i}
file={file} file={file}
handleDeleteFile={this.props.handleDeleteFile} handleDeleteFile={this.props.handleDeleteFile}
handleCancelFile={this.props.handleCancelFile}/> handleCancelFile={this.props.handleCancelFile}
handlePauseFile={this.props.handlePauseFile}
handleResumeFile={this.props.handleResumeFile}
areAssetsDownloadable={this.props.areAssetsDownloadable}/>
); );
} else { } else {
return null; return null;

View File

@ -9,35 +9,59 @@ let FileDragAndDropPreviewOther = React.createClass({
propTypes: { propTypes: {
type: React.PropTypes.string, type: React.PropTypes.string,
progress: React.PropTypes.number, progress: React.PropTypes.number,
onClick: React.PropTypes.func areAssetsDownloadable: React.PropTypes.bool,
toggleUploadProcess: React.PropTypes.func,
downloadFile: React.PropTypes.func,
}, },
getInitialState() { getInitialState() {
return { return {
loading: false paused: true
}; };
}, },
onClick(e) { toggleUploadProcess(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.setState({ this.setState({
loading: true paused: !this.state.paused
}); });
this.props.onClick(e); this.props.toggleUploadProcess();
},
downloadFile() {
console.log('implement this');
}, },
render() { render() {
//let actionSymbol = this.state.loading ? <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} /> : <span className="glyphicon glyphicon-remove delete-file" aria-hidden="true" title="Delete or cancel upload" onClick={this.onClick} />;
let actionSymbol;
if(this.props.progress > 0 && this.props.progress < 99 && this.state.paused) {
actionSymbol = <span className="glyphicon glyphicon-pause action-file" aria-hidden="true" title="Pause upload" onClick={this.toggleUploadProcess}/>;
} else if(this.props.progress > 0 && this.props.progress < 99 && !this.state.paused) {
actionSymbol = <span className="glyphicon glyphicon-play action-file" aria-hidden="true" title="Resume uploading" onClick={this.toggleUploadProcess}/>;
} else if(this.props.progress === 100) {
// 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(this.props.areAssetsDownloadable) {
actionSymbol = <span className="glyphicon glyphicon-download action-file" aria-hidden="true" title="Download file" onClick={this.props.downloadFile}/>;
}
} else {
actionSymbol = <img height={35} src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
}
return ( return (
<div <div
className="file-drag-and-drop-preview"> className="file-drag-and-drop-preview">
<ProgressBar completed={this.props.progress} color="black"/> <ProgressBar completed={this.props.progress} color="black"/>
<div className="file-drag-and-drop-preview-table-wrapper"> <div className="file-drag-and-drop-preview-table-wrapper">
<div className="file-drag-and-drop-preview-other"> <div className="file-drag-and-drop-preview-other">
<span className="glyphicon glyphicon-pause delete-file" aria-hidden="true" title="Delete or cancel upload"/> {actionSymbol}
<span>{'.' + this.props.type}</span> <span>{'.' + this.props.type}</span>
</div> </div>
</div> </div>

View File

@ -9,6 +9,7 @@ import fetch from 'isomorphic-fetch';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { getCookie } from '../../utils/fetch_api_utils'; import { getCookie } from '../../utils/fetch_api_utils';
import S3Fetcher from '../../fetchers/s3_fetcher';
import fineUploader from 'fineUploader'; import fineUploader from 'fineUploader';
import FileDragAndDrop from './file_drag_and_drop'; import FileDragAndDrop from './file_drag_and_drop';
@ -82,8 +83,9 @@ var ReactS3FineUploader = React.createClass({
retry: React.PropTypes.shape({ retry: React.PropTypes.shape({
enableAuto: React.PropTypes.bool enableAuto: React.PropTypes.bool
}), }),
setUploadStatus: React.PropTypes.func, setIsUploadReady: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func isReadyForFormSubmission: React.PropTypes.func,
areAssetsDownloadable: React.PropTypes.bool
}, },
getDefaultProps() { getDefaultProps() {
@ -183,7 +185,8 @@ var ReactS3FineUploader = React.createClass({
onRetry: this.onRetry, onRetry: this.onRetry,
onAutoRetry: this.onAutoRetry, onAutoRetry: this.onAutoRetry,
onManualRetry: this.onManualRetry, onManualRetry: this.onManualRetry,
onDeleteComplete: this.onDeleteComplete onDeleteComplete: this.onDeleteComplete,
onSessionRequestComplete: this.onSessionRequestComplete
} }
}; };
}, },
@ -247,9 +250,9 @@ var ReactS3FineUploader = React.createClass({
// the form is ready for submission or not // the form is ready for submission or not
if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) { if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
// if so, set uploadstatus to true // if so, set uploadstatus to true
this.props.setUploadStatus(true); this.props.setIsUploadReady(true);
} else { } else {
this.props.setUploadStatus(false); this.props.setIsUploadReady(false);
} }
}, },
@ -305,9 +308,9 @@ var ReactS3FineUploader = React.createClass({
if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) { if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
// if so, set uploadstatus to true // if so, set uploadstatus to true
this.props.setUploadStatus(true); this.props.setIsUploadReady(true);
} else { } else {
this.props.setUploadStatus(false); this.props.setIsUploadReady(false);
} }
}, },
@ -324,14 +327,14 @@ var ReactS3FineUploader = React.createClass({
if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) { if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
// if so, set uploadstatus to true // if so, set uploadstatus to true
this.props.setUploadStatus(true); this.props.setIsUploadReady(true);
} else { } else {
this.props.setUploadStatus(false); this.props.setIsUploadReady(false);
} }
}, },
onProgress(id, name, uploadedBytes, totalBytes) { onProgress(id, name, uploadedBytes, totalBytes) {
var newState = React.addons.update(this.state, { let newState = React.addons.update(this.state, {
filesToUpload: { [id]: { filesToUpload: { [id]: {
progress: { $set: (uploadedBytes / totalBytes) * 100} } progress: { $set: (uploadedBytes / totalBytes) * 100} }
} }
@ -339,18 +342,84 @@ var ReactS3FineUploader = React.createClass({
this.setState(newState); this.setState(newState);
}, },
onSessionRequestComplete(response, success) {
if(success) {
// fetch blobs for images
response = response.map((file) => {
file.url = file.s3Url;
file.status = 'online';
file.progress = 100;
return file;
});
// add file to filesToUpload
let updatedFilesToUpload = this.state.filesToUpload.concat(response);
// refresh all files ids,
updatedFilesToUpload = updatedFilesToUpload.map((file, i) => {
file.id = i;
return file;
});
let newState = React.addons.update(this.state, {filesToUpload: {$set: updatedFilesToUpload}});
this.setState(newState);
} else {
let notification = new GlobalNotificationModel('Could not load attached files (Further data)', 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
throw new Error('The session request failed', response);
}
},
handleDeleteFile(fileId) { handleDeleteFile(fileId) {
// In some instances (when the file was already uploaded and is just displayed to the user)
// fineuploader does not register an id on the file (we do, don't be confused by this!).
// Since you can only delete a file by its id, we have to implement this method ourselves
//
// So, if an id is not present, we delete the file manually
// To check which files are already uploaded from previous sessions we check their status.
// If they are, it is "online"
if(this.state.filesToUpload[fileId].status !== 'online') {
// delete file from server // delete file from server
this.state.uploader.deleteFile(fileId); this.state.uploader.deleteFile(fileId);
// this is being continues in onDeleteFile, as // this is being continues in onDeleteFile, as
// fineuploaders deleteFile does not return a correct callback or // fineuploaders deleteFile does not return a correct callback or
// promise // promise
} else {
let fileToDelete = this.state.filesToUpload[fileId];
S3Fetcher
.deleteFile(fileToDelete.s3Key, fileToDelete.s3Bucket)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
}
}, },
handleCancelFile(fileId) { handleCancelFile(fileId) {
this.state.uploader.cancel(fileId); this.state.uploader.cancel(fileId);
}, },
handlePauseFile(fileId) {
if(this.state.uploader.pauseUpload(fileId)) {
this.setStatusOfFile(fileId, 'paused');
} else {
throw new Error('File upload could not be paused.');
}
},
handleResumeFile(fileId) {
if(this.state.uploader.continueUpload(fileId)) {
this.setStatusOfFile(fileId, 'uploading');
} else {
throw new Error('File upload could not be resumed.');
}
},
handleUploadFile(files) { handleUploadFile(files) {
// If multiple set and user already uploaded its work, // If multiple set and user already uploaded its work,
@ -420,6 +489,20 @@ var ReactS3FineUploader = React.createClass({
this.setState(newState); this.setState(newState);
}, },
setStatusOfFile(fileId, status) {
// also, sync files from state with the ones from fineuploader
let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload));
// splice because I can
filesToUpload[fileId].status = status;
// set state
let newState = React.addons.update(this.state, {
filesToUpload: { $set: filesToUpload }
});
this.setState(newState);
},
render() { render() {
return ( return (
<div> <div>
@ -429,7 +512,10 @@ var ReactS3FineUploader = React.createClass({
filesToUpload={this.state.filesToUpload} filesToUpload={this.state.filesToUpload}
handleDeleteFile={this.handleDeleteFile} handleDeleteFile={this.handleDeleteFile}
handleCancelFile={this.handleCancelFile} handleCancelFile={this.handleCancelFile}
handlePauseFile={this.handlePauseFile}
handleResumeFile={this.handleResumeFile}
multiple={this.props.multiple} multiple={this.props.multiple}
areAssetsDownloadable={this.props.areAssetsDownloadable}
dropzoneInactive={!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0} /> dropzoneInactive={!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0} />
</div> </div>
); );

View File

@ -253,7 +253,7 @@ let EditionSummary = React.createClass({
<Row> <Row>
<Col md={12}> <Col md={12}>
<AclButtonList <AclButtonList
className="pull-left" className="text-center"
availableAcls={this.props.edition.acl} availableAcls={this.props.edition.acl}
editions={[this.props.edition]} editions={[this.props.edition]}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
@ -455,9 +455,9 @@ let EditionFurtherDetails = React.createClass({
}); });
}, },
setUploadStatus(isReady) { setIsUploadReady(isReady) {
this.setState({ this.setState({
uploadStatus: isReady isUploadReady: isReady
}); });
}, },
@ -495,7 +495,7 @@ let EditionFurtherDetails = React.createClass({
edition={this.props.edition} /> edition={this.props.edition} />
<FileUploader <FileUploader
submitKey={this.submitKey} submitKey={this.submitKey}
setUploadStatus={this.setUploadStatus} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={this.isReadyForFormSubmission} isReadyForFormSubmission={this.isReadyForFormSubmission}
edition={this.props.edition}/> edition={this.props.edition}/>
</Col> </Col>
@ -506,7 +506,8 @@ let EditionFurtherDetails = React.createClass({
let FileUploader = React.createClass({ let FileUploader = React.createClass({
propTypes: { propTypes: {
setUploadStatus: React.PropTypes.func, edition: React.PropTypes.object,
setIsUploadReady: React.PropTypes.func,
submitKey: React.PropTypes.func, submitKey: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func isReadyForFormSubmission: React.PropTypes.func
}, },
@ -528,7 +529,7 @@ let FileUploader = React.createClass({
sizeLimit: '10000000' sizeLimit: '10000000'
}} }}
submitKey={this.props.submitKey} submitKey={this.props.submitKey}
setUploadStatus={this.props.setUploadStatus} setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission} isReadyForFormSubmission={this.props.isReadyForFormSubmission}
session={{ session={{
endpoint: AppConstants.serverUrl + 'api/blob/otherdatas/fineuploader_session/', endpoint: AppConstants.serverUrl + 'api/blob/otherdatas/fineuploader_session/',
@ -536,9 +537,10 @@ let FileUploader = React.createClass({
'X-CSRFToken': getCookie('csrftoken') 'X-CSRFToken': getCookie('csrftoken')
}, },
params: { params: {
'pk': this.props.edition.other_data.id 'pk': this.props.edition.other_data ? this.props.edition.other_data.id : null
} }
}}/> }}
areAssetsDownloadable={true}/>
); );
} }
}); });

View File

@ -71,9 +71,9 @@ let RegisterPiece = React.createClass( {
}); });
}, },
setUploadStatus(isReady) { setIsUploadReady(isReady) {
this.setState({ this.setState({
uploadStatus: isReady isUploadReady: isReady
}); });
}, },
@ -118,15 +118,6 @@ let RegisterPiece = React.createClass( {
}, },
render() { render() {
let buttons = <span />;
if (this.state.uploadStatus){
buttons = (
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
Register your artwork
</button>);
}
return ( return (
<div className="row ascribe-row"> <div className="row ascribe-row">
<div className="col-md-12"> <div className="col-md-12">
@ -136,7 +127,12 @@ let RegisterPiece = React.createClass( {
url={apiUrls.pieces_list} url={apiUrls.pieces_list}
getFormData={this.getFormData} getFormData={this.getFormData}
handleSuccess={this.handleSuccess} handleSuccess={this.handleSuccess}
buttons={buttons} buttons={<button
type="submit"
className="btn ascribe-btn ascribe-btn-login"
disabled={!this.state.isUploadReady}>
Register your artwork
</button>}
spinner={ spinner={
<button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner"> <button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" /> <img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
@ -146,7 +142,7 @@ let RegisterPiece = React.createClass( {
label="Files to upload"> label="Files to upload">
<FileUploader <FileUploader
submitKey={this.submitKey} submitKey={this.submitKey}
setUploadStatus={this.setUploadStatus} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={this.isReadyForFormSubmission}/> isReadyForFormSubmission={this.isReadyForFormSubmission}/>
</Property> </Property>
<Property <Property
@ -195,7 +191,7 @@ let RegisterPiece = React.createClass( {
let FileUploader = React.createClass({ let FileUploader = React.createClass({
propTypes: { propTypes: {
setUploadStatus: React.PropTypes.func, setIsUploadReady: React.PropTypes.func,
submitKey: React.PropTypes.func, submitKey: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func isReadyForFormSubmission: React.PropTypes.func
}, },
@ -215,8 +211,9 @@ let FileUploader = React.createClass({
itemLimit: 100000, itemLimit: 100000,
sizeLimit: '25000000000' sizeLimit: '25000000000'
}} }}
setUploadStatus={this.props.setUploadStatus} setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission}/> isReadyForFormSubmission={this.props.isReadyForFormSubmission}
areAssetsDownloadable={false}/>
); );
} }
}); });

View File

@ -40,7 +40,8 @@ let apiUrls = {
'users_signup': AppConstants.apiEndpoint + 'users/', 'users_signup': AppConstants.apiEndpoint + 'users/',
'users_username': AppConstants.apiEndpoint + 'users/username/', 'users_username': AppConstants.apiEndpoint + 'users/username/',
'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/', 'wallet_settings': AppConstants.apiEndpoint + 'users/wallet_settings/',
'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/' 'whitelabel_settings': AppConstants.apiEndpoint + 'whitelabel/settings/${subdomain}/',
'delete_s3_file': AppConstants.serverUrl + 's3/delete/'
}; };
export default apiUrls; export default apiUrls;

17
js/fetchers/s3_fetcher.js Normal file
View File

@ -0,0 +1,17 @@
'use strict';
import requests from '../utils/requests';
let S3Fetcher = {
/**
* Fetch the registered applications of a user from the API.
*/
deleteFile(key, bucket) {
return requests.delete('delete_s3_file', {
key,
bucket
});
}
};
export default S3Fetcher;

View File

@ -70,3 +70,38 @@ export function getCookie(name) {
return parts.pop().split(';').shift(); return parts.pop().split(';').shift();
} }
} }
/*
Given a url for an image, this method fetches it and returns a promise that resolves to
a blob object.
It can be used to create a 64base encoded data url.
Taken from: http://jsfiddle.net/jan_miksovsky/yy7zs/
*/
export function fetchImageAsBlob(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// Ask for the result as an ArrayBuffer.
xhr.responseType = 'arraybuffer';
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status >= 400) {
reject(xhr.statusText);
}
};
xhr.onload = function() {
// Obtain a blob: URL for the image data.
let arrayBufferView = new Uint8Array(this.response);
let blob = new Blob([arrayBufferView], {type: 'image/jpeg'});
resolve(blob);
};
xhr.send();
});
}

View File

@ -43,9 +43,29 @@
} }
.file-drag-and-drop-position { .file-drag-and-drop-position {
position: relative;
display: inline-block; display: inline-block;
margin: 0 0 4% 4%; margin: 0 0 4% 4%;
float:left; float:left;
.delete-file {
display: block;
background-color: black;
width: 20px;
height: 20px;
position: absolute;
right: -7px;
top: -7px;
border-radius: 1em;
text-align: center;
cursor: pointer;
span {
color: white;
}
}
} }
.file-drag-and-drop-preview-table-wrapper { .file-drag-and-drop-preview-table-wrapper {

View File

@ -1,3 +1,6 @@
$ascribe-color: rgba(2, 182, 163, 0.5); $ascribe-color: rgba(2, 182, 163, 0.5);
$ascribe-color-dark: rgba(2, 182, 163, 0.8); $ascribe-color-dark: rgba(2, 182, 163, 0.8);
$ascribe-color-full: rgba(2, 182, 163, 1); $ascribe-color-full: rgba(2, 182, 163, 1);
$ascribe-brand-danger: #FC535F;
$ascribe-brand-warning: #FFC354;

View File

@ -3,8 +3,8 @@
$BASE_URL: '<%= BASE_URL %>'; $BASE_URL: '<%= BASE_URL %>';
@import 'variables';
@import 'ascribe_variables'; @import 'ascribe_variables';
@import 'variables';
@import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap'; @import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap';
@import '../node_modules/react-datepicker/dist/react-datepicker'; @import '../node_modules/react-datepicker/dist/react-datepicker';
@import './ascribe-fonts/style'; @import './ascribe-fonts/style';

View File

@ -18,8 +18,8 @@ $gray-lighter: lighten($gray-base, 93.5%) !default; // #eee
$brand-primary: darken(#428bca, 6.5%) !default; // #337ab7 $brand-primary: darken(#428bca, 6.5%) !default; // #337ab7
$brand-success: #5cb85c !default; $brand-success: #5cb85c !default;
$brand-info: #5bc0de !default; $brand-info: #5bc0de !default;
$brand-warning: #f0ad4e !default; $brand-warning: $ascribe-brand-warning !default;
$brand-danger: #d9534f !default; $brand-danger: $ascribe-brand-danger !default;
//== Scaffolding //== Scaffolding
@ -107,9 +107,9 @@ $padding-xs-horizontal: 5px !default;
$line-height-large: 1.3333333 !default; // extra decimals for Win 8.1 Chrome $line-height-large: 1.3333333 !default; // extra decimals for Win 8.1 Chrome
$line-height-small: 1.5 !default; $line-height-small: 1.5 !default;
$border-radius-base: 4px !default; $border-radius-base: 0 !default;
$border-radius-large: 6px !default; $border-radius-large: 0 !default;
$border-radius-small: 3px !default; $border-radius-small: 0 !default;
//** Global color for active items (e.g., navs or dropdowns). //** Global color for active items (e.g., navs or dropdowns).
$component-active-color: #fff !default; $component-active-color: #fff !default;
@ -149,9 +149,9 @@ $table-border-color: #ddd !default;
$btn-font-weight: normal !default; $btn-font-weight: normal !default;
$btn-default-color: #333 !default; $btn-default-color: white !default;
$btn-default-bg: #fff !default; $btn-default-bg: $ascribe-color-full !default;
$btn-default-border: #ccc !default; $btn-default-border: $ascribe-color-full !default;
$btn-primary-color: #fff !default; $btn-primary-color: #fff !default;
$btn-primary-bg: $brand-primary !default; $btn-primary-bg: $brand-primary !default;
@ -171,7 +171,7 @@ $btn-warning-border: darken($btn-warning-bg, 5%) !default;
$btn-danger-color: #fff !default; $btn-danger-color: #fff !default;
$btn-danger-bg: $brand-danger !default; $btn-danger-bg: $brand-danger !default;
$btn-danger-border: darken($btn-danger-bg, 5%) !default; $btn-danger-border: $brand-danger !default;
$btn-link-disabled-color: $gray-light !default; $btn-link-disabled-color: $gray-light !default;
@ -186,7 +186,7 @@ $input-bg: #fff !default;
$input-bg-disabled: $gray-lighter !default; $input-bg-disabled: $gray-lighter !default;
//** Text color for `<input>`s //** Text color for `<input>`s
$input-color: $gray !default; $input-color: white !default;
//** `<input>` border color //** `<input>` border color
$input-border: #ccc !default; $input-border: #ccc !default;
@ -219,9 +219,9 @@ $legend-color: $gray-dark !default;
$legend-border-color: #e5e5e5 !default; $legend-border-color: #e5e5e5 !default;
//** Background color for textual input addons //** Background color for textual input addons
$input-group-addon-bg: $gray-lighter !default; $input-group-addon-bg: $ascribe-color-full !default;
//** Border color for textual input addons //** Border color for textual input addons
$input-group-addon-border-color: $input-border !default; $input-group-addon-border-color: $ascribe-color-full !default;
//** Disabled cursor for form controls and buttons. //** Disabled cursor for form controls and buttons.
$cursor-disabled: not-allowed !default; $cursor-disabled: not-allowed !default;