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] 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; } }