1
0
mirror of https://github.com/ascribe/onion.git synced 2024-11-15 17:45:10 +01:00
onion/js/components/ascribe_detail/edition.js

434 lines
16 KiB
JavaScript
Raw Normal View History

2015-06-26 00:38:40 +02:00
'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';
2015-07-07 15:48:09 +02:00
import UserActions from '../../actions/user_actions';
import UserStore from '../../stores/user_store';
import CoaActions from '../../actions/coa_actions';
import CoaStore from '../../stores/coa_store';
2015-06-26 00:38:40 +02:00
2015-07-08 22:54:07 +02:00
import MediaContainer from './media_container';
2015-06-26 00:38:40 +02:00
2015-07-07 15:48:09 +02:00
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
2015-06-26 00:38:40 +02:00
2015-07-07 15:48:09 +02:00
import Form from './../ascribe_forms/form';
import Property from './../ascribe_forms/property';
2015-07-08 22:54:07 +02:00
import EditionDetailProperty from './detail_property';
2015-07-07 15:48:09 +02:00
import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable';
2015-06-26 00:38:40 +02:00
2015-07-08 22:54:07 +02:00
import EditionHeader from './header';
import EditionFurtherDetails from './further_details';
2015-07-08 22:54:07 +02:00
//import PieceExtraDataForm from './../ascribe_forms/form_piece_extradata';
2015-07-07 15:48:09 +02:00
import RequestActionForm from './../ascribe_forms/form_request_action';
import EditionActions from '../../actions/edition_actions';
import AclButtonList from './../ascribe_buttons/acl_button_list';
2015-06-26 00:38:40 +02:00
//import ReactS3FineUploader from './../ascribe_uploader/react_s3_fine_uploader';
2015-06-29 10:00:26 +02:00
2015-07-07 15:48:09 +02:00
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
2015-06-26 00:38:40 +02:00
2015-07-07 15:48:09 +02:00
import apiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants';
2015-06-26 00:38:40 +02:00
2015-07-07 15:48:09 +02:00
import { getCookie } from '../../utils/fetch_api_utils';
import { getLangText } from '../../utils/lang_utils';
2015-06-29 16:46:12 +02:00
2015-06-26 00:38:40 +02:00
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() {
return (
<Row>
<Col md={6}>
2015-07-03 12:35:45 +02:00
<MediaContainer
2015-07-08 22:54:07 +02:00
content={this.props.edition}/>
2015-06-26 00:38:40 +02:00
</Col>
<Col md={6} className="ascribe-edition-details">
2015-07-08 22:54:07 +02:00
<EditionHeader content={this.props.edition}/>
2015-06-26 00:38:40 +02:00
<EditionSummary
currentUser={this.state.currentUser}
edition={this.props.edition} />
<CollapsibleParagraph
title="Notes"
show={(this.state.currentUser.username && true || false) ||
(this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note)}
defaultExpanded={true}>
2015-06-26 00:38:40 +02:00
<EditionPersonalNote
currentUser={this.state.currentUser}
handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
<EditionPublicEditionNote
handleSuccess={this.props.loadEdition}
edition={this.props.edition}/>
</CollapsibleParagraph>
<CollapsibleParagraph
2015-07-03 19:08:56 +02:00
title={getLangText('Further Details')}
show={this.props.edition.acl.indexOf('edit') > -1
|| Object.keys(this.props.edition.extra_data).length > 0
|| this.props.edition.other_data !== null}>
2015-06-26 00:38:40 +02:00
<EditionFurtherDetails
editable={this.props.edition.acl.indexOf('edit') > -1}
pieceId={this.props.edition.parent}
extraData={this.props.edition.extra_data}
otherData={this.props.edition.other_data}
handleSuccess={this.props.loadEdition}/>
2015-06-26 00:38:40 +02:00
</CollapsibleParagraph>
<CollapsibleParagraph
2015-07-03 19:08:56 +02:00
title={getLangText('Certificate of Authenticity')}
2015-06-26 00:38:40 +02:00
show={this.props.edition.acl.indexOf('coa') > -1}>
<CoaDetails
edition={this.props.edition}/>
</CollapsibleParagraph>
<CollapsibleParagraph
2015-07-03 19:08:56 +02:00
title={getLangText('Provenance/Ownership History')}
2015-06-26 00:38:40 +02:00
show={this.props.edition.ownership_history && this.props.edition.ownership_history.length > 0}>
<EditionDetailHistoryIterator
history={this.props.edition.ownership_history} />
</CollapsibleParagraph>
<CollapsibleParagraph
2015-07-03 19:08:56 +02:00
title={getLangText('Consignment History')}
2015-06-26 00:38:40 +02:00
show={this.props.edition.consign_history && this.props.edition.consign_history.length > 0}>
<EditionDetailHistoryIterator
history={this.props.edition.consign_history} />
</CollapsibleParagraph>
<CollapsibleParagraph
2015-07-03 19:08:56 +02:00
title={getLangText('Loan History')}
2015-06-26 00:38:40 +02:00
show={this.props.edition.loan_history && this.props.edition.loan_history.length > 0}>
<EditionDetailHistoryIterator
history={this.props.edition.loan_history} />
</CollapsibleParagraph>
<CollapsibleParagraph
2015-07-03 19:08:56 +02:00
title={getLangText('SPOOL Details')}>
2015-07-03 12:35:45 +02:00
<SpoolDetails
edition={this.props.edition} />
2015-06-26 00:38:40 +02:00
</CollapsibleParagraph>
</Col>
</Row>
);
}
});
let EditionSummary = React.createClass({
propTypes: {
edition: React.PropTypes.object
},
getTransferWithdrawData(){
return {'bitcoin_id': this.props.edition.bitcoin_id};
},
2015-06-26 00:38:40 +02:00
handleSuccess(){
EditionActions.fetchOne(this.props.edition.id);
},
showNotification(response){
this.handleSuccess();
let notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
},
2015-07-08 22:54:07 +02:00
getStatus(){
2015-06-26 00:38:40 +02:00
let status = null;
if (this.props.edition.status.length > 0){
let statusStr = this.props.edition.status.join().replace(/_/, ' ');
status = <EditionDetailProperty label="STATUS" value={ statusStr }/>;
2015-06-26 00:38:40 +02:00
if (this.props.edition.pending_new_owner && this.props.edition.acl.indexOf('withdraw_transfer') > -1){
status = (
<Form
url={apiUrls.ownership_transfers_withdraw}
getFormData={this.getTransferWithdrawData}
handleSuccess={this.showNotification}>
<EditionDetailProperty label="STATUS" value={ statusStr }>
<button
type="submit"
className="pull-right btn btn-default btn-sm">
WITHDRAW
</button>
</EditionDetailProperty>
</Form>
2015-06-26 00:38:40 +02:00
);
}
}
2015-07-08 22:54:07 +02:00
return status;
},
getActions(){
2015-06-26 00:38:40 +02:00
let actions = null;
if (this.props.edition.request_action && this.props.edition.request_action.length > 0){
actions = (
<RequestActionForm
editions={ [this.props.edition] }
handleSuccess={this.showNotification}/>);
}
else {
actions = (
<Row>
<Col md={12}>
<AclButtonList
2015-06-30 17:12:13 +02:00
className="text-center ascribe-button-list"
2015-06-26 00:38:40 +02:00
availableAcls={this.props.edition.acl}
editions={[this.props.edition]}
handleSuccess={this.handleSuccess} />
</Col>
</Row>);
}
2015-07-08 22:54:07 +02:00
return actions;
},
render() {
2015-06-26 00:38:40 +02:00
return (
<div className="ascribe-detail-header">
2015-07-03 19:08:56 +02:00
<EditionDetailProperty label={getLangText('EDITION')}
value={this.props.edition.edition_number + ' ' + getLangText('of') + ' ' + this.props.edition.num_editions} />
<EditionDetailProperty label={getLangText('ID')} value={ this.props.edition.bitcoin_id } />
<EditionDetailProperty label={getLangText('OWNER')} value={ this.props.edition.owner } />
2015-07-08 22:54:07 +02:00
{this.getStatus()}
2015-06-26 00:38:40 +02:00
<br/>
2015-07-08 22:54:07 +02:00
{this.getActions()}
2015-06-26 00:38:40 +02:00
<hr/>
</div>
);
}
});
let EditionDetailHistoryIterator = React.createClass({
propTypes: {
history: React.PropTypes.array
},
render() {
return (
<Form>
{this.props.history.map((historicalEvent, i) => {
2015-06-26 01:05:20 +02:00
return (
2015-06-26 00:38:40 +02:00
<Property
name={i}
key={i}
label={ historicalEvent[0] }
editable={false}>
<pre className="ascribe-pre">{ historicalEvent[1] }</pre>
</Property>
2015-06-26 01:05:20 +02:00
);
2015-06-26 00:38:40 +02:00
})}
<hr />
</Form>
);
}
});
let EditionPersonalNote = React.createClass({
propTypes: {
edition: React.PropTypes.object,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func
},
showNotification(){
this.props.handleSuccess();
2015-07-03 19:08:56 +02:00
let notification = new GlobalNotificationModel(getLangText('Private note saved'), 'success');
2015-06-26 00:38:40 +02:00
GlobalNotificationActions.appendGlobalNotification(notification);
},
render() {
if (this.props.currentUser.username && true || false) {
return (
<Form
url={apiUrls.note_notes}
handleSuccess={this.showNotification}>
<Property
name='note'
2015-07-03 19:08:56 +02:00
label={getLangText('Personal note (private)')}
2015-06-26 00:38:40 +02:00
editable={true}>
<InputTextAreaToggable
rows={1}
2015-06-26 00:38:40 +02:00
editable={true}
defaultValue={this.props.edition.note_from_user}
2015-07-03 19:08:56 +02:00
placeholder={getLangText('Enter a personal note%s', '...')}
2015-07-10 14:15:22 +02:00
required="required"/>
2015-06-26 00:38:40 +02:00
</Property>
<Property hidden={true} name='bitcoin_id'>
<input defaultValue={this.props.edition.bitcoin_id}/>
</Property>
<hr />
</Form>
);
}
return null;
}
});
let EditionPublicEditionNote = React.createClass({
propTypes: {
edition: React.PropTypes.object,
handleSuccess: React.PropTypes.func
},
showNotification(){
this.props.handleSuccess();
2015-07-08 10:15:58 +02:00
let notification = new GlobalNotificationModel(getLangText('Public note saved'), 'success');
2015-06-26 00:38:40 +02:00
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 (
<Form
url={apiUrls.note_edition}
handleSuccess={this.showNotification}>
<Property
name='note'
2015-07-03 19:08:56 +02:00
label={getLangText('Edition note (public)')}
2015-06-26 00:38:40 +02:00
editable={isEditable}>
<InputTextAreaToggable
rows={1}
2015-06-26 00:38:40 +02:00
editable={isEditable}
defaultValue={this.props.edition.public_note}
2015-07-10 14:15:22 +02:00
placeholder={getLangText('Enter a public note for this edition%s', '...')}
required="required"/>
2015-06-26 00:38:40 +02:00
</Property>
<Property hidden={true} name='bitcoin_id'>
<input defaultValue={this.props.edition.bitcoin_id}/>
</Property>
<hr />
</Form>
);
}
return null;
}
});
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);
}
2015-07-10 14:15:22 +02:00
else {
2015-06-26 00:38:40 +02:00
CoaActions.create(this.props.edition);
}
},
componentWillUnmount() {
CoaStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
render() {
if (this.state.coa.url_safe) {
return (
<div>
2015-06-30 17:12:13 +02:00
<p className="text-center ascribe-button-list">
2015-07-02 19:22:32 +02:00
<a href={this.state.coa.url_safe} target="_blank">
<button className="btn btn-default btn-xs">
2015-07-03 19:08:56 +02:00
{getLangText('Download')} <Glyphicon glyph="cloud-download"/>
2015-07-02 19:22:32 +02:00
</button>
</a>
2015-06-26 00:38:40 +02:00
<Link to="coa_verify">
2015-06-30 17:12:13 +02:00
<button className="btn btn-default btn-xs">
2015-07-03 19:08:56 +02:00
{getLangText('Verify')} <Glyphicon glyph="check"/>
2015-06-30 17:12:13 +02:00
</button>
2015-06-26 00:38:40 +02:00
</Link>
</p>
</div>
);
}
return (
<div className="text-center">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div>);
}
});
2015-07-03 12:35:45 +02:00
let SpoolDetails = React.createClass({
propTypes: {
edition: React.PropTypes.object
},
render() {
let bitcoinIdValue = (
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.bitcoin_id}>{this.props.edition.bitcoin_id}</a>
);
let hashOfArtwork = (
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.hash_as_address}>{this.props.edition.hash_as_address}</a>
);
let ownerAddress = (
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.btc_owner_address_noprefix}>{this.props.edition.btc_owner_address_noprefix}</a>
);
return (
<Form >
<Property
name='artwork_id'
2015-07-03 19:08:56 +02:00
label={getLangText('Artwork ID')}
2015-07-03 12:35:45 +02:00
editable={false}>
<pre className="ascribe-pre">{bitcoinIdValue}</pre>
</Property>
<Property
name='hash_of_artwork'
2015-07-03 19:08:56 +02:00
label={getLangText('Hash of Artwork, title, etc')}
2015-07-03 12:35:45 +02:00
editable={false}>
<pre className="ascribe-pre">{hashOfArtwork}</pre>
</Property>
<Property
name='owner_address'
2015-07-03 19:08:56 +02:00
label={getLangText('Owned by SPOOL address')}
2015-07-03 12:35:45 +02:00
editable={false}>
<pre className="ascribe-pre">{ownerAddress}</pre>
</Property>
<hr />
</Form>);
}
});
2015-06-26 00:38:40 +02:00
export default Edition;