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{