2015-06-05 11:06:36 +02:00
|
|
|
'use strict';
|
|
|
|
|
2015-05-19 17:13:09 +02:00
|
|
|
import React from 'react';
|
2015-06-04 16:15:59 +02:00
|
|
|
import MediaPlayer from './ascribe_media/media_player';
|
2015-06-02 17:32:38 +02:00
|
|
|
|
2015-06-03 11:56:49 +02:00
|
|
|
import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin';
|
2015-06-04 16:15:59 +02:00
|
|
|
import Row from 'react-bootstrap/lib/Row';
|
|
|
|
import Col from 'react-bootstrap/lib/Col';
|
2015-06-03 11:56:49 +02:00
|
|
|
import Button from 'react-bootstrap/lib/Button';
|
2015-06-04 16:15:59 +02:00
|
|
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
2015-06-05 16:20:28 +02:00
|
|
|
import TextareaAutosize from 'react-textarea-autosize';
|
2015-05-27 09:34:49 +02:00
|
|
|
|
2015-06-05 11:06:36 +02:00
|
|
|
import EditionActions from '../actions/edition_actions';
|
2015-06-05 11:40:49 +02:00
|
|
|
import AclButtonList from './ascribe_buttons/acl_button_list';
|
2015-05-19 17:13:09 +02:00
|
|
|
|
2015-06-05 14:22:02 +02:00
|
|
|
import classNames from 'classnames';
|
|
|
|
|
2015-05-26 10:41:41 +02:00
|
|
|
/**
|
2015-05-26 10:52:20 +02:00
|
|
|
* This is the component that implements display-specific functionality
|
2015-05-26 10:41:41 +02:00
|
|
|
*/
|
2015-05-26 13:48:46 +02:00
|
|
|
let Edition = React.createClass({
|
2015-06-05 11:06:36 +02:00
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object,
|
2015-06-05 14:22:02 +02:00
|
|
|
currentUser: React.PropTypes.object,
|
2015-06-05 16:20:28 +02:00
|
|
|
deleteEdition: React.PropTypes.func,
|
|
|
|
savePersonalNote: React.PropTypes.func
|
2015-06-05 11:06:36 +02:00
|
|
|
},
|
|
|
|
|
2015-05-26 13:33:35 +02:00
|
|
|
render() {
|
2015-05-27 17:34:15 +02:00
|
|
|
let thumbnail = this.props.edition.thumbnail;
|
|
|
|
let mimetype = this.props.edition.digital_work.mime;
|
2015-06-03 16:53:27 +02:00
|
|
|
let extraData = null;
|
|
|
|
|
|
|
|
if (this.props.edition.digital_work.encoding_urls) {
|
2015-06-05 16:20:28 +02:00
|
|
|
extraData = this.props.edition.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
2015-06-03 16:53:27 +02:00
|
|
|
}
|
2015-05-27 17:34:15 +02:00
|
|
|
|
2015-06-05 14:22:02 +02:00
|
|
|
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>
|
|
|
|
);
|
|
|
|
|
2015-05-26 13:33:35 +02:00
|
|
|
return (
|
2015-06-04 16:15:59 +02:00
|
|
|
<Row>
|
2015-06-05 16:20:28 +02:00
|
|
|
<Col md={6}>
|
2015-06-04 16:15:59 +02:00
|
|
|
<MediaPlayer mimetype={mimetype}
|
2015-06-03 16:53:27 +02:00
|
|
|
preview={thumbnail}
|
|
|
|
url={this.props.edition.digital_work.url}
|
|
|
|
extraData={extraData} />
|
2015-06-04 16:15:59 +02:00
|
|
|
<p className="text-center">
|
|
|
|
<Button bsSize="xsmall" href={this.props.edition.digital_work.url} target="_blank">
|
|
|
|
Download <Glyphicon glyph="cloud-download" />
|
|
|
|
</Button>
|
|
|
|
</p>
|
|
|
|
</Col>
|
2015-06-05 16:20:28 +02:00
|
|
|
<Col md={6} className="ascribe-edition-details">
|
2015-05-26 14:58:11 +02:00
|
|
|
<EditionHeader edition={this.props.edition}/>
|
2015-06-05 14:22:02 +02:00
|
|
|
<EditionSummary
|
|
|
|
edition={this.props.edition}
|
|
|
|
currentUser={ this.props.currentUser }/>
|
2015-06-05 16:20:28 +02:00
|
|
|
<CollapsibleEditionDetails
|
|
|
|
title="Personal Note"
|
|
|
|
iconName="pencil">
|
|
|
|
<EditionPersonalNote
|
|
|
|
savePersonalNote={this.props.savePersonalNote}/>
|
|
|
|
</CollapsibleEditionDetails>
|
2015-06-05 14:22:02 +02:00
|
|
|
|
|
|
|
<CollapsibleEditionDetails
|
|
|
|
title="Provenance/Ownership History"
|
|
|
|
show={this.props.edition.ownership_history && this.props.edition.ownership_history.length > 0}>
|
2015-06-05 16:20:28 +02:00
|
|
|
<EditionDetailHistoryIterator
|
2015-06-05 14:22:02 +02:00
|
|
|
history={this.props.edition.ownership_history} />
|
|
|
|
</CollapsibleEditionDetails>
|
|
|
|
|
|
|
|
<CollapsibleEditionDetails
|
|
|
|
title="Loan History"
|
|
|
|
show={this.props.edition.loan_history && this.props.edition.loan_history.length > 0}>
|
2015-06-05 16:20:28 +02:00
|
|
|
<EditionDetailHistoryIterator
|
2015-06-05 14:22:02 +02:00
|
|
|
history={this.props.edition.loan_history} />
|
|
|
|
</CollapsibleEditionDetails>
|
|
|
|
|
|
|
|
<CollapsibleEditionDetails
|
|
|
|
title="SPOOL Details">
|
|
|
|
<EditionDetailProperty
|
|
|
|
label="Artwork ID"
|
|
|
|
value={bitcoinIdValue} />
|
|
|
|
<EditionDetailProperty
|
|
|
|
label="Hash of Artwork, title, etc"
|
|
|
|
value={hashOfArtwork} />
|
|
|
|
<EditionDetailProperty
|
|
|
|
label="Owned by SPOOL address"
|
|
|
|
value="MISSING IN /editions/<id> RESOURCE!" />
|
|
|
|
</CollapsibleEditionDetails>
|
|
|
|
|
|
|
|
<CollapsibleEditionDetails
|
|
|
|
title="Delete Actions">
|
2015-06-05 16:20:28 +02:00
|
|
|
<Button
|
2015-06-05 14:22:02 +02:00
|
|
|
bsStyle="danger"
|
|
|
|
onClick={this.props.deleteEdition}>
|
|
|
|
Remove this artwork from your list
|
|
|
|
</Button>
|
|
|
|
</CollapsibleEditionDetails>
|
2015-06-04 16:15:59 +02:00
|
|
|
</Col>
|
|
|
|
</Row>
|
2015-05-26 13:33:35 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-05-26 13:48:46 +02:00
|
|
|
let EditionHeader = React.createClass({
|
2015-06-05 11:06:36 +02:00
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
2015-05-26 13:33:35 +02:00
|
|
|
render() {
|
2015-06-05 11:06:36 +02:00
|
|
|
var titleHtml = <div className="ascribe-detail-title">{this.props.edition.title}</div>;
|
2015-05-26 13:33:35 +02:00
|
|
|
return (
|
|
|
|
<div className="ascribe-detail-header">
|
2015-06-05 14:22:02 +02:00
|
|
|
<EditionDetailProperty label="TITLE" value={titleHtml} />
|
|
|
|
<EditionDetailProperty label="BY" value={this.props.edition.artist_name} />
|
|
|
|
<EditionDetailProperty label="DATE" value={ this.props.edition.date_created.slice(0, 4) } />
|
2015-05-26 13:33:35 +02:00
|
|
|
<hr/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2015-05-19 17:13:09 +02:00
|
|
|
});
|
|
|
|
|
2015-06-05 11:40:49 +02:00
|
|
|
|
2015-06-03 11:56:49 +02:00
|
|
|
let EditionSummary = React.createClass({
|
2015-06-05 11:06:36 +02:00
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object,
|
|
|
|
currentUser: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
2015-06-02 11:38:18 +02:00
|
|
|
handleSuccess(){
|
|
|
|
EditionActions.fetchOne(this.props.edition.id);
|
|
|
|
},
|
2015-06-05 11:06:36 +02:00
|
|
|
|
2015-05-26 13:33:35 +02:00
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div className="ascribe-detail-header">
|
2015-06-05 14:22:02 +02:00
|
|
|
<EditionDetailProperty label="EDITION"
|
2015-06-05 11:06:36 +02:00
|
|
|
value={this.props.edition.edition_number + ' of ' + this.props.edition.num_editions} />
|
2015-06-05 14:22:02 +02:00
|
|
|
<EditionDetailProperty label="ID" value={ this.props.edition.bitcoin_id } />
|
|
|
|
<EditionDetailProperty label="OWNER" value={ this.props.edition.owner } />
|
2015-05-27 15:53:26 +02:00
|
|
|
<br/>
|
2015-06-05 16:20:28 +02:00
|
|
|
<Row>
|
|
|
|
<Col md={12}>
|
|
|
|
<AclButtonList
|
|
|
|
className="pull-left"
|
|
|
|
availableAcls={this.props.edition.acl}
|
|
|
|
editions={[this.props.edition]}
|
|
|
|
handleSuccess={this.handleSuccess} />
|
|
|
|
</Col>
|
|
|
|
</Row>
|
2015-05-26 15:31:28 +02:00
|
|
|
<hr/>
|
|
|
|
</div>
|
|
|
|
);
|
2015-05-27 09:34:49 +02:00
|
|
|
|
2015-05-26 15:31:28 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-05 14:22:02 +02:00
|
|
|
let CollapsibleEditionDetails = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
title: React.PropTypes.string,
|
|
|
|
children: React.PropTypes.oneOfType([
|
|
|
|
React.PropTypes.object,
|
|
|
|
React.PropTypes.array
|
|
|
|
]),
|
2015-06-05 16:20:28 +02:00
|
|
|
show: React.PropTypes.bool,
|
|
|
|
iconName: React.PropTypes.string
|
2015-06-03 11:56:49 +02:00
|
|
|
},
|
2015-06-05 14:22:02 +02:00
|
|
|
|
|
|
|
getDefaultProps() {
|
|
|
|
return {
|
|
|
|
show: true
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2015-06-03 11:56:49 +02:00
|
|
|
render() {
|
2015-06-05 14:22:02 +02:00
|
|
|
if(this.props.show) {
|
|
|
|
return (
|
|
|
|
<div className="ascribe-detail-header">
|
2015-06-05 16:20:28 +02:00
|
|
|
<CollapsibleParagraph
|
|
|
|
title={this.props.title}
|
|
|
|
iconName={this.props.iconName}>
|
2015-06-05 14:22:02 +02:00
|
|
|
{this.props.children}
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const CollapsibleParagraph = React.createClass({
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
title: React.PropTypes.string,
|
|
|
|
children: React.PropTypes.oneOfType([
|
|
|
|
React.PropTypes.object,
|
|
|
|
React.PropTypes.array
|
2015-06-05 16:20:28 +02:00
|
|
|
]),
|
|
|
|
iconName: React.PropTypes.string
|
2015-06-05 14:22:02 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
mixins: [CollapsibleMixin],
|
|
|
|
|
|
|
|
getCollapsibleDOMNode(){
|
|
|
|
return React.findDOMNode(this.refs.panel);
|
|
|
|
},
|
|
|
|
|
|
|
|
getCollapsibleDimensionValue(){
|
|
|
|
return React.findDOMNode(this.refs.panel).scrollHeight;
|
|
|
|
},
|
|
|
|
|
|
|
|
onHandleToggle(e){
|
|
|
|
e.preventDefault();
|
|
|
|
this.setState({expanded: !this.state.expanded});
|
|
|
|
},
|
|
|
|
|
2015-06-05 16:20:28 +02:00
|
|
|
render() {
|
2015-06-05 14:22:02 +02:00
|
|
|
let styles = this.getCollapsibleClassSet();
|
2015-06-05 16:20:28 +02:00
|
|
|
let text = this.isExpanded() ? '-' : '+';
|
|
|
|
|
|
|
|
let icon = this.props.iconName ? (<Glyphicon style={{fontSize: '.75em'}} glyph={this.props.iconName} />) : null;
|
|
|
|
|
2015-06-03 11:56:49 +02:00
|
|
|
return (
|
2015-06-05 14:22:02 +02:00
|
|
|
<div className="ascribe-edition-collapsible-wrapper">
|
|
|
|
<div onClick={this.onHandleToggle}>
|
2015-06-05 16:20:28 +02:00
|
|
|
<span>{text} {icon} {this.props.title} </span>
|
2015-06-05 14:22:02 +02:00
|
|
|
</div>
|
|
|
|
<div ref='panel' className={classNames(styles) + ' ascribe-edition-collapible-content'}>
|
|
|
|
{this.props.children}
|
|
|
|
</div>
|
2015-06-03 11:56:49 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2015-05-26 15:31:28 +02:00
|
|
|
let EditionDetailProperty = React.createClass({
|
2015-06-05 11:06:36 +02:00
|
|
|
propTypes: {
|
|
|
|
label: React.PropTypes.string,
|
2015-06-05 11:32:10 +02:00
|
|
|
value: React.PropTypes.oneOfType([
|
|
|
|
React.PropTypes.string,
|
|
|
|
React.PropTypes.element
|
2015-06-05 14:22:02 +02:00
|
|
|
]),
|
|
|
|
separator: React.PropTypes.string,
|
|
|
|
labelClassName: React.PropTypes.string,
|
|
|
|
valueClassName: React.PropTypes.string
|
|
|
|
},
|
|
|
|
|
|
|
|
getDefaultProps() {
|
|
|
|
return {
|
|
|
|
separator: ':',
|
|
|
|
labelClassName: 'col-xs-5 col-sm-5 col-md-5 col-lg-5',
|
|
|
|
valueClassName: 'col-xs-7 col-sm-7 col-md-7 col-lg-7'
|
|
|
|
};
|
2015-06-05 11:06:36 +02:00
|
|
|
},
|
|
|
|
|
2015-05-26 15:31:28 +02:00
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div className="row ascribe-detail-property">
|
|
|
|
<div className="row-same-height">
|
2015-06-05 14:22:02 +02:00
|
|
|
<div className={this.props.labelClassName + ' col-xs-height col-bottom'}>
|
|
|
|
<div>{ this.props.label }{this.props.separator}</div>
|
2015-05-26 13:33:35 +02:00
|
|
|
</div>
|
2015-06-05 14:22:02 +02:00
|
|
|
<div className={this.props.valueClassName + ' col-xs-height col-bottom'}>
|
2015-05-26 15:31:28 +02:00
|
|
|
<div>{ this.props.value }</div>
|
2015-05-26 13:33:35 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-05 14:22:02 +02:00
|
|
|
let EditionDetailHistoryIterator = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
history: React.PropTypes.array
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
{this.props.history.map((historicalEvent, i) => {
|
|
|
|
return (
|
|
|
|
<EditionDetailProperty
|
|
|
|
key={i}
|
|
|
|
label={historicalEvent[0]}
|
|
|
|
value={historicalEvent[1]}
|
|
|
|
labelClassName="col-xs-4 col-sm-4 col-md-4 col-lg-4"
|
|
|
|
valueClassName="col-xs-8 col-sm-8 col-md-8 col-lg-8"
|
|
|
|
separator="" />
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-05 16:20:28 +02:00
|
|
|
let EditionPersonalNote = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
savePersonalNote: React.PropTypes.func
|
|
|
|
},
|
|
|
|
|
|
|
|
prepareSavePersonalNote() {
|
|
|
|
let personalNote = React.findDOMNode(this.refs.personalNote).value;
|
|
|
|
this.props.savePersonalNote(personalNote);
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<Row>
|
|
|
|
<Col md={12} className="ascribe-edition-personal-note">
|
|
|
|
<TextareaAutosize
|
|
|
|
ref="personalNote"
|
|
|
|
className="form-control"
|
|
|
|
rows={3}
|
|
|
|
placeholder='Write something...' />
|
|
|
|
<Button
|
|
|
|
onClick={this.prepareSavePersonalNote}
|
|
|
|
className="pull-right">Save</Button>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-05-26 16:46:02 +02:00
|
|
|
|
2015-05-27 09:34:49 +02:00
|
|
|
export default Edition;
|