From c2cff6a63a8d69cd2096e8716a3cdce0dab6807c Mon Sep 17 00:00:00 2001 From: diminator Date: Wed, 24 Jun 2015 17:44:05 +0200 Subject: [PATCH 01/53] removed src fixed emails in notification instead of username --- gulpfile.js | 3 ++- js/components/register_piece.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index e570c2ff..d7106b19 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -91,7 +91,8 @@ gulp.task('browser-sync', function() { browserSync({ files: config.filesToWatch, proxy: 'http://localhost:4000', - port: 3000 + port: 3000, + browser: "chromium-browser" }); }); diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 384f0d23..693c43be 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -40,7 +40,7 @@ let RegisterPiece = React.createClass( { return data; }, handleChange(){ - this.setState({digital_work_key: this.refs.uploader.refs.fineuploader.state.filesToUpload[0].key}) + this.setState({digital_work_key: this.refs.uploader.refs.fineuploader.state.filesToUpload[0].key}); }, render() { From b7e933c94385eeceaa1c56467f0d1faf8d56151f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 25 Jun 2015 13:28:49 +0200 Subject: [PATCH 02/53] add delete buttons --- .../file_drag_and_drop_preview.js | 18 +++--- .../file_drag_and_drop_preview_image.js | 3 + .../file_drag_and_drop_preview_other.js | 7 +- js/components/register_piece.js | 2 +- sass/ascribe_react_s3_fineuploader.scss | 64 ------------------- sass/ascribe_uploader.scss | 34 +++++++++- sass/main.scss | 1 - 7 files changed, 50 insertions(+), 79 deletions(-) delete mode 100644 sass/ascribe_react_s3_fineuploader.scss diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview.js b/js/components/ascribe_uploader/file_drag_and_drop_preview.js index 4f3980c3..2842f925 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview.js @@ -1,3 +1,5 @@ +'use strict'; + import React from 'react'; import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image'; @@ -6,7 +8,7 @@ import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other'; let FileDragAndDropPreview = React.createClass({ - propsTypes: { + propTypes: { file: React.PropTypes.shape({ url: React.PropTypes.string, type: React.PropTypes.string @@ -28,20 +30,20 @@ let FileDragAndDropPreview = React.createClass({ render() { let previewElement; - // Decide wether an image or a placeholder picture should be displayed + // Decide whether an image or a placeholder picture should be displayed if(this.props.file.type.split('/')[0] === 'image') { - previewElement = (); } else { - previewElement = (); } return ( -
{previewElement}
diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js index 53790191..f2a12c36 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js @@ -1,3 +1,5 @@ +'use strict'; + import React from 'react'; import ProgressBar from 'react-progressbar'; @@ -18,6 +20,7 @@ let FileDragAndDropPreviewImage = React.createClass({ className="file-drag-and-drop-preview-image" style={imageStyle}> + ); } diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js index 18f4031c..96661c67 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js @@ -1,3 +1,5 @@ +'use strict'; + import React from 'react'; import ProgressBar from 'react-progressbar'; @@ -8,12 +10,13 @@ let FileDragAndDropPreviewOther = React.createClass({ }, render() { - return( -
+ {'.' + this.props.type}
diff --git a/js/components/register_piece.js b/js/components/register_piece.js index b12d2c2b..5ed7b0ba 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -179,7 +179,7 @@ let FileUploader = React.createClass( { } return name; }} - multiple={false}/> + multiple={true}/> ); } }); diff --git a/sass/ascribe_react_s3_fineuploader.scss b/sass/ascribe_react_s3_fineuploader.scss deleted file mode 100644 index e6583313..00000000 --- a/sass/ascribe_react_s3_fineuploader.scss +++ /dev/null @@ -1,64 +0,0 @@ -.file-drag-and-drop { - display: table-cell; - outline: 1px dashed #616161; - cursor: pointer; - vertical-align: middle; - text-align: center; - height:208px; - width: 672px; - background-color: #FAFAFA; - transition: .1s linear background-color; -} - -.file-drag-and-drop:hover { - background-color: rgba(72, 218, 203, 0.2); -} - -.file-drag-and-drop > span { - font-size: 1.5em; -} - -.has-files { - text-align: left; - padding: 3em 0 0 0; -} - -.file-drag-and-drop-position { - display: inline-block; - margin: 0 0 3em 3em; - float:left; -} - -.file-drag-and-drop-preview-table-wrapper { - display: table; - height:94px; - width:104px; -} - -.file-drag-and-drop-preview { - overflow:hidden; - cursor: default; - background-color: #EEEEEE; - border: 1px solid #616161; -} - -.file-drag-and-drop-preview-image { - display: table; - height:104px; - width:104px; - overflow:hidden; - border: 1px solid #616161; -} - -.file-drag-and-drop-preview-other { - display: table-cell; - text-align: center; - vertical-align: middle; - -} - -.file-drag-and-drop-preview-other span { - font-size: 1.1em; - display: block; - margin-top: -10px; -} diff --git a/sass/ascribe_uploader.scss b/sass/ascribe_uploader.scss index 487aa1b0..adf3bd23 100644 --- a/sass/ascribe_uploader.scss +++ b/sass/ascribe_uploader.scss @@ -56,6 +56,33 @@ width:104px; overflow:hidden; border: 1px solid #616161; + text-align: center; +} + +.file-drag-and-drop-preview-image .delete-file { + font-size: 3em; + margin-top: .5em; + color: white; + text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black; + cursor: pointer; + + &:hover { + color: #d9534f; + } +} + +.file-drag-and-drop-preview-other .delete-file { + position: relative; + top: .45em; + margin-top: 0; + font-size: 3em; + color: white; + text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black; + cursor: pointer; + + &:hover { + color: #d9534f; + } } .file-drag-and-drop-preview-other { @@ -65,8 +92,9 @@ } -.file-drag-and-drop-preview-other span { - font-size: 1.1em; +.file-drag-and-drop-preview-other span:not(:first-child) { + font-size: 1.2em; + font-family: mercury_light; display: block; - margin-top: -10px; + margin-top: 1.5em; } diff --git a/sass/main.scss b/sass/main.scss index 0fa799ab..0593624e 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -22,7 +22,6 @@ $BASE_URL: '<%= BASE_URL %>'; @import 'ascribe_piece_register'; @import 'offset_right'; @import 'ascribe_settings'; -@import 'ascribe_react_s3_fineuploader'; body { background-color: #FDFDFD; From ae17d2cb035e20a14ffc55b6be8716d25769b81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 25 Jun 2015 13:46:10 +0200 Subject: [PATCH 03/53] first cut: delete functionality --- .../ascribe_uploader/file_drag_and_drop.js | 1 - .../ascribe_uploader/file_drag_and_drop_preview.js | 13 +++++++------ .../file_drag_and_drop_preview_image.js | 4 +++- .../file_drag_and_drop_preview_iterator.js | 4 +++- .../file_drag_and_drop_preview_other.js | 4 +++- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index 49fd69f9..e34270ed 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -99,7 +99,6 @@ var FileDragAndDrop = React.createClass({ }, render: function () { - console.log(this.props.dropzoneInactive); let hasFiles = this.props.filesToUpload.length > 0; let className = hasFiles ? 'file-drag-and-drop has-files ' : 'file-drag-and-drop '; className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview.js b/js/components/ascribe_uploader/file_drag_and_drop_preview.js index 2842f925..fabcddb0 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview.js @@ -33,18 +33,19 @@ let FileDragAndDropPreview = React.createClass({ // Decide whether an image or a placeholder picture should be displayed if(this.props.file.type.split('/')[0] === 'image') { previewElement = (); + onClick={this.handleDeleteFile} + progress={this.props.file.progress} + url={this.props.file.url}/>); } else { previewElement = (); + onClick={this.handleDeleteFile} + progress={this.props.file.progress} + type={this.props.file.type.split('/')[1]}/>); } return (
+ className="file-drag-and-drop-position"> {previewElement}
); diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js index f2a12c36..36917e47 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js @@ -6,7 +6,8 @@ import ProgressBar from 'react-progressbar'; let FileDragAndDropPreviewImage = React.createClass({ propTypes: { progress: React.PropTypes.number, - url: React.PropTypes.string + url: React.PropTypes.string, + onClick: React.PropTypes.func }, render() { @@ -17,6 +18,7 @@ let FileDragAndDropPreviewImage = React.createClass({ return (
diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js index 47168793..82635ee4 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js @@ -1,3 +1,5 @@ +'use strict'; + import React from 'react'; import FileDragAndDropPreview from './file_drag_and_drop_preview'; @@ -14,7 +16,7 @@ let FileDragAndDropPreviewIterator = React.createClass({
{this.props.files.map((file, i) => { return ( - diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js index 96661c67..60141f28 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js @@ -6,12 +6,14 @@ import ProgressBar from 'react-progressbar'; let FileDragAndDropPreviewOther = React.createClass({ propTypes: { type: React.PropTypes.string, - progress: React.PropTypes.number + progress: React.PropTypes.number, + onClick: React.PropTypes.func }, render() { return (
From 08f574a42b6e5cb54701a6b046987cfd22f4b512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 25 Jun 2015 14:15:02 +0200 Subject: [PATCH 04/53] finish delete functionality --- .../file_drag_and_drop_preview_image.js | 2 +- .../file_drag_and_drop_preview_other.js | 2 +- .../ascribe_uploader/react_s3_fine_uploader.js | 15 ++++++++++----- sass/ascribe_uploader.scss | 2 -- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js index 36917e47..dcbc0652 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js @@ -22,7 +22,7 @@ let FileDragAndDropPreviewImage = React.createClass({ className="file-drag-and-drop-preview-image" style={imageStyle}> - + {this.props.progress === 100 ? : null}
); } diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js index 60141f28..36c1ccdd 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js @@ -18,7 +18,7 @@ let FileDragAndDropPreviewOther = React.createClass({
- + {this.props.progress === 100 ? : null} {'.' + this.props.type}
diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index d0b61ad6..626d8bd7 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -229,18 +229,23 @@ var ReactS3FineUploader = React.createClass({ onDeleteComplete(id, xhr, isError) { if(isError) { + let notification = new GlobalNotificationModel('Couldn\'t delete file', 'danger', 10000); + GlobalNotificationActions.appendGlobalNotification(notification); + } else { // also, sync files from state with the ones from fineuploader let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload)); + // splice because I can filesToUpload.splice(id, 1); // set state - this.setState({ - filesToUpload: React.addons.update(this.state.filesToUpload, {$set: filesToUpload}) + let newState = React.addons.update(this.state, { + filesToUpload: { $set: filesToUpload } }); - } else { - console.log(id); - // TODO: add global notification + this.setState(newState); + + let notification = new GlobalNotificationModel('File deleted', 'success', 10000); + GlobalNotificationActions.appendGlobalNotification(notification); } }, diff --git a/sass/ascribe_uploader.scss b/sass/ascribe_uploader.scss index adf3bd23..aa76003e 100644 --- a/sass/ascribe_uploader.scss +++ b/sass/ascribe_uploader.scss @@ -93,8 +93,6 @@ } .file-drag-and-drop-preview-other span:not(:first-child) { - font-size: 1.2em; - font-family: mercury_light; display: block; margin-top: 1.5em; } From 12da6c1426613a93d332e048955a1735fcc1489b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Thu, 25 Jun 2015 14:18:55 +0200 Subject: [PATCH 05/53] add todo --- js/components/ascribe_uploader/file_drag_and_drop_preview.js | 3 +++ js/components/ascribe_uploader/react_s3_fine_uploader.js | 1 + 2 files changed, 4 insertions(+) diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview.js b/js/components/ascribe_uploader/file_drag_and_drop_preview.js index fabcddb0..466ebe0d 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview.js @@ -27,6 +27,9 @@ let FileDragAndDropPreview = React.createClass({ } }, + // implement a handle cancel action here that triggers fineuploaders cancel method + // to delete files that are currently uploading + render() { let previewElement; diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 626d8bd7..985e5d90 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -221,6 +221,7 @@ var ReactS3FineUploader = React.createClass({ onCancel() { console.log('cancel'); + // handle file removal here, for this.state.filesToUpload (same as in onDeleteComplete) }, onSessionRequestComplete() { From fba30a565bc97b555b9f011714b07c192dbb7b3f Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 25 Jun 2015 14:39:39 +0200 Subject: [PATCH 06/53] coa renders per page verify --- js/actions/coa_actions.js | 36 +++++++++ js/components/coa_verify_container.js | 101 ++++++++++++++++++++++++++ js/components/edition.js | 64 +++++++++++++++- js/components/signup_container.js | 2 +- js/constants/api_urls.js | 3 + js/fetchers/coa_fetcher.js | 19 +++++ js/routes.js | 3 + js/stores/coa_store.js | 18 +++++ sass/ascribe_edition.scss | 16 ++++ sass/ascribe_textarea.scss | 3 +- 10 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 js/actions/coa_actions.js create mode 100644 js/components/coa_verify_container.js create mode 100644 js/fetchers/coa_fetcher.js create mode 100644 js/stores/coa_store.js diff --git a/js/actions/coa_actions.js b/js/actions/coa_actions.js new file mode 100644 index 00000000..b475aeb7 --- /dev/null +++ b/js/actions/coa_actions.js @@ -0,0 +1,36 @@ +'use strict'; + +import alt from '../alt'; +import CoaFetcher from '../fetchers/coa_fetcher'; + + +class CoaActions { + constructor() { + this.generateActions( + 'updateCoa' + ); + } + + fetchOne(id) { + CoaFetcher.fetchOne(id) + .then((res) => { + this.actions.updateCoa(res.coa); + console.log(res.coa); + }) + .catch((err) => { + console.log(err); + }); + } + create(edition) { + CoaFetcher.create(edition.bitcoin_id) + .then((res) => { + this.actions.updateCoa(res.coa); + console.log(res.coa); + }) + .catch((err) => { + console.log(err); + }); + } +} + +export default alt.createActions(CoaActions); diff --git a/js/components/coa_verify_container.js b/js/components/coa_verify_container.js new file mode 100644 index 00000000..b1721999 --- /dev/null +++ b/js/components/coa_verify_container.js @@ -0,0 +1,101 @@ +'use strict'; + +import React from 'react'; +import Router from 'react-router'; + +import GlobalNotificationModel from '../models/global_notification_model'; +import GlobalNotificationActions from '../actions/global_notification_actions'; + +import Form from './ascribe_forms/form'; +import Property from './ascribe_forms/property'; +import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable'; + +import apiUrls from '../constants/api_urls'; + + +let CoaVerifyContainer = React.createClass({ + mixins: [Router.Navigation], + + render() { + return ( +
+
+
+ Verify your Certificate of Authenticity +
+ + +
+
+ ascribe is using the following public key for verification: +
+
+                -----BEGIN PUBLIC KEY-----
+                MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDddadqY31kKPFYk8PQA8BWSTbm
+                gaGf9KEYBALp2nWAJcwq80qBzGF+gfi0Z+yb4ooeKHl27GnuxZYValE1Z5ZujfeJ
+                TgO4li59ZMYiah8oXZp/OysrBwCvWw0PtWd8/D9Nc4PqyOz5gzEh6kFah5VsuAke
+                Znu2w7KmeLZ85SmwEQIDAQAB
+                -----END PUBLIC KEY-----
+                
+
+ ); + } +}); + + +let CoaVerifyForm = React.createClass({ + mixins: [Router.Navigation], + + handleSuccess(response){ + let notification = null; + if (response.verdict){ + notification = new GlobalNotificationModel('Certificate of Authenticity successfully verified', 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + } + }, + + render() { + return ( +
+
+ Verify your Certificate of Authenticity + } + spinner={ + + }> + + + + + + +
+
+
+ ); + } +}); + + +export default CoaVerifyContainer; \ No newline at end of file diff --git a/js/components/edition.js b/js/components/edition.js index 34975699..68febdd7 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -6,9 +6,12 @@ import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; import Button from 'react-bootstrap/lib/Button'; import Glyphicon from 'react-bootstrap/lib/Glyphicon'; +import Panel from 'react-bootstrap/lib/Panel'; import UserActions from '../actions/user_actions'; import UserStore from '../stores/user_store'; +import CoaActions from '../actions/coa_actions'; +import CoaStore from '../stores/coa_store'; import MediaPlayer from './ascribe_media/media_player'; @@ -29,6 +32,7 @@ import GlobalNotificationActions from '../actions/global_notification_actions'; import requests from '../utils/requests'; import apiUrls from '../constants/api_urls'; +import AppConstants from '../constants/application_constants'; /** * This is the component that implements display-specific functionality @@ -109,13 +113,20 @@ let Edition = React.createClass({ -1 || Object.keys(this.props.edition.extra_data).length > 0}> + -1}> + + + 0}> @@ -421,4 +432,55 @@ let EditionFurtherDetails = React.createClass({ } }); +let CoaDetails = React.createClass({ + propTypes: { + edition: React.PropTypes.object + }, + + getInitialState() { + return CoaStore.getState(); + }, + + componentDidMount() { + CoaStore.listen(this.onChange); + if (this.props.edition.coa) { + CoaActions.fetchOne(this.props.edition.coa); + } + else{ + console.log('create coa'); + CoaActions.create(this.props.edition); + } + }, + + componentWillUnmount() { + CoaStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + if (this.state.coa.url_safe) { + return ( +
+

+ + +

+
+ ); + } + return ( +
+ +
); + + } +}); + export default Edition; diff --git a/js/components/signup_container.js b/js/components/signup_container.js index b8849669..65dfe217 100644 --- a/js/components/signup_container.js +++ b/js/components/signup_container.js @@ -115,7 +115,7 @@ let SignupForm = React.createClass({ name='promo_code' label="Promocode">
diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js index 608893e1..e6bbd3af 100644 --- a/js/constants/api_urls.js +++ b/js/constants/api_urls.js @@ -6,6 +6,9 @@ let apiUrls = { 'applications': AppConstants.apiEndpoint + 'applications/', 'application_token_refresh': AppConstants.apiEndpoint + 'applications/refresh_token/', 'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/', + 'coa': AppConstants.apiEndpoint + 'coa/${id}/', + 'coa_create': AppConstants.apiEndpoint + 'coa/', + 'coa_verify': AppConstants.apiEndpoint + 'coa/verify_coa/', 'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/', 'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/', 'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/${edition_id}/', diff --git a/js/fetchers/coa_fetcher.js b/js/fetchers/coa_fetcher.js new file mode 100644 index 00000000..48ee9e73 --- /dev/null +++ b/js/fetchers/coa_fetcher.js @@ -0,0 +1,19 @@ +'use strict'; + +import requests from '../utils/requests'; + +let CoaFetcher = { + /** + * Fetch one user from the API. + * If no arg is supplied, load the current user + */ + fetchOne(id) { + return requests.get('coa', {'id': id}); + }, + create(bitcoinId) { + console.log(bitcoinId); + return requests.post('coa_create', {body: {'bitcoin_id': bitcoinId}}); + } +}; + +export default CoaFetcher; diff --git a/js/routes.js b/js/routes.js index 02ed27c5..7dc34687 100644 --- a/js/routes.js +++ b/js/routes.js @@ -12,6 +12,8 @@ import SignupContainer from './components/signup_container'; import PasswordResetContainer from './components/password_reset_container'; import SettingsContainer from './components/settings_container'; +import CoaVerifyContainer from './components/coa_verify_container'; + import AppConstants from './constants/application_constants'; import RegisterPiece from './components/register_piece'; @@ -28,6 +30,7 @@ let routes = ( + diff --git a/js/stores/coa_store.js b/js/stores/coa_store.js new file mode 100644 index 00000000..1aa762f3 --- /dev/null +++ b/js/stores/coa_store.js @@ -0,0 +1,18 @@ +'use strict'; + +import alt from '../alt'; +import CoaActions from '../actions/coa_actions'; + + +class CoaStore { + constructor() { + this.coa = {}; + this.bindActions(CoaActions); + } + + onUpdateCoa(coa) { + this.coa = coa; + } +} + +export default alt.createStore(CoaStore, 'CoaStore'); diff --git a/sass/ascribe_edition.scss b/sass/ascribe_edition.scss index acf4b529..f1af95d2 100644 --- a/sass/ascribe_edition.scss +++ b/sass/ascribe_edition.scss @@ -28,4 +28,20 @@ width:100%; margin-top: 1em; +} + +.coa-file-wrapper{ + display: table; + height: 200px; + overflow: hidden; + margin: 0 auto; + width: 100%; + padding: 1em; +} + +.coa-file { + display: table-cell; + vertical-align: middle; + border: 1px solid #CCC; + background-color: #F8F8F8; } \ No newline at end of file diff --git a/sass/ascribe_textarea.scss b/sass/ascribe_textarea.scss index 8d52ca64..2d368dbf 100644 --- a/sass/ascribe_textarea.scss +++ b/sass/ascribe_textarea.scss @@ -5,7 +5,8 @@ } .ascribe-textarea-editable:hover { - border: 1px solid #AAA; + //border: 1px solid #AAA;; + border: none; } .ascribe-pre{ From ddabedfb73802c30194da163a415254139c25e2a Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 25 Jun 2015 14:47:29 +0200 Subject: [PATCH 07/53] browserify-shim --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e79cf8b..dbf65f45 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "devDependencies": { "babel-eslint": "^3.1.11", "babel-jest": "^5.2.0", - "browserify-shim": "^3.8.9", "jest-cli": "^0.4.0" }, "dependencies": { @@ -44,6 +43,7 @@ "bootstrap-sass": "^3.3.4", "browser-sync": "^2.7.5", "browserify": "^9.0.8", + "browserify-shim": "^3.8.9", "classnames": "^1.2.2", "compression": "^1.4.4", "envify": "^3.4.0", From d980ef7949fe0ee8ca446d735a4c5279c783b3f2 Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 25 Jun 2015 15:23:10 +0200 Subject: [PATCH 08/53] verify button link --- js/components/edition.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/js/components/edition.js b/js/components/edition.js index 68febdd7..cb5f0dbb 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -1,12 +1,12 @@ 'use strict'; import React from 'react'; +import Router from 'react-router'; import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; import Button from 'react-bootstrap/lib/Button'; import Glyphicon from 'react-bootstrap/lib/Glyphicon'; -import Panel from 'react-bootstrap/lib/Panel'; import UserActions from '../actions/user_actions'; import UserStore from '../stores/user_store'; @@ -30,10 +30,10 @@ import AclButtonList from './ascribe_buttons/acl_button_list'; import GlobalNotificationModel from '../models/global_notification_model'; import GlobalNotificationActions from '../actions/global_notification_actions'; -import requests from '../utils/requests'; import apiUrls from '../constants/api_urls'; import AppConstants from '../constants/application_constants'; +let Link = Router.Link; /** * This is the component that implements display-specific functionality */ @@ -468,9 +468,12 @@ let CoaDetails = React.createClass({ - + + + +

); From 93c1b2e826c870e3c2b3e469e33d4f2a5311f2da Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 25 Jun 2015 15:25:34 +0200 Subject: [PATCH 09/53] login location with baseurl --- js/components/login_container.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/components/login_container.js b/js/components/login_container.js index 8c149d8c..d4085540 100644 --- a/js/components/login_container.js +++ b/js/components/login_container.js @@ -10,6 +10,7 @@ import Form from './ascribe_forms/form'; import Property from './ascribe_forms/property'; import apiUrls from '../constants/api_urls'; +import AppConstants from '../constants/application_constants'; let LoginContainer = React.createClass({ @@ -45,7 +46,7 @@ let LoginForm = React.createClass({ Users on Stack Overflow claim this is a bug in chrome and should be fixed in the future. Until then, we redirect the HARD way, but reloading the whole page using window.location */ - window.location = '/collection'; + window.location = AppConstants.baseUrl + '/collection'; }, render() { From 64017f604c034ad8173d1321cb1cc11c9d5dd4c8 Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 25 Jun 2015 15:30:47 +0200 Subject: [PATCH 10/53] login location with baseurl --- js/components/login_container.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/components/login_container.js b/js/components/login_container.js index d4085540..4363971f 100644 --- a/js/components/login_container.js +++ b/js/components/login_container.js @@ -46,7 +46,7 @@ let LoginForm = React.createClass({ Users on Stack Overflow claim this is a bug in chrome and should be fixed in the future. Until then, we redirect the HARD way, but reloading the whole page using window.location */ - window.location = AppConstants.baseUrl + '/collection'; + window.location = AppConstants.baseUrl + 'collection'; }, render() { From b1ad4cc36f37ea8f7365da72b7a0fb62a259a9fd Mon Sep 17 00:00:00 2001 From: diminator Date: Thu, 25 Jun 2015 23:25:03 +0200 Subject: [PATCH 11/53] withdraw transfer part one --- js/components/edition.js | 489 ------------------------------- js/components/login_container.js | 2 +- sass/main.scss | 6 + 3 files changed, 7 insertions(+), 490 deletions(-) diff --git a/js/components/edition.js b/js/components/edition.js index cb5f0dbb..e69de29b 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -1,489 +0,0 @@ -'use strict'; - -import React from 'react'; -import Router from 'react-router'; - -import Row from 'react-bootstrap/lib/Row'; -import Col from 'react-bootstrap/lib/Col'; -import Button from 'react-bootstrap/lib/Button'; -import Glyphicon from 'react-bootstrap/lib/Glyphicon'; - -import UserActions from '../actions/user_actions'; -import UserStore from '../stores/user_store'; -import CoaActions from '../actions/coa_actions'; -import CoaStore from '../stores/coa_store'; - -import MediaPlayer from './ascribe_media/media_player'; - -import CollapsibleParagraph from './ascribe_collapsible/collapsible_paragraph'; - -import Form from './ascribe_forms/form'; -import Property from './ascribe_forms/property'; -import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable'; - -import PieceExtraDataForm from './ascribe_forms/form_piece_extradata'; -import RequestActionForm from './ascribe_forms/form_request_action'; - -import EditionActions from '../actions/edition_actions'; -import AclButtonList from './ascribe_buttons/acl_button_list'; - -import GlobalNotificationModel from '../models/global_notification_model'; -import GlobalNotificationActions from '../actions/global_notification_actions'; - -import apiUrls from '../constants/api_urls'; -import AppConstants from '../constants/application_constants'; - -let Link = Router.Link; -/** - * This is the component that implements display-specific functionality - */ -let Edition = React.createClass({ - propTypes: { - edition: React.PropTypes.object, - loadEdition: React.PropTypes.func - }, - - getInitialState() { - return UserStore.getState(); - }, - - componentDidMount() { - UserStore.listen(this.onChange); - UserActions.fetchCurrentUser(); - }, - - componentWillUnmount() { - UserStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - - render() { - let thumbnail = this.props.edition.thumbnail; - let mimetype = this.props.edition.digital_work.mime; - let extraData = null; - - if (this.props.edition.digital_work.encoding_urls) { - extraData = this.props.edition.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; }); - } - - let bitcoinIdValue = ( - {this.props.edition.bitcoin_id} - ); - - let hashOfArtwork = ( - {this.props.edition.hash_as_address} - ); - - let ownerAddress = ( - {this.props.edition.btc_owner_address_noprefix} - ); - - return ( - - - -

- -

- - - - - - -1 || this.props.edition.public_note)}> - - - - - -1 || Object.keys(this.props.edition.extra_data).length > 0}> - - - - -1}> - - - - 0}> - - - - 0}> - - - - 0}> - - - - -
- -
{bitcoinIdValue}
-
- -
{hashOfArtwork}
-
- -
{ownerAddress}
-
-
-
-
- -
- ); - } -}); - -let EditionHeader = React.createClass({ - propTypes: { - edition: React.PropTypes.object - }, - - render() { - var titleHtml =
{this.props.edition.title}
; - return ( -
- - - -
-
- ); - } -}); - - -let EditionSummary = React.createClass({ - propTypes: { - edition: React.PropTypes.object - }, - - handleSuccess(){ - EditionActions.fetchOne(this.props.edition.id); - }, - showNotification(response){ - this.handleSuccess(); - let notification = new GlobalNotificationModel(response.notification, 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - }, - render() { - let status = null; - if (this.props.edition.status.length > 0){ - status = ; - } - let actions = null; - if (this.props.edition.request_action && this.props.edition.request_action.length > 0){ - actions = ( - ); - } - else { - actions = ( - - - - - ); - } - - return ( -
- - - - {status} -
- {actions} -
-
- ); - - } -}); - - -let EditionDetailProperty = React.createClass({ - propTypes: { - label: React.PropTypes.string, - value: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.element - ]), - separator: React.PropTypes.string, - labelClassName: React.PropTypes.string, - valueClassName: React.PropTypes.string - }, - - getDefaultProps() { - return { - separator: ':', - labelClassName: 'col-xs-5 col-sm-4 col-md-3 col-lg-3', - valueClassName: 'col-xs-7 col-sm-8 col-md-9 col-lg-9' - }; - }, - - render() { - return ( -
-
-
-
{ this.props.label + this.props.separator}
-
-
-
{ this.props.value }
-
-
-
- ); - } -}); - -let EditionDetailHistoryIterator = React.createClass({ - propTypes: { - history: React.PropTypes.array - }, - - render() { - return ( -
- {this.props.history.map((historicalEvent, i) => { - return ( - - ); - })} -
- ); - } -}); - -let EditionPersonalNote = React.createClass({ - propTypes: { - edition: React.PropTypes.object, - currentUser: React.PropTypes.object, - handleSuccess: React.PropTypes.func - }, - showNotification(){ - this.props.handleSuccess(); - let notification = new GlobalNotificationModel('Private note saved', 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - }, - - render() { - if (this.props.currentUser.username && true || false) { - return ( -
- - - - -
-
- ); - } - return null; - } -}); - -let EditionPublicEditionNote = React.createClass({ - propTypes: { - edition: React.PropTypes.object, - handleSuccess: React.PropTypes.func - }, - showNotification(){ - this.props.handleSuccess(); - let notification = new GlobalNotificationModel('Public note saved', 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - }, - render() { - let isEditable = this.props.edition.acl.indexOf('edit') > -1; - if (this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note){ - return ( -
- - - - -
-
- ); - } - return null; - } -}); - - -let EditionFurtherDetails = React.createClass({ - propTypes: { - edition: React.PropTypes.object, - handleSuccess: React.PropTypes.func - }, - showNotification(){ - this.props.handleSuccess(); - let notification = new GlobalNotificationModel('Details updated', 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - }, - - render() { - let editable = this.props.edition.acl.indexOf('edit') > -1; - return ( - - - - - - - - ); - } -}); - -let CoaDetails = React.createClass({ - propTypes: { - edition: React.PropTypes.object - }, - - getInitialState() { - return CoaStore.getState(); - }, - - componentDidMount() { - CoaStore.listen(this.onChange); - if (this.props.edition.coa) { - CoaActions.fetchOne(this.props.edition.coa); - } - else{ - console.log('create coa'); - CoaActions.create(this.props.edition); - } - }, - - componentWillUnmount() { - CoaStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - - render() { - if (this.state.coa.url_safe) { - return ( -
-

- - - - - -

-
- ); - } - return ( -
- -
); - - } -}); - -export default Edition; diff --git a/js/components/login_container.js b/js/components/login_container.js index 4363971f..24b63ce7 100644 --- a/js/components/login_container.js +++ b/js/components/login_container.js @@ -40,7 +40,7 @@ let LoginForm = React.createClass({ GlobalNotificationActions.appendGlobalNotification(notification); /*Taken from http://stackoverflow.com/a/14916411 */ - /* + /* We actually have to trick the Browser into showing the "save password" dialog as Chrome expects the login page to be reloaded after the login. Users on Stack Overflow claim this is a bug in chrome and should be fixed in the future. diff --git a/sass/main.scss b/sass/main.scss index 0fa799ab..7b2a7494 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -33,6 +33,12 @@ body { display: none; } +.no-margin{ + margin: 0; +} +.no-padding{ + padding: 0; +} .navbar-default { border: none; border-left:0; From 7e115e26ad665c42c8728e8f69ce436dedd0cc22 Mon Sep 17 00:00:00 2001 From: diminator Date: Fri, 26 Jun 2015 00:38:40 +0200 Subject: [PATCH 12/53] withdraw transfer part one --- js/components/edition.js | 525 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) diff --git a/js/components/edition.js b/js/components/edition.js index e69de29b..682e0c9b 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -0,0 +1,525 @@ +'use strict'; + +import React from 'react'; +import Router from 'react-router'; + +import Row from 'react-bootstrap/lib/Row'; +import Col from 'react-bootstrap/lib/Col'; +import Button from 'react-bootstrap/lib/Button'; +import Glyphicon from 'react-bootstrap/lib/Glyphicon'; + +import UserActions from '../actions/user_actions'; +import UserStore from '../stores/user_store'; +import CoaActions from '../actions/coa_actions'; +import CoaStore from '../stores/coa_store'; + +import MediaPlayer from './ascribe_media/media_player'; + +import CollapsibleParagraph from './ascribe_collapsible/collapsible_paragraph'; + +import Form from './ascribe_forms/form'; +import Property from './ascribe_forms/property'; +import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable'; + +import PieceExtraDataForm from './ascribe_forms/form_piece_extradata'; +import RequestActionForm from './ascribe_forms/form_request_action'; + +import EditionActions from '../actions/edition_actions'; +import AclButtonList from './ascribe_buttons/acl_button_list'; + +import GlobalNotificationModel from '../models/global_notification_model'; +import GlobalNotificationActions from '../actions/global_notification_actions'; + +import apiUrls from '../constants/api_urls'; +import AppConstants from '../constants/application_constants'; + +let Link = Router.Link; +/** + * This is the component that implements display-specific functionality + */ +let Edition = React.createClass({ + propTypes: { + edition: React.PropTypes.object, + loadEdition: React.PropTypes.func + }, + + getInitialState() { + return UserStore.getState(); + }, + + componentDidMount() { + UserStore.listen(this.onChange); + UserActions.fetchCurrentUser(); + }, + + componentWillUnmount() { + UserStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + let thumbnail = this.props.edition.thumbnail; + let mimetype = this.props.edition.digital_work.mime; + let extraData = null; + + if (this.props.edition.digital_work.encoding_urls) { + extraData = this.props.edition.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; }); + } + + let bitcoinIdValue = ( + {this.props.edition.bitcoin_id} + ); + + let hashOfArtwork = ( + {this.props.edition.hash_as_address} + ); + + let ownerAddress = ( + {this.props.edition.btc_owner_address_noprefix} + ); + + return ( + + + +

+ +

+ + + + + + -1 || this.props.edition.public_note)}> + + + + + -1 || Object.keys(this.props.edition.extra_data).length > 0}> + + + + -1}> + + + + 0}> + + + + 0}> + + + + 0}> + + + + +
+ +
{bitcoinIdValue}
+
+ +
{hashOfArtwork}
+
+ +
{ownerAddress}
+
+
+
+
+ +
+ ); + } +}); + +let EditionHeader = React.createClass({ + propTypes: { + edition: React.PropTypes.object + }, + + render() { + var titleHtml =
{this.props.edition.title}
; + return ( +
+ + + +
+
+ ); + } +}); + + +let EditionSummary = React.createClass({ + propTypes: { + edition: React.PropTypes.object + }, + + handleSuccess(){ + EditionActions.fetchOne(this.props.edition.id); + }, + showNotification(response){ + this.handleSuccess(); + let notification = new GlobalNotificationModel(response.notification, 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + render() { + let status = null; + if (this.props.edition.status.length > 0){ + let statusStr = this.props.edition.status.join().replace(/_/, ' '); + let statusAction = null; + if (this.props.edition.pending_new_owner && this.props.edition.acl.indexOf('withdraw_transfer') > -1){ + statusAction = ( + + ); + } + status =( + + {statusAction} + ); + } + let actions = null; + if (this.props.edition.request_action && this.props.edition.request_action.length > 0){ + actions = ( + ); + } + else { + actions = ( + + + + + ); + } + + return ( +
+ + + + {status} +
+ {actions} +
+
+ ); + + } +}); + + +let EditionDetailProperty = React.createClass({ + propTypes: { + label: React.PropTypes.string, + value: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element + ]), + separator: React.PropTypes.string, + labelClassName: React.PropTypes.string, + valueClassName: React.PropTypes.string + }, + + getDefaultProps() { + return { + separator: ':', + labelClassName: 'col-xs-5 col-sm-4 col-md-3 col-lg-3', + valueClassName: 'col-xs-7 col-sm-8 col-md-9 col-lg-9' + }; + }, + + render() { + let value = this.props.value; + if (this.props.children){ + value = ( +
+
+ { this.props.value } +
+
+ { this.props.children } +
+
); + } + return ( +
+
+
+
{ this.props.label + this.props.separator}
+
+
+ {value} +
+
+
+ ); + } +}); + +let EditionDetailHistoryIterator = React.createClass({ + propTypes: { + history: React.PropTypes.array + }, + + render() { + return ( +
+ {this.props.history.map((historicalEvent, i) => { + //return ( + // + //); + return( + +
{ historicalEvent[1] }
+
+ ) + })} +
+ + ); + } +}); + +let EditionPersonalNote = React.createClass({ + propTypes: { + edition: React.PropTypes.object, + currentUser: React.PropTypes.object, + handleSuccess: React.PropTypes.func + }, + showNotification(){ + this.props.handleSuccess(); + let notification = new GlobalNotificationModel('Private note saved', 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + + render() { + if (this.props.currentUser.username && true || false) { + return ( +
+ + + + +
+
+ ); + } + return null; + } +}); + +let EditionPublicEditionNote = React.createClass({ + propTypes: { + edition: React.PropTypes.object, + handleSuccess: React.PropTypes.func + }, + showNotification(){ + this.props.handleSuccess(); + let notification = new GlobalNotificationModel('Public note saved', 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + render() { + let isEditable = this.props.edition.acl.indexOf('edit') > -1; + if (this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note){ + return ( +
+ + + + +
+
+ ); + } + return null; + } +}); + + +let EditionFurtherDetails = React.createClass({ + propTypes: { + edition: React.PropTypes.object, + handleSuccess: React.PropTypes.func + }, + showNotification(){ + this.props.handleSuccess(); + let notification = new GlobalNotificationModel('Details updated', 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + + render() { + let editable = this.props.edition.acl.indexOf('edit') > -1; + return ( + + + + + + + + ); + } +}); + +let CoaDetails = React.createClass({ + propTypes: { + edition: React.PropTypes.object + }, + + getInitialState() { + return CoaStore.getState(); + }, + + componentDidMount() { + CoaStore.listen(this.onChange); + if (this.props.edition.coa) { + CoaActions.fetchOne(this.props.edition.coa); + } + else{ + console.log('create coa'); + CoaActions.create(this.props.edition); + } + }, + + componentWillUnmount() { + CoaStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + }, + + render() { + if (this.state.coa.url_safe) { + return ( +
+

+ + + + + +

+
+ ); + } + return ( +
+ +
); + + } +}); + +export default Edition; From 99d6dd278c85a9365d69a214ab8d54b65dcc9e80 Mon Sep 17 00:00:00 2001 From: diminator Date: Fri, 26 Jun 2015 01:05:20 +0200 Subject: [PATCH 13/53] withdraw fix --- js/components/edition.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/js/components/edition.js b/js/components/edition.js index 682e0c9b..e583273d 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -224,7 +224,7 @@ let EditionSummary = React.createClass({ ); } - status =( + status = ( {statusAction} ); @@ -323,16 +323,7 @@ let EditionDetailHistoryIterator = React.createClass({ return (
{this.props.history.map((historicalEvent, i) => { - //return ( - // - //); - return( + return (
{ historicalEvent[1] }
- ) + ); })}
From eff25a980157d37ef659c4e9a268a91864615038 Mon Sep 17 00:00:00 2001 From: diminator Date: Fri, 26 Jun 2015 11:44:35 +0200 Subject: [PATCH 14/53] withdraw transfer with delete of transfer record --- js/components/edition.js | 27 +++++++++++++++++---------- js/constants/api_urls.js | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/js/components/edition.js b/js/components/edition.js index e583273d..b3af22f7 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -203,6 +203,9 @@ let EditionSummary = React.createClass({ edition: React.PropTypes.object }, + getTransferWithdrawData(){ + return {'bitcoin_id': this.props.edition.bitcoin_id}; + }, handleSuccess(){ EditionActions.fetchOne(this.props.edition.id); }, @@ -215,19 +218,23 @@ let EditionSummary = React.createClass({ let status = null; if (this.props.edition.status.length > 0){ let statusStr = this.props.edition.status.join().replace(/_/, ' '); - let statusAction = null; + status = ; if (this.props.edition.pending_new_owner && this.props.edition.acl.indexOf('withdraw_transfer') > -1){ - statusAction = ( - + status = ( +
+ + + +
); } - status = ( - - {statusAction} - ); } let actions = null; if (this.props.edition.request_action && this.props.edition.request_action.length > 0){ diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js index e6bbd3af..e1e5d15e 100644 --- a/js/constants/api_urls.js +++ b/js/constants/api_urls.js @@ -23,6 +23,7 @@ let apiUrls = { 'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/deny/', 'ownership_shares': AppConstants.apiEndpoint + 'ownership/shares/', 'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/', + 'ownership_transfers_withdraw': AppConstants.apiEndpoint + 'ownership/transfers/withdraw/', 'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/', 'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/', 'ownership_unconsigns_request': AppConstants.apiEndpoint + 'ownership/unconsigns/request/', From 5916c27c455ad6a787f3530dd098bd2b39eb4bb4 Mon Sep 17 00:00:00 2001 From: diminator Date: Mon, 29 Jun 2015 10:00:26 +0200 Subject: [PATCH 15/53] uploader defaultprops --- .../react_s3_fine_uploader.js | 69 +++++++++++++++++-- js/components/edition.js | 31 +++++++++ js/components/register_piece.js | 53 +------------- 3 files changed, 99 insertions(+), 54 deletions(-) diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 985e5d90..c2842e3c 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -6,6 +6,8 @@ import promise from 'es6-promise'; promise.polyfill(); import fetch from 'isomorphic-fetch'; +import AppConstants from '../../constants/application_constants'; + import fineUploader from 'fineUploader'; import FileDragAndDrop from './file_drag_and_drop'; @@ -18,7 +20,8 @@ var ReactS3FineUploader = React.createClass({ propTypes: { keyRoutine: React.PropTypes.shape({ url: React.PropTypes.string, - fileClass: React.PropTypes.string + fileClass: React.PropTypes.string, + bitcoinId: React.PropTypes.string }), createBlobRoutine: React.PropTypes.shape({ url: React.PropTypes.string @@ -84,7 +87,64 @@ var ReactS3FineUploader = React.createClass({ uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()) }; }, - + getDefaultProps() { + return { + autoUpload: true, + debug: false, + objectProperties: { + acl: 'public-read', + bucket: 'ascribe0' + }, + request: { + endpoint: 'https://ascribe0.s3.amazonaws.com', + accessKey: 'AKIAIVCZJ33WSCBQ3QDA' + }, + uploadSuccess: { + params: { + isBrowserPreviewCapable: fineUploader.supportedFeatures.imagePreviews + } + }, + signature: { + endpoint: AppConstants.serverUrl + 's3/signature/' + //customHeaders: { + // 'Authorization': 'OAuth ' + getCookie('sessionid') + //} + }, + deleteFile: { + enabled: true, + method: 'DELETE', + endpoint: AppConstants.serverUrl + 's3/delete' + //customHeaders: { + // 'X-CSRFToken': getCookie('csrftoken') + //} + }, + cors: { + expected: true + }, + chunking: { + enabled: true + }, + resume: { + enabled: true + }, + retry: { + enableAuto: false + }, + session: { + endpoint: null + }, + messages: { + unsupportedBrowser: '

Upload is not functional in IE7 as IE7 has no support for CORS!

' + }, + formatFileName: function(name){// fix maybe + if (name !== undefined && name.length > 26) { + name = name.slice(0, 15) + '...' + name.slice(-15); + } + return name; + }, + multiple: false + }; + }, propsToConfig() { let objectProperties = this.props.objectProperties; objectProperties.key = this.requestKey; @@ -140,7 +200,8 @@ var ReactS3FineUploader = React.createClass({ credentials: 'include', body: JSON.stringify({ 'filename': filename, - 'file_class': 'digitalwork' + 'file_class': this.props.keyRoutine.fileClass, + 'bitcoin_id': this.props.keyRoutine.bitcoinId }) }) .then((res) => { @@ -326,7 +387,7 @@ var ReactS3FineUploader = React.createClass({ onDrop={this.handleUploadFile} filesToUpload={this.state.filesToUpload} handleDeleteFile={this.handleDeleteFile} - multiple={this.props.multiple} + multiple={this.props.multiple} dropzoneInactive={!this.props.multiple && this.state.filesToUpload.length > 0} /> ); } diff --git a/js/components/edition.js b/js/components/edition.js index b3af22f7..78f36628 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -27,6 +27,9 @@ import RequestActionForm from './ascribe_forms/form_request_action'; import EditionActions from '../actions/edition_actions'; import AclButtonList from './ascribe_buttons/acl_button_list'; +import fineUploader from 'fineUploader'; +import ReactS3FineUploader from './ascribe_uploader/react_s3_fine_uploader'; + import GlobalNotificationModel from '../models/global_notification_model'; import GlobalNotificationActions from '../actions/global_notification_actions'; @@ -460,12 +463,40 @@ let EditionFurtherDetails = React.createClass({ handleSuccess={this.showNotification} editable={editable} edition={this.props.edition} /> + ); } }); +let FileUploader = React.createClass( { + handleChange(){ + this.setState({other_data_key: this.refs.fineuploader.state.filesToUpload[0].key}); + }, + render() { + return ( + + ); + } +}); + let CoaDetails = React.createClass({ propTypes: { edition: React.PropTypes.object diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 5ed7b0ba..a73fc2ea 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -2,8 +2,8 @@ import React from 'react'; +import { getCookie } from '../utils/fetch_api_utils'; import AppConstants from '../constants/application_constants'; -import fineUploader from 'fineUploader'; import Router from 'react-router'; @@ -19,6 +19,7 @@ import ReactS3FineUploader from './ascribe_uploader/react_s3_fine_uploader'; import DatePicker from 'react-datepicker/dist/react-datepicker'; + let RegisterPiece = React.createClass( { mixins: [Router.Navigation], @@ -128,58 +129,10 @@ let FileUploader = React.createClass( { url: apiUrls.blob_digitalworks }} handleChange={this.props.handleChange} - autoUpload={true} - debug={false} - objectProperties={{ - acl: 'public-read', - bucket: 'ascribe0' - }} - request={{ - endpoint: 'https://ascribe0.s3.amazonaws.com', - accessKey: 'AKIAIVCZJ33WSCBQ3QDA' - }} - signature={{ - endpoint: AppConstants.serverUrl + 's3/signature/' - }} - uploadSuccess={{ - params: { - isBrowserPreviewCapable: fineUploader.supportedFeatures.imagePreviews - } - }} - cors={{ - expected: true - }} - chunking={{ - enabled: true - }} - resume={{ - enabled: true - }} - retry={{ - enableAuto: false - }} - deleteFile={{ - enabled: true, - method: 'DELETE', - endpoint: AppConstants.serverUrl + 's3/delete' - }} validation={{ itemLimit: 100000, sizeLimit: '25000000000' - }} - session={{ - endpoint: null - }} - messages={{ - unsupportedBrowser: '

Upload is not functional in IE7 as IE7 has no support for CORS!

' - }} - formatFileName={(name) => {// fix maybe - if (name !== undefined && name.length > 26) { - name = name.slice(0, 15) + '...' + name.slice(-15); - } - return name; - }} - multiple={true}/> + }}/> ); } }); From 66fff1c50a8ca918beb46b8a28739a6ecb7f2465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 29 Jun 2015 10:01:54 +0200 Subject: [PATCH 16/53] implement cancel functionality --- .../ascribe_uploader/file_drag_and_drop.js | 11 ++++- .../file_drag_and_drop_preview.js | 9 +++- .../file_drag_and_drop_preview_image.js | 8 +++- .../file_drag_and_drop_preview_iterator.js | 6 ++- .../file_drag_and_drop_preview_other.js | 9 +++- .../react_s3_fine_uploader.js | 46 ++++++++++++------- js/components/register_piece.js | 14 +++++- 7 files changed, 76 insertions(+), 27 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index e34270ed..f08289f6 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -17,6 +17,7 @@ var FileDragAndDrop = React.createClass({ onDragEnd: React.PropTypes.func, filesToUpload: React.PropTypes.array, handleDeleteFile: React.PropTypes.func, + handleCancelFile: React.PropTypes.func, multiple: React.PropTypes.bool, dropzoneInactive: React.PropTypes.bool }, @@ -85,6 +86,13 @@ var FileDragAndDrop = React.createClass({ this.props.handleDeleteFile(fileId); }, + handleCancelFile(fileId) { + // input's value is not change the second time someone + // inputs the same file again, therefore we need to reset its value + this.refs.fileinput.getDOMNode().value = ''; + this.props.handleCancelFile(fileId); + }, + handleOnClick() { // when multiple is set to false and the user already uploaded a piece, // do not propagate event @@ -117,7 +125,8 @@ var FileDragAndDrop = React.createClass({ {hasFiles ? null : this.props.multiple ? Click or drag to add files : Click or drag to add a file} + handleDeleteFile={this.handleDeleteFile} + handleCancelFile={this.handleCancelFile}/> - {this.props.progress === 100 ? : null} +
); } diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js index 82635ee4..b9b70039 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js @@ -7,7 +7,8 @@ import FileDragAndDropPreview from './file_drag_and_drop_preview'; let FileDragAndDropPreviewIterator = React.createClass({ propTypes: { files: React.PropTypes.array, - handleDeleteFile: React.PropTypes.func + handleDeleteFile: React.PropTypes.func, + handleCancelFile: React.PropTypes.func }, render() { @@ -19,7 +20,8 @@ let FileDragAndDropPreviewIterator = React.createClass({ + handleDeleteFile={this.props.handleDeleteFile} + handleCancelFile={this.props.handleCancelFile}/> ); })}
diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js index 36c1ccdd..52a3e281 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_other.js @@ -13,12 +13,17 @@ let FileDragAndDropPreviewOther = React.createClass({ render() { return (
- {this.props.progress === 100 ? : null} + {'.' + this.props.type}
diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 985e5d90..584cbd5b 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -59,7 +59,8 @@ var ReactS3FineUploader = React.createClass({ deleteFile: React.PropTypes.shape({ enabled: React.PropTypes.bool, method: React.PropTypes.string, - endpoint: React.PropTypes.string + endpoint: React.PropTypes.string, + customHeaders: React.PropTypes.object }), session: React.PropTypes.shape({ endpoint: React.PropTypes.bool @@ -79,6 +80,7 @@ var ReactS3FineUploader = React.createClass({ }, getInitialState() { + console.log(this.props); return { filesToUpload: [], uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig()) @@ -109,6 +111,7 @@ var ReactS3FineUploader = React.createClass({ callbacks: { onSubmit: this.onSubmit, onComplete: this.onComplete, + onCancel: this.onCancel, onDelete: this.onDelete, onSessionRequestComplete: this.onSessionRequestComplete, onProgress: this.onProgress, @@ -219,9 +222,11 @@ var ReactS3FineUploader = React.createClass({ console.log('delete'); }, - onCancel() { - console.log('cancel'); - // handle file removal here, for this.state.filesToUpload (same as in onDeleteComplete) + onCancel(id) { + this.removeFileWithIdFromFilesToUpload(id); + + let notification = new GlobalNotificationModel('File upload canceled', 'success', 10000); + GlobalNotificationActions.appendGlobalNotification(notification); }, onSessionRequestComplete() { @@ -233,17 +238,7 @@ var ReactS3FineUploader = React.createClass({ let notification = new GlobalNotificationModel('Couldn\'t delete file', 'danger', 10000); GlobalNotificationActions.appendGlobalNotification(notification); } else { - // also, sync files from state with the ones from fineuploader - let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload)); - - // splice because I can - filesToUpload.splice(id, 1); - - // set state - let newState = React.addons.update(this.state, { - filesToUpload: { $set: filesToUpload } - }); - this.setState(newState); + this.removeFileWithIdFromFilesToUpload(id); let notification = new GlobalNotificationModel('File deleted', 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); @@ -267,6 +262,10 @@ var ReactS3FineUploader = React.createClass({ // promise }, + handleCancelFile(fileId) { + this.state.uploader.cancel(fileId); + }, + handleUploadFile(files) { // If multiple set and user already uploaded its work, @@ -320,13 +319,28 @@ var ReactS3FineUploader = React.createClass({ this.setState(newState); }, + removeFileWithIdFromFilesToUpload(fileId) { + // also, sync files from state with the ones from fineuploader + let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload)); + + // splice because I can + filesToUpload.splice(fileId, 1); + + // set state + let newState = React.addons.update(this.state, { + filesToUpload: { $set: filesToUpload } + }); + this.setState(newState); + }, + render() { return ( 0} /> ); } diff --git a/js/components/register_piece.js b/js/components/register_piece.js index 5ed7b0ba..e878d1f6 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -115,7 +115,14 @@ let RegisterPiece = React.createClass( { }); -let FileUploader = React.createClass( { +let FileUploader = React.createClass({ + getCookie(name) { + let value = '; ' + document.cookie; + let parts = value.split('; ' + name + '='); + if (parts.length === 2) { + return parts.pop().split(';').shift(); + } + }, render() { return ( Date: Mon, 29 Jun 2015 11:44:16 +0200 Subject: [PATCH 17/53] refactor form validation --- .../ascribe_uploader/file_drag_and_drop.js | 2 +- .../react_s3_fine_uploader.js | 34 +++++++-- js/components/register_piece.js | 69 +++++++++++++------ 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index 6e8843be..4ef77872 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -60,7 +60,6 @@ var FileDragAndDrop = React.createClass({ } }, - handleDrop(event) { event.preventDefault(); event.stopPropagation(); @@ -107,6 +106,7 @@ var FileDragAndDrop = React.createClass({ }, render: function () { + // has files only is true if there are files that do not have the status deleted or canceled let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0; let className = hasFiles ? 'file-drag-and-drop has-files ' : 'file-drag-and-drop '; className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 7afd4981..86dd8d98 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -27,7 +27,7 @@ var ReactS3FineUploader = React.createClass({ createBlobRoutine: React.PropTypes.shape({ url: React.PropTypes.string }), - handleChange: React.PropTypes.func, + submitKey: React.PropTypes.func, autoUpload: React.PropTypes.bool, debug: React.PropTypes.bool, objectProperties: React.PropTypes.shape({ @@ -80,7 +80,9 @@ var ReactS3FineUploader = React.createClass({ multiple: React.PropTypes.bool, retry: React.PropTypes.shape({ enableAuto: React.PropTypes.bool - }) + }), + setUploadStatus: React.PropTypes.func, + isReadyForFormSubmission: React.PropTypes.func }, getDefaultProps() { @@ -104,7 +106,7 @@ var ReactS3FineUploader = React.createClass({ endpoint: AppConstants.serverUrl + 's3/signature/', customHeaders: { 'X-CSRFToken': getCookie('csrftoken') - }, + } }, deleteFile: { enabled: true, @@ -239,8 +241,16 @@ var ReactS3FineUploader = React.createClass({ }); this.setState(newState); this.createBlob(files[id]); - this.props.handleChange(); - console.log('completed ' + files[id].name); + this.props.submitKey(files[id].key); + + // also, lets check if after the completion of this upload, + // the form is ready for submission or not + if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) { + // if so, set uploadstatus to true + this.props.setUploadStatus(true); + } else { + this.props.setUploadStatus(false); + } }, createBlob(file) { @@ -291,6 +301,13 @@ var ReactS3FineUploader = React.createClass({ let notification = new GlobalNotificationModel('File upload canceled', 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); + + if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) { + // if so, set uploadstatus to true + this.props.setUploadStatus(true); + } else { + this.props.setUploadStatus(false); + } }, onSessionRequestComplete() { @@ -307,6 +324,13 @@ var ReactS3FineUploader = React.createClass({ let notification = new GlobalNotificationModel('File deleted', 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); } + + if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) { + // if so, set uploadstatus to true + this.props.setUploadStatus(true); + } else { + this.props.setUploadStatus(false); + } }, onProgress(id, name, uploadedBytes, totalBytes) { diff --git a/js/components/register_piece.js b/js/components/register_piece.js index c234ff92..51e89565 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -24,8 +24,12 @@ let RegisterPiece = React.createClass( { mixins: [Router.Navigation], getInitialState(){ - return {digital_work_key: null}; + return { + digitalWorkKey: null, + uploadStatus: false + }; }, + handleSuccess(){ let notification = new GlobalNotificationModel('Login successsful', 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); @@ -37,30 +41,46 @@ let RegisterPiece = React.createClass( { for (let ref in this.refs.form.refs){ data[this.refs.form.refs[ref].props.name] = this.refs.form.refs[ref].state.value; } - data.digital_work_key = this.state.digital_work_key; + data.digital_work_key = this.state.digitalWorkKey; return data; }, - handleChange(){ - this.setState({digital_work_key: this.refs.uploader.refs.fineuploader.state.filesToUpload[0].key}); + + submitKey(key){ + this.setState({ + digitalWorkKey: key + }); + }, + + setUploadStatus(isReady) { + console.log(isReady); + this.setState({ + uploadStatus: isReady + }); + }, + + isReadyForFormSubmission(files) { + files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled'); + console.log(files); + if(files.length > 0 && files[0].status === 'upload successful') { + return true; + } else { + return false; + } }, render() { let buttons = null; - if (this.refs.uploader && this.refs.uploader.refs.fineuploader.state.filesToUpload[0].status === 'upload successful'){ + + if (this.state.uploadStatus){ buttons = ( ); } + return (
-
- -
-
-
+

Lock down title

}> + + + @@ -117,17 +144,15 @@ let RegisterPiece = React.createClass( { let FileUploader = React.createClass({ - getCookie(name) { - let value = '; ' + document.cookie; - let parts = value.split('; ' + name + '='); - if (parts.length === 2) { - return parts.pop().split(';').shift(); - } + propTypes: { + setUploadStatus: React.PropTypes.func, + submitKey: React.PropTypes.func, + isReadyForFormSubmission: React.PropTypes.func }, + render() { return ( + }} + setUploadStatus={this.props.setUploadStatus} + isReadyForFormSubmission={this.props.isReadyForFormSubmission}/> ); } }); From 867ebec13491cb4ff5e30aae01026f259cd04535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 29 Jun 2015 13:41:37 +0200 Subject: [PATCH 18/53] fix styling for fineuploader --- .../ascribe_uploader/file_drag_and_drop.js | 4 +++- sass/ascribe_settings.scss | 11 +++++++--- sass/ascribe_uploader.scss | 21 +++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index 4ef77872..ee3f524b 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -7,6 +7,7 @@ import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterato // Taken from: https://github.com/fedosejev/react-file-drag-and-drop var FileDragAndDrop = React.createClass({ propTypes: { + className: React.PropTypes.string, onDragStart: React.PropTypes.func, onDrop: React.PropTypes.func.isRequired, onDrag: React.PropTypes.func, @@ -110,6 +111,7 @@ var FileDragAndDrop = React.createClass({ let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0; let className = hasFiles ? 'file-drag-and-drop has-files ' : 'file-drag-and-drop '; className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; + className += ' ' + this.props.className; return (
- {hasFiles ? null : this.props.multiple ? Click or drag to add files : Click or drag to add a file} + {hasFiles ? null : this.props.multiple ? Click or drag to add files : Click or drag to add a file} span { - font-size: 1.5em; +.file-drag-and-drop .file-drag-and-drop-dialog { + font-size: 1.25em !important; + + &:before { + content: ' '; + display: inline-block; + vertical-align: middle; /* vertical alignment of the inline element */ + height: 100%; + } } .has-files { @@ -61,7 +70,7 @@ .file-drag-and-drop-preview-image .delete-file { font-size: 3em; - margin-top: .5em; + margin-top: .3em; color: white; text-shadow: -2px 0 black, 0 2px black, 2px 0 black, 0 -2px black; cursor: pointer; From dcb3e00e3af7d10e6c81e88c60efacdcb8a9761f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 29 Jun 2015 14:36:55 +0200 Subject: [PATCH 19/53] styling for mobile --- .../ascribe_uploader/file_drag_and_drop.js | 4 +-- .../file_drag_and_drop_preview_image.js | 24 +++++++++++---- .../react_s3_fine_uploader.js | 3 +- sass/ascribe_settings.scss | 9 +++--- sass/ascribe_uploader.scss | 30 ++++++++++--------- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index ee3f524b..7db49192 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -109,9 +109,9 @@ var FileDragAndDrop = React.createClass({ render: function () { // has files only is true if there are files that do not have the status deleted or canceled let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0; - let className = hasFiles ? 'file-drag-and-drop has-files ' : 'file-drag-and-drop '; + let className = hasFiles ? 'has-files ' : ''; className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; - className += ' ' + this.props.className; + className += this.props.className ? ' ' + this.props.className : ''; return (
:
diff --git a/js/components/edition.js b/js/components/edition.js index 3d2193ae..073bb952 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -253,7 +253,7 @@ let EditionSummary = React.createClass({ @@ -577,14 +577,14 @@ let CoaDetails = React.createClass({ if (this.state.coa.url_safe) { return (
-

- + - +

diff --git a/sass/main.scss b/sass/main.scss index 4b7230bb..681e5149 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -242,3 +242,7 @@ body { .col-bottom { vertical-align: bottom; } + +.ascribe-button-list button { + margin-right: 1em; +} \ No newline at end of file From c6528d8999a52af7f71f3f08fd25847e9d9bb66b Mon Sep 17 00:00:00 2001 From: diminator Date: Tue, 30 Jun 2015 17:12:51 +0200 Subject: [PATCH 44/53] other_data : delete without cascade and show acl --- .../react_s3_fine_uploader.js | 8 +- js/components/edition.js | 83 ++++++++++--------- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index 8e720f02..6514a588 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -345,10 +345,10 @@ var ReactS3FineUploader = React.createClass({ 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)', 'danger', 10000); - GlobalNotificationActions.appendGlobalNotification(notification); - - throw new Error('The session request failed', response); + //let notification = new GlobalNotificationModel('Could not load attached files (Further data)', 'danger', 10000); + //GlobalNotificationActions.appendGlobalNotification(notification); + // + //throw new Error('The session request failed', response); } }, diff --git a/js/components/edition.js b/js/components/edition.js index 6bd096a4..ba6014ce 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -120,7 +120,9 @@ let Edition = React.createClass({ -1 || Object.keys(this.props.edition.extra_data).length > 0}> + show={this.props.edition.acl.indexOf('edit') > -1 + || Object.keys(this.props.edition.extra_data).length > 0 + || this.props.edition.other_data !== null}> @@ -493,16 +495,12 @@ let EditionFurtherDetails = React.createClass({ handleSuccess={this.showNotification} editable={editable} edition={this.props.edition} /> - - - - + ); @@ -518,34 +516,43 @@ let FileUploader = React.createClass({ }, render() { + if (!this.props.editable && this.props.edition.other_data === null){ + return null; + } return ( - +
+ + + +
+
); } }); From 929c694ad739f16717503ca6c088b6185bffbf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 30 Jun 2015 17:57:20 +0200 Subject: [PATCH 45/53] finalized delete functionality --- .../ascribe_uploader/file_drag_and_drop.js | 2 +- .../file_drag_and_drop_preview_iterator.js | 2 +- .../ascribe_uploader/react_s3_fine_uploader.js | 7 +++++-- js/utils/requests.js | 14 +++++++++++++- sass/main.scss | 2 +- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop.js b/js/components/ascribe_uploader/file_drag_and_drop.js index 5879a2ef..28c87677 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop.js +++ b/js/components/ascribe_uploader/file_drag_and_drop.js @@ -133,7 +133,7 @@ let FileDragAndDrop = React.createClass({ render: function () { // has files only is true if there are files that do not have the status deleted or canceled - let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0; + let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; let className = hasFiles ? 'has-files ' : ''; className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; className += this.props.className ? ' ' + this.props.className : ''; diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js index 8d8171ff..896d9f1f 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_iterator.js @@ -19,7 +19,7 @@ let FileDragAndDropPreviewIterator = React.createClass({ return (
{this.props.files.map((file, i) => { - if(file.status !== 'deleted' && file.status !== 'canceled') { + if(file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1) { return ( this.onDeleteComplete(fileToDelete.id, null, false)) @@ -417,7 +420,7 @@ var ReactS3FineUploader = React.createClass({ }, handleUploadFile(files) { - + console.log(this.state.files); // If multiple set and user already uploaded its work, // cancel upload if(!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0) { @@ -512,7 +515,7 @@ var ReactS3FineUploader = React.createClass({ handleResumeFile={this.handleResumeFile} 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' && file.size !== -1).length > 0} />
); } diff --git a/js/utils/requests.js b/js/utils/requests.js index e10a8a99..7e42dfee 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -27,6 +27,17 @@ class Requests { return response.text(); } + customJSONparse(responseText) { + // If the responses' body does not contain any data, + // fetch will resolve responseText to the string 'None'. + // If this is the case, we can not try to parse it as JSON. + if(responseText !== 'None') { + return JSON.parse(responseText); + } else { + return {}; + } + } + handleFatalError(err) { this.fatalErrorHandler(err); throw new ServerError(err); @@ -36,6 +47,7 @@ class Requests { if (!json.success) { let error = new APIError(); error.json = json; + console.error(new Error('The \'success\' property is missing in the server\'s response.')); throw error; } return json; @@ -83,7 +95,7 @@ class Requests { merged.method = verb; return fetch(url, merged) .then(this.unpackResponse) - .then(JSON.parse) + .then(this.customJSONparse) .catch(this.handleFatalError.bind(this)) .then(this.handleAPIError); } diff --git a/sass/main.scss b/sass/main.scss index 681e5149..d8d5345e 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -244,5 +244,5 @@ body { } .ascribe-button-list button { - margin-right: 1em; + margin-right: 1px; } \ No newline at end of file From 1db9921dd84af069313dcebfa23036a05ef44e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 1 Jul 2015 10:00:53 +0200 Subject: [PATCH 46/53] use safe urls for thumbnails and links --- .../ascribe_uploader/file_drag_and_drop_preview.js | 4 ++-- .../file_drag_and_drop_preview_image.js | 4 ++-- .../file_drag_and_drop_preview_other.js | 4 ++-- .../ascribe_uploader/react_s3_fine_uploader.js | 10 +++++++--- sass/ascribe_uploader.scss | 8 ++++++++ 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview.js b/js/components/ascribe_uploader/file_drag_and_drop_preview.js index 84853018..c58f6a38 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview.js @@ -57,7 +57,7 @@ let FileDragAndDropPreview = React.createClass({ url={this.props.file.url} toggleUploadProcess={this.toggleUploadProcess} areAssetsDownloadable={this.props.areAssetsDownloadable} - downloadFile={this.handleDownloadFile}/>); + downloadUrl={this.props.file.s3UrlSafe}/>); } else { previewElement = (); + downloadUrl={this.props.file.s3UrlSafe}/>); } return ( diff --git a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js index 1cbf67e6..04d35a47 100644 --- a/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js +++ b/js/components/ascribe_uploader/file_drag_and_drop_preview_image.js @@ -10,7 +10,7 @@ let FileDragAndDropPreviewImage = React.createClass({ progress: React.PropTypes.number, url: React.PropTypes.string, toggleUploadProcess: React.PropTypes.func, - downloadFile: React.PropTypes.func, + downloadUrl: React.PropTypes.string, areAssetsDownloadable: React.PropTypes.bool }, @@ -48,7 +48,7 @@ let FileDragAndDropPreviewImage = React.createClass({ // 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 =