From eaed3011557bb2a3d2ef8305aaec7b57d6a6dce0 Mon Sep 17 00:00:00 2001 From: diminator Date: Tue, 8 Dec 2015 11:46:30 +0100 Subject: [PATCH 01/14] coa flush causes dispatcherror --- js/actions/coa_actions.js | 16 ++++++++-------- js/components/ascribe_detail/edition.js | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/js/actions/coa_actions.js b/js/actions/coa_actions.js index d3d13290..a7f36305 100644 --- a/js/actions/coa_actions.js +++ b/js/actions/coa_actions.js @@ -14,27 +14,27 @@ class CoaActions { } fetchOrCreate(id, bitcoinId) { - return Q.Promise((resolve, reject) => { + //return Q.Promise((resolve, reject) => { CoaFetcher.fetchOne(id) .then((res) => { if (res.coa) { this.actions.updateCoa(res.coa); - resolve(res.coa); + //resolve(res.coa); } else { - this.actions.create(bitcoinId); + this.actions.create(bitcoinId).defer(); } }) .catch((err) => { console.logGlobal(err); this.actions.updateCoa(null); - reject(err); + //reject(err); }); - }); + //}); } create(bitcoinId) { - return Q.Promise((resolve, reject) => { + //return Q.Promise((resolve, reject) => { CoaFetcher.create(bitcoinId) .then((res) => { this.actions.updateCoa(res.coa); @@ -42,9 +42,9 @@ class CoaActions { .catch((err) => { console.logGlobal(err); this.actions.updateCoa(null); - reject(err); + //reject(err); }); - }); + //}); } } diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index bc2f0cfa..3c88e022 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -256,6 +256,7 @@ let CoaDetails = React.createClass({ componentDidMount() { let { edition } = this.props; CoaStore.listen(this.onChange); + CoaActions.updateCoa({}); if(edition.coa) { CoaActions.fetchOrCreate(edition.coa, edition.bitcoin_id); } @@ -300,7 +301,7 @@ let CoaDetails = React.createClass({ } return (
- +
); } From bf6b78c058a81b873f358e4e4292c491bc1d9bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 8 Dec 2015 11:49:03 +0100 Subject: [PATCH 02/14] Revert "coa flush causes dispatcherror" This reverts commit eaed3011557bb2a3d2ef8305aaec7b57d6a6dce0. --- js/actions/coa_actions.js | 16 ++++++++-------- js/components/ascribe_detail/edition.js | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/js/actions/coa_actions.js b/js/actions/coa_actions.js index a7f36305..d3d13290 100644 --- a/js/actions/coa_actions.js +++ b/js/actions/coa_actions.js @@ -14,27 +14,27 @@ class CoaActions { } fetchOrCreate(id, bitcoinId) { - //return Q.Promise((resolve, reject) => { + return Q.Promise((resolve, reject) => { CoaFetcher.fetchOne(id) .then((res) => { if (res.coa) { this.actions.updateCoa(res.coa); - //resolve(res.coa); + resolve(res.coa); } else { - this.actions.create(bitcoinId).defer(); + this.actions.create(bitcoinId); } }) .catch((err) => { console.logGlobal(err); this.actions.updateCoa(null); - //reject(err); + reject(err); }); - //}); + }); } create(bitcoinId) { - //return Q.Promise((resolve, reject) => { + return Q.Promise((resolve, reject) => { CoaFetcher.create(bitcoinId) .then((res) => { this.actions.updateCoa(res.coa); @@ -42,9 +42,9 @@ class CoaActions { .catch((err) => { console.logGlobal(err); this.actions.updateCoa(null); - //reject(err); + reject(err); }); - //}); + }); } } diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index 3c88e022..bc2f0cfa 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -256,7 +256,6 @@ let CoaDetails = React.createClass({ componentDidMount() { let { edition } = this.props; CoaStore.listen(this.onChange); - CoaActions.updateCoa({}); if(edition.coa) { CoaActions.fetchOrCreate(edition.coa, edition.bitcoin_id); } @@ -301,7 +300,7 @@ let CoaDetails = React.createClass({ } return (
- +
); } From da75353b00120f5293b89a1b9a7a346ba3f93c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 8 Dec 2015 13:28:47 +0100 Subject: [PATCH 03/14] Refactor Edition endpoints to create Coa on demand properly --- js/actions/coa_actions.js | 51 ------------- js/actions/edition_actions.js | 19 ++--- js/components/ascribe_detail/edition.js | 75 +++---------------- .../ascribe_detail/edition_container.js | 47 ++++++------ js/sources/edition_source.js | 36 +++++++++ js/stores/coa_store.js | 22 ------ js/stores/edition_store.js | 46 ++++++++++-- 7 files changed, 118 insertions(+), 178 deletions(-) delete mode 100644 js/actions/coa_actions.js create mode 100644 js/sources/edition_source.js delete mode 100644 js/stores/coa_store.js diff --git a/js/actions/coa_actions.js b/js/actions/coa_actions.js deleted file mode 100644 index d3d13290..00000000 --- a/js/actions/coa_actions.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -import { alt } from '../alt'; -import CoaFetcher from '../fetchers/coa_fetcher'; - -import Q from 'q'; - -class CoaActions { - constructor() { - this.generateActions( - 'updateCoa', - 'flushCoa' - ); - } - - fetchOrCreate(id, bitcoinId) { - return Q.Promise((resolve, reject) => { - CoaFetcher.fetchOne(id) - .then((res) => { - if (res.coa) { - this.actions.updateCoa(res.coa); - resolve(res.coa); - } - else { - this.actions.create(bitcoinId); - } - }) - .catch((err) => { - console.logGlobal(err); - this.actions.updateCoa(null); - reject(err); - }); - }); - } - - create(bitcoinId) { - return Q.Promise((resolve, reject) => { - CoaFetcher.create(bitcoinId) - .then((res) => { - this.actions.updateCoa(res.coa); - }) - .catch((err) => { - console.logGlobal(err); - this.actions.updateCoa(null); - reject(err); - }); - }); - } -} - -export default alt.createActions(CoaActions); diff --git a/js/actions/edition_actions.js b/js/actions/edition_actions.js index 3f659524..9563ed92 100644 --- a/js/actions/edition_actions.js +++ b/js/actions/edition_actions.js @@ -1,27 +1,18 @@ 'use strict'; import { alt } from '../alt'; -import EditionFetcher from '../fetchers/edition_fetcher'; class EditionActions { constructor() { this.generateActions( - 'updateEdition', - 'editionFailed' + 'fetchEdition', + 'successFetchEdition', + 'successFetchCoa', + 'errorCoa', + 'errorEdition' ); } - - fetchOne(editionId) { - EditionFetcher.fetchOne(editionId) - .then((res) => { - this.actions.updateEdition(res.edition); - }) - .catch((err) => { - console.logGlobal(err); - this.actions.editionFailed(err.json); - }); - } } export default alt.createActions(EditionActions); diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index bc2f0cfa..567c7d7f 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -8,11 +8,6 @@ import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; 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 HistoryIterator from './history_iterator'; import MediaContainer from './media_container'; @@ -44,6 +39,7 @@ let Edition = React.createClass({ actionPanelButtonListType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func, edition: React.PropTypes.object, + currentUser: React.PropTypes.object, loadEdition: React.PropTypes.func }, @@ -55,32 +51,6 @@ let Edition = React.createClass({ }; }, - getInitialState() { - return UserStore.getState(); - }, - - componentDidMount() { - UserStore.listen(this.onChange); - UserActions.fetchCurrentUser(); - }, - - componentWillUnmount() { - // Flushing the coa state is essential to not displaying the same - // data to the user while he's on another edition - // - // BUGFIX: Previously we had this line in the componentWillUnmount of - // CoaDetails, but since we're reloading the edition after performing an ACL action - // on it, this resulted in multiple events occupying the dispatcher, which eventually - // resulted in crashing the app. - CoaActions.flushCoa(); - - UserStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - render() { let FurtherDetailsType = this.props.furtherDetailsType; @@ -101,13 +71,13 @@ let Edition = React.createClass({ + coa={this.props.edition.coa}/> + currentUser={this.props.currentUser}/> {return {'bitcoin_id': this.props.edition.bitcoin_id}; }} label={getLangText('Personal note (public)')} @@ -154,7 +124,7 @@ let Edition = React.createClass({ show={!!this.props.edition.public_note || !!this.props.edition.acl.acl_edit} successMessage={getLangText('Public edition note saved')} url={ApiUrls.note_public_edition} - currentUser={this.state.currentUser}/> + currentUser={this.props.currentUser}/>

- + @@ -291,10 +238,10 @@ let CoaDetails = React.createClass({

); - } else if(typeof this.state.coa === 'string'){ + } else if(typeof this.props.coa === 'string'){ return (
- {this.state.coa} + {this.props.coa}
); } diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 0e74e38d..2a0e7d5e 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -9,12 +9,16 @@ import { ResourceNotFoundError } from '../../models/errors'; import EditionActions from '../../actions/edition_actions'; import EditionStore from '../../stores/edition_store'; +import UserActions from '../../actions/user_actions'; +import UserStore from '../../stores/user_store'; + import Edition from './edition'; import AscribeSpinner from '../ascribe_spinner'; import { getLangText } from '../../utils/lang_utils'; import { setDocumentTitle } from '../../utils/dom_utils'; +import { mergeOptions } from '../../utils/general_utils'; /** @@ -30,33 +34,37 @@ let EditionContainer = React.createClass({ mixins: [History, ReactError], getInitialState() { - return EditionStore.getState(); + return mergeOptions( + EditionStore.getState(), + UserStore.getState() + ); }, componentDidMount() { EditionStore.listen(this.onChange); + UserStore.listen(this.onChange); // Every time we're entering the edition detail page, // just reset the edition that is saved in the edition store // as it will otherwise display wrong/old data once the user loads // the edition detail a second time - EditionActions.updateEdition({}); - this.loadEdition(); + EditionActions.fetchEdition(this.props.params.editionId); + + UserActions.fetchCurrentUser(); }, // This is done to update the container when the user clicks on the prev or next // button to update the URL parameter (and therefore to switch pieces) componentWillReceiveProps(nextProps) { if(this.props.params.editionId !== nextProps.params.editionId) { - EditionActions.updateEdition({}); - EditionActions.fetchOne(nextProps.params.editionId); + EditionActions.fetchEdition(this.props.params.editionId); } }, componentDidUpdate() { - const { editionError } = this.state; + const { editionMeta } = this.state; - if(editionError && editionError.status === 404) { + if(editionMeta.err && editionMeta.err.status === 404) { this.throws(new ResourceNotFoundError(getLangText("Oops, the edition you're looking for doesn't exist."))); } }, @@ -68,30 +76,27 @@ let EditionContainer = React.createClass({ onChange(state) { this.setState(state); - if (!state.edition.digital_work) { - return; - } - let isEncoding = state.edition.digital_work.isEncoding; - if (state.edition.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { - let timerId = window.setInterval(() => EditionActions.fetchOne(this.props.params.editionId), 10000); - this.setState({timerId: timerId}); - } - }, - loadEdition() { - EditionActions.fetchOne(this.props.params.editionId); + if(state && state.edition && state.edition.digital_work) { + let isEncoding = state.edition.digital_work.isEncoding; + if (state.edition.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { + let timerId = window.setInterval(() => EditionActions.fetchOne(this.props.params.editionId), 10000); + this.setState({timerId: timerId}); + } + } }, render() { - if(this.state.edition && this.state.edition.id) { + if (this.state.edition && this.state.edition.id && + this.state.currentUser && this.state.currentUser.email) { setDocumentTitle([this.state.edition.artist_name, this.state.edition.title].join(', ')); - return ( + currentUser={this.state.currentUser} + loadEdition={() => EditionActions.fetchEdition(this.props.params.editionId)} /> ); } else { return ( diff --git a/js/sources/edition_source.js b/js/sources/edition_source.js new file mode 100644 index 00000000..157b0087 --- /dev/null +++ b/js/sources/edition_source.js @@ -0,0 +1,36 @@ +'use strict'; + +import requests from '../utils/requests'; + +import EditionActions from '../actions/edition_actions'; + + +const EditionSource = { + lookupEdition: { + remote(state) { + return requests.get('edition', { bitcoin_id: state.editionMeta.idToFetch }); + }, + + success: EditionActions.successFetchEdition, + error: EditionActions.errorEdition + }, + + lookupCoa: { + remote(state) { + return requests.get('coa', { id: state.edition.coa }); + }, + + success: EditionActions.successFetchCoa, + error: EditionActions.errorCoa + }, + + performCreateCoa: { + remote(state) { + return requests.post('coa_create', {body: { bitcoin_id: state.edition.bitcoin_id }}); + }, + success: EditionActions.successFetchCoa, + error: EditionActions.errorCoa + } +}; + +export default EditionSource; \ No newline at end of file diff --git a/js/stores/coa_store.js b/js/stores/coa_store.js deleted file mode 100644 index e76e480e..00000000 --- a/js/stores/coa_store.js +++ /dev/null @@ -1,22 +0,0 @@ -'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; - } - - onFlushCoa() { - this.coa = {}; - } -} - -export default alt.createStore(CoaStore, 'CoaStore'); diff --git a/js/stores/edition_store.js b/js/stores/edition_store.js index 22e78d23..212dc25c 100644 --- a/js/stores/edition_store.js +++ b/js/stores/edition_store.js @@ -1,23 +1,57 @@ 'use strict'; import { alt } from '../alt'; + import EditionActions from '../actions/edition_actions'; +import EditionSource from '../sources/edition_source'; class EditionStore { constructor() { - this.edition = {}; - this.editionError = null; + this.edition = null; + this.editionMeta = { + err: null, + idToFetch: null + }; + this.coaMeta = { + err: null + }; + this.bindActions(EditionActions); + this.registerAsync(EditionSource); } - onUpdateEdition(edition) { + onFetchEdition(idToFetch) { + this.editionMeta.idToFetch = idToFetch; + + if(!this.getInstance().isLoading()) { + this.getInstance().lookupEdition(); + } + } + + onSuccessFetchEdition({ edition }) { + this.editionMeta.err = null; + this.editionMeta.idToFetch = null; this.edition = edition; - this.editionError = null; + + if(this.edition && this.edition.coa && typeof this.edition.coa.constructor !== Object) { + this.getInstance().lookupCoa(); + } else if(this.edition && !this.edition.coa) { + this.getInstance().performCreateCoa(); + } } - onEditionFailed(error) { - this.editionError = error; + onSuccessFetchCoa({ coa }) { + this.coaMeta.err = null; + this.edition.coa = coa; + } + + onEditionError(err) { + this.editionMeta.err = err; + } + + onCoaError(err) { + this.coaMeta.err = err; } } From 2cc02d9599f28c6968507079ff383c18714f9a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 8 Dec 2015 14:18:31 +0100 Subject: [PATCH 04/14] Increase robustness of coa fetching routine --- js/actions/edition_actions.js | 1 + js/components/ascribe_detail/edition.js | 25 +++++++++++++++++-- .../ascribe_detail/edition_container.js | 2 ++ js/fetchers/coa_fetcher.js | 18 ------------- js/fetchers/edition_fetcher.js | 15 ----------- js/stores/edition_store.js | 17 ++++++++++--- js/utils/requests.js | 9 ++++++- 7 files changed, 48 insertions(+), 39 deletions(-) delete mode 100644 js/fetchers/coa_fetcher.js delete mode 100644 js/fetchers/edition_fetcher.js diff --git a/js/actions/edition_actions.js b/js/actions/edition_actions.js index 9563ed92..1727deff 100644 --- a/js/actions/edition_actions.js +++ b/js/actions/edition_actions.js @@ -9,6 +9,7 @@ class EditionActions { 'fetchEdition', 'successFetchEdition', 'successFetchCoa', + 'flushEdition', 'errorCoa', 'errorEdition' ); diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index 567c7d7f..d12d2a41 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -39,6 +39,7 @@ let Edition = React.createClass({ actionPanelButtonListType: React.PropTypes.func, furtherDetailsType: React.PropTypes.func, edition: React.PropTypes.object, + coaError: React.PropTypes.object, currentUser: React.PropTypes.object, loadEdition: React.PropTypes.func }, @@ -77,7 +78,9 @@ let Edition = React.createClass({ title={getLangText('Certificate of Authenticity')} show={this.props.edition.acl.acl_coa === true}> + coa={this.props.edition.coa} + coaError={this.props.coaError} + editionId={this.props.edition.bitcoin_id}/>
+

{getLangText('There was an error generating your Certificate of Authenticity.')}

+

+ {getLangText('Try to refresh the page. If this happens repeatedly, please ')} + {getLangText('contact us')}. +

+ + ); + } if(this.props.coa && this.props.coa.url_safe) { return (
diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 2a0e7d5e..add06ccd 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -48,6 +48,7 @@ let EditionContainer = React.createClass({ // just reset the edition that is saved in the edition store // as it will otherwise display wrong/old data once the user loads // the edition detail a second time + EditionActions.flushEdition(); EditionActions.fetchEdition(this.props.params.editionId); UserActions.fetchCurrentUser(); @@ -95,6 +96,7 @@ let EditionContainer = React.createClass({ actionPanelButtonListType={this.props.actionPanelButtonListType} furtherDetailsType={this.props.furtherDetailsType} edition={this.state.edition} + coaError={this.state.coaMeta.err} currentUser={this.state.currentUser} loadEdition={() => EditionActions.fetchEdition(this.props.params.editionId)} /> ); diff --git a/js/fetchers/coa_fetcher.js b/js/fetchers/coa_fetcher.js deleted file mode 100644 index e4956c66..00000000 --- a/js/fetchers/coa_fetcher.js +++ /dev/null @@ -1,18 +0,0 @@ -'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) { - return requests.post('coa_create', {body: {'bitcoin_id': bitcoinId}}); - } -}; - -export default CoaFetcher; diff --git a/js/fetchers/edition_fetcher.js b/js/fetchers/edition_fetcher.js deleted file mode 100644 index 2d289a3e..00000000 --- a/js/fetchers/edition_fetcher.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -import requests from '../utils/requests'; - -let EditionFetcher = { - /** - * Fetch one user from the API. - * If no arg is supplied, load the current user - */ - fetchOne(editionId) { - return requests.get('edition', {'bitcoin_id': editionId}); - } -}; - -export default EditionFetcher; diff --git a/js/stores/edition_store.js b/js/stores/edition_store.js index 212dc25c..fbe070e1 100644 --- a/js/stores/edition_store.js +++ b/js/stores/edition_store.js @@ -36,7 +36,7 @@ class EditionStore { if(this.edition && this.edition.coa && typeof this.edition.coa.constructor !== Object) { this.getInstance().lookupCoa(); - } else if(this.edition && !this.edition.coa) { + } else if(this.edition && !this.edition.coa && this.edition.acl.acl_coa) { this.getInstance().performCreateCoa(); } } @@ -46,11 +46,22 @@ class EditionStore { this.edition.coa = coa; } - onEditionError(err) { + onFlushEdition() { + this.edition = null; + this.editionMeta = { + err: null, + idToFetch: null + }; + this.coaMeta = { + err: null + }; + } + + onErrorEdition(err) { this.editionMeta.err = err; } - onCoaError(err) { + onErrorCoa(err) { this.coaMeta.err = err; } } diff --git a/js/utils/requests.js b/js/utils/requests.js index 9195661d..d3f5d964 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -12,7 +12,14 @@ import { argsToQueryParams } from '../utils/url_utils'; class Requests { unpackResponse(response) { if (response.status >= 500) { - throw new Error(response.status + ' - ' + response.statusText + ' - on URL:' + response.url); + let err = new Error(response.status + ' - ' + response.statusText + ' - on URL:' + response.url); + response + .text() + .then((resText) => JSON.parse(resText)) + .then((resJSON) => { + err = new Error(resJSON.errors.pop()); + }) + .catch(() => { throw err; }); } return Q.Promise((resolve, reject) => { From e8d5e0390df3c0202ead740fccc47826434673da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 8 Dec 2015 17:38:04 +0100 Subject: [PATCH 05/14] Improve robustness for server-side failing requests for COA --- .../ascribe_detail/edition_container.js | 3 +- js/stores/edition_store.js | 31 ++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index add06ccd..24010d47 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -64,8 +64,7 @@ let EditionContainer = React.createClass({ componentDidUpdate() { const { editionMeta } = this.state; - - if(editionMeta.err && editionMeta.err.status === 404) { + if(editionMeta.err && editionMeta.err.json && editionMeta.err.json.status === 404) { this.throws(new ResourceNotFoundError(getLangText("Oops, the edition you're looking for doesn't exist."))); } }, diff --git a/js/stores/edition_store.js b/js/stores/edition_store.js index fbe070e1..6a129ac1 100644 --- a/js/stores/edition_store.js +++ b/js/stores/edition_store.js @@ -29,21 +29,30 @@ class EditionStore { } } - onSuccessFetchEdition({ edition }) { - this.editionMeta.err = null; - this.editionMeta.idToFetch = null; - this.edition = edition; + onSuccessFetchEdition(res) { + if(res && res.edition) { + this.edition = res.edition; + this.editionMeta.err = null; + this.editionMeta.idToFetch = null; - if(this.edition && this.edition.coa && typeof this.edition.coa.constructor !== Object) { - this.getInstance().lookupCoa(); - } else if(this.edition && !this.edition.coa && this.edition.acl.acl_coa) { - this.getInstance().performCreateCoa(); + if (this.edition.coa && this.edition.acl.acl_coa && + typeof this.edition.coa.constructor !== Object) { + this.getInstance().lookupCoa(); + } else if(!this.edition.coa && this.edition.acl.acl_coa) { + this.getInstance().performCreateCoa(); + } + } else { + this.editionMeta.err = new Error('Problem fetching the edition'); } } - onSuccessFetchCoa({ coa }) { - this.coaMeta.err = null; - this.edition.coa = coa; + onSuccessFetchCoa(res) { + if (res && res.coa && this.edition) { + this.edition.coa = res.coa; + this.coaMeta.err = null; + } else { + this.coaMeta.err = new Error('Problem generating/fetching the COA'); + } } onFlushEdition() { From 4727646654069ada51ea06fe1650037eca1414c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 8 Dec 2015 17:43:19 +0100 Subject: [PATCH 06/14] Add nice message for waiting user --- js/components/ascribe_detail/edition.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index d12d2a41..fbc13a50 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -269,6 +269,8 @@ let CoaDetails = React.createClass({ return (
+

{getLangText("Just a sec, we\'re generating your COA")}

+

{getLangText('(you may leave the page)')}

); } From 8226011e8a85b706e072c94cfa5b60116434bf6a Mon Sep 17 00:00:00 2001 From: diminator Date: Tue, 8 Dec 2015 18:17:08 +0100 Subject: [PATCH 07/14] query_params in coa_verify --- js/components/coa_verify_container.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/js/components/coa_verify_container.js b/js/components/coa_verify_container.js index 3f98fffa..9e6ff4e8 100644 --- a/js/components/coa_verify_container.js +++ b/js/components/coa_verify_container.js @@ -17,9 +17,13 @@ import { setDocumentTitle } from '../utils/dom_utils'; let CoaVerifyContainer = React.createClass({ + + propTypes: { + location: React.PropTypes.object + }, + render() { setDocumentTitle(getLangText('Verify your Certificate of Authenticity')); - return (

@@ -27,7 +31,7 @@ let CoaVerifyContainer = React.createClass({ {getLangText('Verify your Certificate of Authenticity')}
- +

{getLangText('ascribe is using the following public key for verification')}: @@ -47,6 +51,10 @@ let CoaVerifyContainer = React.createClass({ let CoaVerifyForm = React.createClass({ + propTypes: { + location: React.PropTypes.object + }, + handleSuccess(response){ let notification = null; if (response.verdict) { @@ -56,6 +64,7 @@ let CoaVerifyForm = React.createClass({ }, render() { + const {message, signature} = this.props.location.query; return (
@@ -90,6 +100,7 @@ let CoaVerifyForm = React.createClass({
From 7318d4e5dfa31d4a23318c2c2a2d008db52a697b Mon Sep 17 00:00:00 2001 From: diminator Date: Wed, 9 Dec 2015 11:41:46 +0100 Subject: [PATCH 08/14] embed in edition --- js/components/ascribe_detail/edition.js | 15 ++++++++++++--- js/components/ascribe_detail/edition_container.js | 12 ++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index fbc13a50..068b526c 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -244,7 +244,16 @@ let CoaDetails = React.createClass({ if(this.props.coa && this.props.coa.url_safe) { return ( ); } else if(typeof this.props.coa === 'string'){ @@ -268,7 +277,7 @@ let CoaDetails = React.createClass({ } return (
- +

{getLangText("Just a sec, we\'re generating your COA")}

{getLangText('(you may leave the page)')}

diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 24010d47..a1fa7f58 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -87,16 +87,16 @@ let EditionContainer = React.createClass({ }, render() { - if (this.state.edition && this.state.edition.id && - this.state.currentUser && this.state.currentUser.email) { - setDocumentTitle([this.state.edition.artist_name, this.state.edition.title].join(', ')); + const {edition, currentUser, coaMeta} = this.state; + if (edition && edition.id && currentUser && currentUser.email) { + setDocumentTitle([edition.artist_name, edition.title].join(', ')); return ( EditionActions.fetchEdition(this.props.params.editionId)} /> ); } else { From 03481f3b09d5f5ceeda97276a3137b48010a189b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 9 Dec 2015 13:04:15 +0100 Subject: [PATCH 09/14] Fix bug: Old edition HTTP request holds loading data for new edition request --- js/stores/edition_store.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/stores/edition_store.js b/js/stores/edition_store.js index 6a129ac1..53a16cc4 100644 --- a/js/stores/edition_store.js +++ b/js/stores/edition_store.js @@ -24,9 +24,7 @@ class EditionStore { onFetchEdition(idToFetch) { this.editionMeta.idToFetch = idToFetch; - if(!this.getInstance().isLoading()) { - this.getInstance().lookupEdition(); - } + this.getInstance().lookupEdition(); } onSuccessFetchEdition(res) { From 2a251897d516e8cfad1a0f0df6ce3180816d2cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 9 Dec 2015 13:18:07 +0100 Subject: [PATCH 10/14] Split EditionSource up into CoaSource and EditionSource --- js/sources/coa_source.js | 27 +++++++++++++++++++++++++++ js/sources/edition_source.js | 17 ----------------- js/stores/edition_store.js | 4 +++- 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 js/sources/coa_source.js diff --git a/js/sources/coa_source.js b/js/sources/coa_source.js new file mode 100644 index 00000000..88c72cce --- /dev/null +++ b/js/sources/coa_source.js @@ -0,0 +1,27 @@ +'use strict'; + +import requests from '../utils/requests'; + +import EditionActions from '../actions/edition_actions'; + + +const CoaSource = { + lookupCoa: { + remote(state) { + return requests.get('coa', { id: state.edition.coa }); + }, + + success: EditionActions.successFetchCoa, + error: EditionActions.errorCoa + }, + + performCreateCoa: { + remote(state) { + return requests.post('coa_create', {body: { bitcoin_id: state.edition.bitcoin_id }}); + }, + success: EditionActions.successFetchCoa, + error: EditionActions.errorCoa + } +}; + +export default CoaSource; \ No newline at end of file diff --git a/js/sources/edition_source.js b/js/sources/edition_source.js index 157b0087..3e48d257 100644 --- a/js/sources/edition_source.js +++ b/js/sources/edition_source.js @@ -13,23 +13,6 @@ const EditionSource = { success: EditionActions.successFetchEdition, error: EditionActions.errorEdition - }, - - lookupCoa: { - remote(state) { - return requests.get('coa', { id: state.edition.coa }); - }, - - success: EditionActions.successFetchCoa, - error: EditionActions.errorCoa - }, - - performCreateCoa: { - remote(state) { - return requests.post('coa_create', {body: { bitcoin_id: state.edition.bitcoin_id }}); - }, - success: EditionActions.successFetchCoa, - error: EditionActions.errorCoa } }; diff --git a/js/stores/edition_store.js b/js/stores/edition_store.js index 53a16cc4..29a9a0d0 100644 --- a/js/stores/edition_store.js +++ b/js/stores/edition_store.js @@ -3,7 +3,9 @@ import { alt } from '../alt'; import EditionActions from '../actions/edition_actions'; + import EditionSource from '../sources/edition_source'; +import CoaSource from '../sources/coa_source'; class EditionStore { @@ -18,7 +20,7 @@ class EditionStore { }; this.bindActions(EditionActions); - this.registerAsync(EditionSource); + this.registerAsync(Object.assign(EditionSource, CoaSource)); } onFetchEdition(idToFetch) { From 057a278d1916a29aba4f970f6450d0db7316ff11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 9 Dec 2015 13:21:55 +0100 Subject: [PATCH 11/14] In EditionStore use empty object instead of null as default state --- js/components/ascribe_detail/edition_container.js | 4 ++-- js/stores/edition_store.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index a1fa7f58..f72b47f9 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -87,8 +87,8 @@ let EditionContainer = React.createClass({ }, render() { - const {edition, currentUser, coaMeta} = this.state; - if (edition && edition.id && currentUser && currentUser.email) { + const { edition, currentUser, coaMeta } = this.state; + if (Object.keys(edition).length && edition.id && currentUser && currentUser.email) { setDocumentTitle([edition.artist_name, edition.title].join(', ')); return ( Date: Wed, 9 Dec 2015 13:28:11 +0100 Subject: [PATCH 12/14] Fix setState warning for EditionContainer --- js/components/ascribe_detail/edition_container.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index f72b47f9..34775592 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -72,6 +72,7 @@ let EditionContainer = React.createClass({ componentWillUnmount() { window.clearInterval(this.state.timerId); EditionStore.unlisten(this.onChange); + UserStore.unlisten(this.onChange); }, onChange(state) { From 7605f093aba84235a67ccdd8dcb2b65ce50318f3 Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Wed, 9 Dec 2015 14:44:31 +0100 Subject: [PATCH 13/14] Fix Request error case --- js/utils/requests.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/js/utils/requests.js b/js/utils/requests.js index 8388f9db..d9c5288d 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -13,11 +13,17 @@ class Requests { unpackResponse(response) { if (response.status >= 500) { let err = new Error(response.status + ' - ' + response.statusText + ' - on URL:' + response.url); - response + + return response .text() - .then((resText) => JSON.parse(resText)) - .then((resJSON) => { - err = new Error(resJSON.errors.pop()); + .then((resText) => { + const resJson = JSON.parse(resText); + err = new Error(resJson.errors.pop()); + + // ES6 promises don't have a .finally() clause so + // we fake that here by forcing the .catch() clause + // to run + return Promise.reject(); }) .catch(() => { throw err; }); } @@ -57,9 +63,7 @@ class Requests { resolve({}); } } - }).catch((err) => { - reject(err); - }); + }).catch(reject); }); } From 02af0246093e6bec0664c346ba4b149a2fbbfe0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Wed, 9 Dec 2015 15:56:47 +0100 Subject: [PATCH 14/14] Cosmetic changes in EditionContainer and CoaVerifyContainer --- js/components/ascribe_detail/edition_container.js | 7 +++++-- js/components/coa_verify_container.js | 13 +++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 34775592..d70c0601 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -89,12 +89,15 @@ let EditionContainer = React.createClass({ render() { const { edition, currentUser, coaMeta } = this.state; + const { actionPanelButtonListType, furtherDetailsType } = this.props; + if (Object.keys(edition).length && edition.id && currentUser && currentUser.email) { setDocumentTitle([edition.artist_name, edition.title].join(', ')); + return (
@@ -31,7 +32,9 @@ let CoaVerifyContainer = React.createClass({ {getLangText('Verify your Certificate of Authenticity')}
- +

{getLangText('ascribe is using the following public key for verification')}: @@ -52,7 +55,8 @@ let CoaVerifyContainer = React.createClass({ let CoaVerifyForm = React.createClass({ propTypes: { - location: React.PropTypes.object + message: React.PropTypes.string, + signature: React.PropTypes.string }, handleSuccess(response){ @@ -64,7 +68,8 @@ let CoaVerifyForm = React.createClass({ }, render() { - const {message, signature} = this.props.location.query; + const { message, signature } = this.props; + return (