mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
Refactor Edition endpoints to create Coa on demand properly
This commit is contained in:
parent
bf6b78c058
commit
da75353b00
@ -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);
|
@ -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);
|
||||
|
@ -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({
|
||||
<EditionSummary
|
||||
actionPanelButtonListType={this.props.actionPanelButtonListType}
|
||||
edition={this.props.edition}
|
||||
currentUser={this.state.currentUser}
|
||||
currentUser={this.props.currentUser}
|
||||
handleSuccess={this.props.loadEdition}/>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Certificate of Authenticity')}
|
||||
show={this.props.edition.acl.acl_coa === true}>
|
||||
<CoaDetails
|
||||
edition={this.props.edition}/>
|
||||
coa={this.props.edition.coa}/>
|
||||
</CollapsibleParagraph>
|
||||
|
||||
<CollapsibleParagraph
|
||||
@ -133,7 +103,7 @@ let Edition = React.createClass({
|
||||
|
||||
<CollapsibleParagraph
|
||||
title="Notes"
|
||||
show={!!(this.state.currentUser.username
|
||||
show={!!(this.props.currentUser.username
|
||||
|| this.props.edition.acl.acl_edit
|
||||
|| this.props.edition.public_note)}>
|
||||
<Note
|
||||
@ -144,7 +114,7 @@ let Edition = React.createClass({
|
||||
editable={true}
|
||||
successMessage={getLangText('Private note saved')}
|
||||
url={ApiUrls.note_private_edition}
|
||||
currentUser={this.state.currentUser}/>
|
||||
currentUser={this.props.currentUser}/>
|
||||
<Note
|
||||
id={() => {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}/>
|
||||
</CollapsibleParagraph>
|
||||
<CollapsibleParagraph
|
||||
title={getLangText('Further Details')}
|
||||
@ -246,38 +216,15 @@ let EditionSummary = React.createClass({
|
||||
|
||||
let CoaDetails = React.createClass({
|
||||
propTypes: {
|
||||
edition: React.PropTypes.object
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return CoaStore.getState();
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
let { edition } = this.props;
|
||||
CoaStore.listen(this.onChange);
|
||||
if(edition.coa) {
|
||||
CoaActions.fetchOrCreate(edition.coa, edition.bitcoin_id);
|
||||
}
|
||||
else {
|
||||
CoaActions.create(edition.bitcoin_id);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
CoaStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
coa: React.PropTypes.object
|
||||
},
|
||||
|
||||
render() {
|
||||
if(this.state.coa && this.state.coa.url_safe) {
|
||||
if(this.props.coa && this.props.coa.url_safe) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-center ascribe-button-list">
|
||||
<a href={this.state.coa.url_safe} target="_blank">
|
||||
<a href={this.props.coa.url_safe} target="_blank">
|
||||
<button className="btn btn-default btn-xs">
|
||||
{getLangText('Download')} <Glyphicon glyph="cloud-download"/>
|
||||
</button>
|
||||
@ -291,10 +238,10 @@ let CoaDetails = React.createClass({
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if(typeof this.state.coa === 'string'){
|
||||
} else if(typeof this.props.coa === 'string'){
|
||||
return (
|
||||
<div className="text-center">
|
||||
{this.state.coa}
|
||||
{this.props.coa}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -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 (
|
||||
<Edition
|
||||
actionPanelButtonListType={this.props.actionPanelButtonListType}
|
||||
furtherDetailsType={this.props.furtherDetailsType}
|
||||
edition={this.state.edition}
|
||||
loadEdition={this.loadEdition} />
|
||||
currentUser={this.state.currentUser}
|
||||
loadEdition={() => EditionActions.fetchEdition(this.props.params.editionId)} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
|
36
js/sources/edition_source.js
Normal file
36
js/sources/edition_source.js
Normal file
@ -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;
|
@ -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');
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user