mirror of
https://github.com/ascribe/onion.git
synced 2025-01-05 11:25:09 +01:00
piece view
This commit is contained in:
parent
d5ce1ecde2
commit
8ac26275f5
55
js/components/ascribe_detail/detail_property.js
Normal file
55
js/components/ascribe_detail/detail_property.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
let DetailProperty = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
label: React.PropTypes.string,
|
||||||
|
value: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.element
|
||||||
|
]),
|
||||||
|
separator: React.PropTypes.string,
|
||||||
|
labelClassName: React.PropTypes.string,
|
||||||
|
valueClassName: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
separator: ':',
|
||||||
|
labelClassName: 'col-xs-5 col-sm-4 col-md-3 col-lg-3',
|
||||||
|
valueClassName: 'col-xs-7 col-sm-8 col-md-9 col-lg-9'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let value = this.props.value;
|
||||||
|
if (this.props.children){
|
||||||
|
value = (
|
||||||
|
<div className="row-same-height">
|
||||||
|
<div className="col-xs-6 col-xs-height col-bottom no-padding">
|
||||||
|
{ this.props.value }
|
||||||
|
</div>
|
||||||
|
<div className="col-xs-6 col-xs-height">
|
||||||
|
{ this.props.children }
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="row ascribe-detail-property">
|
||||||
|
<div className="row-same-height">
|
||||||
|
<div className={this.props.labelClassName + ' col-xs-height col-bottom'}>
|
||||||
|
<div>{ this.props.label + this.props.separator}</div>
|
||||||
|
</div>
|
||||||
|
<div className={this.props.valueClassName + ' col-xs-height col-bottom'}>
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default DetailProperty;
|
@ -7,21 +7,23 @@ import Row from 'react-bootstrap/lib/Row';
|
|||||||
import Col from 'react-bootstrap/lib/Col';
|
import Col from 'react-bootstrap/lib/Col';
|
||||||
import Button from 'react-bootstrap/lib/Button';
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin';
|
|
||||||
|
|
||||||
import UserActions from '../../actions/user_actions';
|
import UserActions from '../../actions/user_actions';
|
||||||
import UserStore from '../../stores/user_store';
|
import UserStore from '../../stores/user_store';
|
||||||
import CoaActions from '../../actions/coa_actions';
|
import CoaActions from '../../actions/coa_actions';
|
||||||
import CoaStore from '../../stores/coa_store';
|
import CoaStore from '../../stores/coa_store';
|
||||||
|
|
||||||
import MediaPlayer from './../ascribe_media/media_player';
|
import MediaContainer from './media_container';
|
||||||
|
|
||||||
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
|
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
|
||||||
|
|
||||||
import Form from './../ascribe_forms/form';
|
import Form from './../ascribe_forms/form';
|
||||||
import Property from './../ascribe_forms/property';
|
import Property from './../ascribe_forms/property';
|
||||||
|
import EditionDetailProperty from './detail_property';
|
||||||
import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable';
|
import InputTextAreaToggable from './../ascribe_forms/input_textarea_toggable';
|
||||||
|
|
||||||
|
import EditionHeader from './header';
|
||||||
|
|
||||||
import PieceExtraDataForm from './../ascribe_forms/form_piece_extradata';
|
import PieceExtraDataForm from './../ascribe_forms/form_piece_extradata';
|
||||||
import RequestActionForm from './../ascribe_forms/form_request_action';
|
import RequestActionForm from './../ascribe_forms/form_request_action';
|
||||||
|
|
||||||
@ -35,7 +37,6 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
|
|||||||
|
|
||||||
import apiUrls from '../../constants/api_urls';
|
import apiUrls from '../../constants/api_urls';
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import { getCookie } from '../../utils/fetch_api_utils';
|
import { getCookie } from '../../utils/fetch_api_utils';
|
||||||
|
|
||||||
@ -72,10 +73,10 @@ let Edition = React.createClass({
|
|||||||
<Row>
|
<Row>
|
||||||
<Col md={6}>
|
<Col md={6}>
|
||||||
<MediaContainer
|
<MediaContainer
|
||||||
edition={this.props.edition}/>
|
content={this.props.edition}/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={6} className="ascribe-edition-details">
|
<Col md={6} className="ascribe-edition-details">
|
||||||
<EditionHeader edition={this.props.edition}/>
|
<EditionHeader content={this.props.edition}/>
|
||||||
<EditionSummary
|
<EditionSummary
|
||||||
currentUser={this.state.currentUser}
|
currentUser={this.state.currentUser}
|
||||||
edition={this.props.edition} />
|
edition={this.props.edition} />
|
||||||
@ -142,106 +143,6 @@ let Edition = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let MediaContainer = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
edition: React.PropTypes.object
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let thumbnail = this.props.edition.thumbnail;
|
|
||||||
let mimetype = this.props.edition.digital_work.mime;
|
|
||||||
let embed = null;
|
|
||||||
let extraData = null;
|
|
||||||
|
|
||||||
if (this.props.edition.digital_work.encoding_urls) {
|
|
||||||
extraData = this.props.edition.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (['video', 'audio'].indexOf(mimetype) > -1){
|
|
||||||
embed = (
|
|
||||||
<CollapsibleButton
|
|
||||||
button={
|
|
||||||
<Button bsSize="xsmall" className="ascribe-margin-1px">
|
|
||||||
Embed
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
panel={
|
|
||||||
<pre className="">
|
|
||||||
{'<iframe width="560" height="315" src="http://embed.ascribe.io/edition/'
|
|
||||||
+ this.props.edition.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<MediaPlayer mimetype={mimetype}
|
|
||||||
preview={thumbnail}
|
|
||||||
url={this.props.edition.digital_work.url}
|
|
||||||
extraData={extraData} />
|
|
||||||
<p className="text-center">
|
|
||||||
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.edition.digital_work.url} target="_blank">
|
|
||||||
Download <Glyphicon glyph="cloud-download"/>
|
|
||||||
</Button>
|
|
||||||
{embed}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let CollapsibleButton = React.createClass({
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
button: React.PropTypes.object,
|
|
||||||
children: React.PropTypes.oneOfType([
|
|
||||||
React.PropTypes.object,
|
|
||||||
React.PropTypes.array
|
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {expanded: false};
|
|
||||||
},
|
|
||||||
handleToggle(e){
|
|
||||||
e.preventDefault();
|
|
||||||
this.setState({expanded: !this.state.expanded});
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
let isVisible = (this.state.expanded) ? '' : 'invisible';
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<span onClick={this.handleToggle}>
|
|
||||||
{this.props.button}
|
|
||||||
</span>
|
|
||||||
<div ref='panel' className={isVisible}>
|
|
||||||
{this.props.panel}
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let EditionHeader = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
edition: React.PropTypes.object
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
var titleHtml = <div className="ascribe-detail-title">{this.props.edition.title}</div>;
|
|
||||||
return (
|
|
||||||
<div className="ascribe-detail-header">
|
|
||||||
<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) } />
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let EditionSummary = React.createClass({
|
let EditionSummary = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -259,7 +160,7 @@ let EditionSummary = React.createClass({
|
|||||||
let notification = new GlobalNotificationModel(response.notification, 'success');
|
let notification = new GlobalNotificationModel(response.notification, 'success');
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
},
|
},
|
||||||
render() {
|
getStatus(){
|
||||||
let status = null;
|
let status = null;
|
||||||
if (this.props.edition.status.length > 0){
|
if (this.props.edition.status.length > 0){
|
||||||
let statusStr = this.props.edition.status.join().replace(/_/, ' ');
|
let statusStr = this.props.edition.status.join().replace(/_/, ' ');
|
||||||
@ -281,6 +182,9 @@ let EditionSummary = React.createClass({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return status;
|
||||||
|
},
|
||||||
|
getActions(){
|
||||||
let actions = null;
|
let actions = null;
|
||||||
if (this.props.edition.request_action && this.props.edition.request_action.length > 0){
|
if (this.props.edition.request_action && this.props.edition.request_action.length > 0){
|
||||||
actions = (
|
actions = (
|
||||||
@ -300,16 +204,18 @@ let EditionSummary = React.createClass({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>);
|
</Row>);
|
||||||
}
|
}
|
||||||
|
return actions;
|
||||||
|
},
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="ascribe-detail-header">
|
<div className="ascribe-detail-header">
|
||||||
<EditionDetailProperty label="EDITION"
|
<EditionDetailProperty label="EDITION"
|
||||||
value={this.props.edition.edition_number + ' of ' + this.props.edition.num_editions} />
|
value={this.props.edition.edition_number + ' of ' + this.props.edition.num_editions} />
|
||||||
<EditionDetailProperty label="ID" value={ this.props.edition.bitcoin_id } />
|
<EditionDetailProperty label="ID" value={ this.props.edition.bitcoin_id } />
|
||||||
<EditionDetailProperty label="OWNER" value={ this.props.edition.owner } />
|
<EditionDetailProperty label="OWNER" value={ this.props.edition.owner } />
|
||||||
{status}
|
{this.getStatus()}
|
||||||
<br/>
|
<br/>
|
||||||
{actions}
|
{this.getActions()}
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -318,54 +224,6 @@ let EditionSummary = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
let EditionDetailProperty = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
label: React.PropTypes.string,
|
|
||||||
value: React.PropTypes.oneOfType([
|
|
||||||
React.PropTypes.string,
|
|
||||||
React.PropTypes.element
|
|
||||||
]),
|
|
||||||
separator: React.PropTypes.string,
|
|
||||||
labelClassName: React.PropTypes.string,
|
|
||||||
valueClassName: React.PropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
separator: ':',
|
|
||||||
labelClassName: 'col-xs-5 col-sm-4 col-md-3 col-lg-3',
|
|
||||||
valueClassName: 'col-xs-7 col-sm-8 col-md-9 col-lg-9'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let value = this.props.value;
|
|
||||||
if (this.props.children){
|
|
||||||
value = (
|
|
||||||
<div className="row-same-height">
|
|
||||||
<div className="col-xs-6 col-xs-height col-bottom no-padding">
|
|
||||||
{ this.props.value }
|
|
||||||
</div>
|
|
||||||
<div className="col-xs-6 col-xs-height">
|
|
||||||
{ this.props.children }
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="row ascribe-detail-property">
|
|
||||||
<div className="row-same-height">
|
|
||||||
<div className={this.props.labelClassName + ' col-xs-height col-bottom'}>
|
|
||||||
<div>{ this.props.label + this.props.separator}</div>
|
|
||||||
</div>
|
|
||||||
<div className={this.props.valueClassName + ' col-xs-height col-bottom'}>
|
|
||||||
{value}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let EditionDetailHistoryIterator = React.createClass({
|
let EditionDetailHistoryIterator = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
history: React.PropTypes.array
|
history: React.PropTypes.array
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import EditionActions from '../actions/edition_actions';
|
import EditionActions from '../../actions/edition_actions';
|
||||||
import EditionStore from '../stores/edition_store';
|
import EditionStore from '../../stores/edition_store';
|
||||||
|
|
||||||
import Edition from './ascribe_detail/edition';
|
import Edition from './edition';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the component that implements resource/data specific functionality
|
* This is the component that implements resource/data specific functionality
|
174
js/components/ascribe_detail/further_details.js
Normal file
174
js/components/ascribe_detail/further_details.js
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Row from 'react-bootstrap/lib/Row';
|
||||||
|
import Col from 'react-bootstrap/lib/Col';
|
||||||
|
|
||||||
|
|
||||||
|
import Form from './../ascribe_forms/form';
|
||||||
|
import Property from './../ascribe_forms/property';
|
||||||
|
|
||||||
|
import PieceExtraDataForm from './../ascribe_forms/form_piece_extradata';
|
||||||
|
|
||||||
|
import ReactS3FineUploader from './../ascribe_uploader/react_s3_fine_uploader';
|
||||||
|
|
||||||
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
||||||
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
||||||
|
|
||||||
|
import apiUrls from '../../constants/api_urls';
|
||||||
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
|
import { getCookie } from '../../utils/fetch_api_utils';
|
||||||
|
|
||||||
|
let FurtherDetails = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
content: React.PropTypes.object,
|
||||||
|
handleSuccess: React.PropTypes.func
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
loading: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
showNotification(){
|
||||||
|
this.props.handleSuccess();
|
||||||
|
let notification = new GlobalNotificationModel('Details updated', 'success');
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
submitKey(key){
|
||||||
|
this.setState({
|
||||||
|
otherDataKey: key
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setIsUploadReady(isReady) {
|
||||||
|
this.setState({
|
||||||
|
isUploadReady: isReady
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
isReadyForFormSubmission(files) {
|
||||||
|
files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');
|
||||||
|
if(files.length > 0 && files[0].status === 'upload successful') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let editable = this.props.content.acl.indexOf('edit') > -1;
|
||||||
|
return (<span />);
|
||||||
|
//return (
|
||||||
|
// <Row>
|
||||||
|
// <Col md={12} className="ascribe-edition-personal-note">
|
||||||
|
// <PieceExtraDataForm
|
||||||
|
// name='artist_contact_info'
|
||||||
|
// title='Artist Contact Info'
|
||||||
|
// handleSuccess={this.showNotification}
|
||||||
|
// editable={editable}
|
||||||
|
// content={this.props.content} />
|
||||||
|
// <PieceExtraDataForm
|
||||||
|
// name='display_instructions'
|
||||||
|
// title='Display Instructions'
|
||||||
|
// handleSuccess={this.showNotification}
|
||||||
|
// editable={editable}
|
||||||
|
// content={this.props.content} />
|
||||||
|
// <PieceExtraDataForm
|
||||||
|
// name='technology_details'
|
||||||
|
// title='Technology Details'
|
||||||
|
// handleSuccess={this.showNotification}
|
||||||
|
// editable={editable}
|
||||||
|
// content={this.props.content} />
|
||||||
|
// <FileUploader
|
||||||
|
// submitKey={this.submitKey}
|
||||||
|
// setIsUploadReady={this.setIsUploadReady}
|
||||||
|
// isReadyForFormSubmission={this.isReadyForFormSubmission}
|
||||||
|
// editable={editable}
|
||||||
|
// content={this.props.content}/>
|
||||||
|
// </Col>
|
||||||
|
// </Row>
|
||||||
|
//);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let FileUploader = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
content: React.PropTypes.object,
|
||||||
|
setIsUploadReady: React.PropTypes.func,
|
||||||
|
submitKey: React.PropTypes.func,
|
||||||
|
isReadyForFormSubmission: React.PropTypes.func,
|
||||||
|
editable: React.PropTypes.bool
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// Essentially there a three cases important to the fileuploader
|
||||||
|
//
|
||||||
|
// 1. there is no other_data => do not show the fileuploader at all
|
||||||
|
// 2. there is other_data, but user has no edit rights => show fileuploader but without action buttons
|
||||||
|
// 3. both other_data and editable are defined or true => show fileuploade with all action buttons
|
||||||
|
if (!this.props.editable && !this.props.content.other_data){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Form>
|
||||||
|
<Property
|
||||||
|
label="Additional files">
|
||||||
|
<ReactS3FineUploader
|
||||||
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'otherdata',
|
||||||
|
bitcoinId: this.props.content.bitcoin_id
|
||||||
|
}}
|
||||||
|
createBlobRoutine={{
|
||||||
|
url: apiUrls.blob_otherdatas,
|
||||||
|
bitcoinId: this.props.content.bitcoin_id
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: 100000,
|
||||||
|
sizeLimit: '10000000'
|
||||||
|
}}
|
||||||
|
submitKey={this.props.submitKey}
|
||||||
|
setIsUploadReady={this.props.setIsUploadReady}
|
||||||
|
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
|
||||||
|
session={{
|
||||||
|
endpoint: AppConstants.serverUrl + 'api/blob/otherdatas/fineuploader_session/',
|
||||||
|
customHeaders: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
'pk': this.props.content.other_data ? this.props.content.other_data.id : null
|
||||||
|
},
|
||||||
|
cors: {
|
||||||
|
expected: true,
|
||||||
|
sendCredentials: true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
signature={{
|
||||||
|
endpoint: AppConstants.serverUrl + 's3/signature/',
|
||||||
|
customHeaders: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
deleteFile={{
|
||||||
|
enabled: true,
|
||||||
|
method: 'DELETE',
|
||||||
|
endpoint: AppConstants.serverUrl + 's3/delete',
|
||||||
|
customHeaders: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
areAssetsDownloadable={true}
|
||||||
|
areAssetsEditable={this.props.editable}/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FurtherDetails;
|
26
js/components/ascribe_detail/header.js
Normal file
26
js/components/ascribe_detail/header.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import EditionDetailProperty from './detail_property';
|
||||||
|
|
||||||
|
|
||||||
|
let Header = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
content: React.PropTypes.object
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var titleHtml = <div className="ascribe-detail-title">{this.props.content.title}</div>;
|
||||||
|
return (
|
||||||
|
<div className="ascribe-detail-header">
|
||||||
|
<EditionDetailProperty label="TITLE" value={titleHtml} />
|
||||||
|
<EditionDetailProperty label="BY" value={this.props.content.artist_name} />
|
||||||
|
<EditionDetailProperty label="DATE" value={ this.props.content.date_created.slice(0, 4) } />
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Header;
|
62
js/components/ascribe_detail/media_container.js
Normal file
62
js/components/ascribe_detail/media_container.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Button from 'react-bootstrap/lib/Button';
|
||||||
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||||
|
|
||||||
|
import MediaPlayer from './../ascribe_media/media_player';
|
||||||
|
|
||||||
|
import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
|
||||||
|
|
||||||
|
|
||||||
|
let MediaContainer = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
content: React.PropTypes.object
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let thumbnail = this.props.content.thumbnail;
|
||||||
|
let mimetype = this.props.content.digital_work.mime;
|
||||||
|
let embed = null;
|
||||||
|
let extraData = null;
|
||||||
|
|
||||||
|
if (this.props.content.digital_work.encoding_urls) {
|
||||||
|
extraData = this.props.content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['video', 'audio'].indexOf(mimetype) > -1){
|
||||||
|
embed = (
|
||||||
|
<CollapsibleButton
|
||||||
|
button={
|
||||||
|
<Button bsSize="xsmall" className="ascribe-margin-1px">
|
||||||
|
Embed
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
panel={
|
||||||
|
<pre className="">
|
||||||
|
{'<iframe width="560" height="315" src="http://embed.ascribe.io/content/'
|
||||||
|
+ this.props.content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<MediaPlayer mimetype={mimetype}
|
||||||
|
preview={thumbnail}
|
||||||
|
url={this.props.content.digital_work.url}
|
||||||
|
extraData={extraData} />
|
||||||
|
<p className="text-center">
|
||||||
|
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.content.digital_work.url} target="_blank">
|
||||||
|
Download <Glyphicon glyph="cloud-download"/>
|
||||||
|
</Button>
|
||||||
|
{embed}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default MediaContainer;
|
71
js/components/ascribe_detail/piece.js
Normal file
71
js/components/ascribe_detail/piece.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Row from 'react-bootstrap/lib/Row';
|
||||||
|
import Col from 'react-bootstrap/lib/Col';
|
||||||
|
|
||||||
|
import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph';
|
||||||
|
|
||||||
|
import FurtherDetails from './further_details';
|
||||||
|
//import UserActions from '../../actions/user_actions';
|
||||||
|
//import UserStore from '../../stores/user_store';
|
||||||
|
|
||||||
|
import MediaContainer from './media_container';
|
||||||
|
|
||||||
|
import Header from './header';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the component that implements display-specific functionality
|
||||||
|
*/
|
||||||
|
let Piece = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
piece: React.PropTypes.object,
|
||||||
|
loadPiece: 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}>
|
||||||
|
<MediaContainer
|
||||||
|
content={this.props.piece}/>
|
||||||
|
</Col>
|
||||||
|
<Col md={6} className="ascribe-edition-details">
|
||||||
|
<Header
|
||||||
|
content={this.props.piece}/>
|
||||||
|
<CollapsibleParagraph
|
||||||
|
title="Further Details"
|
||||||
|
show={this.props.piece.acl.indexOf('edit') > -1
|
||||||
|
|| Object.keys(this.props.piece.extra_data).length > 0
|
||||||
|
|| this.props.piece.other_data !== null}>
|
||||||
|
<FurtherDetails
|
||||||
|
handleSuccess={this.props.loadPiece}
|
||||||
|
content={this.props.piece}/>
|
||||||
|
</CollapsibleParagraph>
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Piece;
|
57
js/components/ascribe_detail/piece_container.js
Normal file
57
js/components/ascribe_detail/piece_container.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import PieceActions from '../../actions/piece_actions';
|
||||||
|
import PieceStore from '../../stores/piece_store';
|
||||||
|
|
||||||
|
import Piece from './piece';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the component that implements resource/data specific functionality
|
||||||
|
*/
|
||||||
|
let PieceContainer = React.createClass({
|
||||||
|
getInitialState() {
|
||||||
|
return PieceStore.getState();
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(state) {
|
||||||
|
this.setState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
PieceStore.listen(this.onChange);
|
||||||
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
// Every time we're leaving the piece detail page,
|
||||||
|
// just reset the piece that is saved in the piece store
|
||||||
|
// as it will otherwise display wrong/old data once the user loads
|
||||||
|
// the piece detail a second time
|
||||||
|
PieceActions.updatePiece({});
|
||||||
|
|
||||||
|
PieceStore.unlisten(this.onChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
loadPiece() {
|
||||||
|
PieceActions.fetchOne(this.props.params.pieceId);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if('title' in this.state.piece) {
|
||||||
|
return (
|
||||||
|
<Piece
|
||||||
|
piece={this.state.piece}
|
||||||
|
loadPiece={this.loadPiece}/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<p>Loading</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PieceContainer;
|
@ -91,7 +91,7 @@ let RegisterPiece = React.createClass( {
|
|||||||
this.state.orderBy,
|
this.state.orderBy,
|
||||||
this.state.orderAsc);
|
this.state.orderAsc);
|
||||||
|
|
||||||
this.transitionTo('edition', {editionId: response.piece.bitcoin_id});
|
this.transitionTo('piece', {editionId: response.piece.id});
|
||||||
},
|
},
|
||||||
|
|
||||||
getFormData(){
|
getFormData(){
|
||||||
|
@ -30,7 +30,7 @@ let apiUrls = {
|
|||||||
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',
|
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',
|
||||||
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/',
|
'ownership_unconsigns_deny': AppConstants.apiEndpoint + 'ownership/unconsigns/deny/',
|
||||||
'ownership_unconsigns_request': AppConstants.apiEndpoint + 'ownership/unconsigns/request/',
|
'ownership_unconsigns_request': AppConstants.apiEndpoint + 'ownership/unconsigns/request/',
|
||||||
'piece': AppConstants.apiEndpoint + 'pieces/${piece_id}',
|
'piece': AppConstants.apiEndpoint + 'pieces/${piece_id}/',
|
||||||
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
|
'piece_extradata': AppConstants.apiEndpoint + 'pieces/${piece_id}/extradata/',
|
||||||
'piece_first_edition_id': AppConstants.apiEndpoint + 'pieces/${piece_id}/edition_index/',
|
'piece_first_edition_id': AppConstants.apiEndpoint + 'pieces/${piece_id}/edition_index/',
|
||||||
'pieces_list': AppConstants.apiEndpoint + 'pieces/',
|
'pieces_list': AppConstants.apiEndpoint + 'pieces/',
|
||||||
|
@ -5,11 +5,10 @@ import requests from '../utils/requests';
|
|||||||
|
|
||||||
let PieceFetcher = {
|
let PieceFetcher = {
|
||||||
/**
|
/**
|
||||||
* Fetch one user from the API.
|
* Fetch a piece from the API.
|
||||||
* If no arg is supplied, load the current user
|
|
||||||
*/
|
*/
|
||||||
fetchOne() {
|
fetchOne(id) {
|
||||||
return requests.get('piece');
|
return requests.get('piece', {'piece_id': id});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ import Router from 'react-router';
|
|||||||
|
|
||||||
import AscribeApp from './components/ascribe_app';
|
import AscribeApp from './components/ascribe_app';
|
||||||
import PieceList from './components/piece_list';
|
import PieceList from './components/piece_list';
|
||||||
import EditionContainer from './components/edition_container';
|
import PieceContainer from './components/ascribe_detail/piece_container';
|
||||||
|
import EditionContainer from './components/ascribe_detail/edition_container';
|
||||||
|
|
||||||
import LoginContainer from './components/login_container';
|
import LoginContainer from './components/login_container';
|
||||||
import SignupContainer from './components/signup_container';
|
import SignupContainer from './components/signup_container';
|
||||||
@ -26,6 +27,7 @@ let routes = (
|
|||||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||||
<Route name="login" path="login" handler={LoginContainer} />
|
<Route name="login" path="login" handler={LoginContainer} />
|
||||||
<Route name="pieces" path="collection" handler={PieceList} />
|
<Route name="pieces" path="collection" handler={PieceList} />
|
||||||
|
<Route name="piece" path="pieces/:pieceId" handler={PieceContainer} />
|
||||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||||
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
<Route name="register_piece" path="register_piece" handler={RegisterPiece} />
|
||||||
|
Loading…
Reference in New Issue
Block a user