diff --git a/js/components/ascribe_collapsible/collapsible_paragraph.js b/js/components/ascribe_collapsible/collapsible_paragraph.js index e04e82c2..8b3b3cf4 100644 --- a/js/components/ascribe_collapsible/collapsible_paragraph.js +++ b/js/components/ascribe_collapsible/collapsible_paragraph.js @@ -23,7 +23,7 @@ const CollapsibleParagraph = React.createClass({ getInitialState() { return { - expanded: false + expanded: this.props.defaultExpanded }; }, diff --git a/js/components/ascribe_detail/detail_property.js b/js/components/ascribe_detail/detail_property.js index f220fc98..991226f9 100644 --- a/js/components/ascribe_detail/detail_property.js +++ b/js/components/ascribe_detail/detail_property.js @@ -18,8 +18,8 @@ let DetailProperty = React.createClass({ getDefaultProps() { return { separator: ':', - labelClassName: 'col-xs-3 col-sm-3 col-md-2 col-lg-2', - valueClassName: 'col-xs-9 col-sm-9 col-md-10 col-lg-10' + labelClassName: 'col-xs-3 col-sm-3 col-md-2 col-lg-2 col-xs-height col-bottom ascribe-detail-property-label', + valueClassName: 'col-xs-9 col-sm-9 col-md-10 col-lg-10 col-xs-height col-bottom ascribe-detail-property-value' }; }, @@ -52,11 +52,11 @@ let DetailProperty = React.createClass({ return (
-
- { this.props.label + this.props.separator} +
+ { this.props.label } { this.props.separator}
{value}
diff --git a/js/components/ascribe_detail/media_container.js b/js/components/ascribe_detail/media_container.js index 529817c3..6ac2f745 100644 --- a/js/components/ascribe_detail/media_container.js +++ b/js/components/ascribe_detail/media_container.js @@ -19,7 +19,33 @@ const EMBED_IFRAME_HEIGHT = { let MediaContainer = React.createClass({ propTypes: { - content: React.PropTypes.object + content: React.PropTypes.object, + refreshObject: React.PropTypes.func + }, + + getInitialState() { + return {timerId: null}; + }, + + componentDidMount() { + if (!this.props.content.digital_work) { + return; + } + let isEncoding = this.props.content.digital_work.isEncoding; + if (this.props.content.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { + let timerId = window.setInterval(this.props.refreshObject, 10000); + this.setState({timerId: timerId}); + } + }, + + componentWillUpdate() { + if (this.props.content.digital_work.isEncoding === 100) { + window.clearInterval(this.state.timerId); + } + }, + + componentWillUnmount() { + window.clearInterval(this.state.timerId); }, render() { diff --git a/js/components/ascribe_detail/piece.js b/js/components/ascribe_detail/piece.js index 3fabb055..ed312f5f 100644 --- a/js/components/ascribe_detail/piece.js +++ b/js/components/ascribe_detail/piece.js @@ -1,38 +1,14 @@ '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 DetailProperty from './detail_property'; - -import UserActions from '../../actions/user_actions'; -import UserStore from '../../stores/user_store'; - -import PieceListActions from '../../actions/piece_list_actions'; -import PieceListStore from '../../stores/piece_list_store'; - -import EditionListActions from '../../actions/edition_list_actions'; - import PieceActions from '../../actions/piece_actions'; import MediaContainer from './media_container'; -import EditionDetailProperty from './detail_property'; - -import AclButtonList from './../ascribe_buttons/acl_button_list'; -import CreateEditionsForm from '../ascribe_forms/create_editions_form'; -import CreateEditionsButton from '../ascribe_buttons/create_editions_button'; -import DeleteButton from '../ascribe_buttons/delete_button'; - -import GlobalNotificationModel from '../../models/global_notification_model'; -import GlobalNotificationActions from '../../actions/global_notification_actions'; - -import { getLangText } from '../../utils/lang_utils'; -import { mergeOptions } from '../../utils/general_utils'; - /** * This is the component that implements display-specific functionality @@ -40,97 +16,16 @@ import { mergeOptions } from '../../utils/general_utils'; let Piece = React.createClass({ propTypes: { piece: React.PropTypes.object, + header: React.PropTypes.object, + subheader: React.PropTypes.object, + buttons: React.PropTypes.object, loadPiece: React.PropTypes.func, children: React.PropTypes.object }, - mixins: [Router.Navigation], - getInitialState() { - return mergeOptions( - UserStore.getState(), - PieceListStore.getState(), - { - showCreateEditionsDialog: false - } - ); - }, - - componentDidMount() { - UserStore.listen(this.onChange); - PieceListStore.listen(this.onChange); - UserActions.fetchCurrentUser(); - }, - - componentWillUnmount() { - UserStore.unlisten(this.onChange); - PieceListStore.unlisten(this.onChange); - }, - - onChange(state) { - this.setState(state); - }, - - toggleCreateEditionsDialog() { - this.setState({ - showCreateEditionsDialog: !this.state.showCreateEditionsDialog - }); - }, - - handleEditionCreationSuccess() { - PieceActions.updateProperty({key: 'num_editions', value: 0}); - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, this.state.filterBy); - this.toggleCreateEditionsDialog(); - }, - - handleDeleteSuccess(response) { - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, this.state.filterBy); - - // since we're deleting a piece, we just need to close - // all editions dialogs and not reload them - EditionListActions.closeAllEditionLists(); - EditionListActions.clearAllEditionSelections(); - - let notification = new GlobalNotificationModel(response.notification, 'success'); - GlobalNotificationActions.appendGlobalNotification(notification); - - this.transitionTo('pieces'); - }, - - getCreateEditionsDialog() { - if(this.props.piece.num_editions < 1 && this.state.showCreateEditionsDialog) { - return ( -
- -
-
- ); - } else { - return (
); - } - }, - - handlePollingSuccess(pieceId, numEditions) { - - // we need to refresh the num_editions property of the actual piece we're looking at - PieceActions.updateProperty({ - key: 'num_editions', - value: numEditions - }); - - // as well as its representation in the collection - // btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion - // list item also uses the firstEdition property which we can only get from the server in that case. - // Therefore we need to at least refetch the changed piece from the server or on our case simply all - PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, this.state.filterBy); - - let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000); - GlobalNotificationActions.appendGlobalNotification(notification); + updateObject() { + return PieceActions.fetchOne(this.props.piece.id); }, render() { @@ -138,38 +33,14 @@ let Piece = React.createClass({ -
-

{this.props.piece.title}

-
- - - {this.props.piece.num_editions > 0 ? : null} -
-
-
- -
+ {this.props.header} + {this.props.subheader} + {this.props.buttons} - - - - - - {this.getCreateEditionsDialog()} {this.props.children} diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index 8e3a5750..8625f7a0 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -1,37 +1,59 @@ 'use strict'; import React from 'react'; +import Router from 'react-router'; import PieceActions from '../../actions/piece_actions'; import PieceStore from '../../stores/piece_store'; +import PieceListActions from '../../actions/piece_list_actions'; +import PieceListStore from '../../stores/piece_list_store'; + +import UserActions from '../../actions/user_actions'; +import UserStore from '../../stores/user_store'; + +import EditionListActions from '../../actions/edition_list_actions'; + import Piece from './piece'; import CollapsibleParagraph from './../ascribe_collapsible/collapsible_paragraph'; import FurtherDetails from './further_details'; +import DetailProperty from './detail_property'; + +import AclButtonList from './../ascribe_buttons/acl_button_list'; +import CreateEditionsForm from '../ascribe_forms/create_editions_form'; +import CreateEditionsButton from '../ascribe_buttons/create_editions_button'; +import DeleteButton from '../ascribe_buttons/delete_button'; + +import GlobalNotificationModel from '../../models/global_notification_model'; +import GlobalNotificationActions from '../../actions/global_notification_actions'; + import AppConstants from '../../constants/application_constants'; +import { mergeOptions } from '../../utils/general_utils'; +import { getLangText } from '../../utils/lang_utils'; /** * This is the component that implements resource/data specific functionality */ let PieceContainer = React.createClass({ - getInitialState() { - return PieceStore.getState(); - }, - onChange(state) { - this.setState(state); - if (!state.piece.digital_work) { - return; - } - let isEncoding = state.piece.digital_work.isEncoding; - if (state.piece.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { - let timerId = window.setInterval(() => PieceActions.fetchOne(this.props.params.pieceId), 10000); - this.setState({timerId: timerId}); - } + mixins: [Router.Navigation], + + getInitialState() { + return mergeOptions( + UserStore.getState(), + PieceListStore.getState(), + PieceStore.getState(), + { + showCreateEditionsDialog: false + } + ); }, componentDidMount() { + UserStore.listen(this.onChange); + PieceListStore.listen(this.onChange); + UserActions.fetchCurrentUser(); PieceStore.listen(this.onChange); PieceActions.fetchOne(this.props.params.pieceId); }, @@ -42,21 +64,121 @@ let PieceContainer = React.createClass({ // as it will otherwise display wrong/old data once the user loads // the piece detail a second time PieceActions.updatePiece({}); - window.clearInterval(this.state.timerId); PieceStore.unlisten(this.onChange); + UserStore.unlisten(this.onChange); + PieceListStore.unlisten(this.onChange); }, + onChange(state) { + this.setState(state); + }, loadPiece() { PieceActions.fetchOne(this.props.params.pieceId); }, + + toggleCreateEditionsDialog() { + this.setState({ + showCreateEditionsDialog: !this.state.showCreateEditionsDialog + }); + }, + + handleEditionCreationSuccess() { + PieceActions.updateProperty({key: 'num_editions', value: 0}); + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); + this.toggleCreateEditionsDialog(); + }, + + handleDeleteSuccess(response) { + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); + + // since we're deleting a piece, we just need to close + // all editions dialogs and not reload them + EditionListActions.closeAllEditionLists(); + EditionListActions.clearAllEditionSelections(); + + let notification = new GlobalNotificationModel(response.notification, 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + + this.transitionTo('pieces'); + }, + + getCreateEditionsDialog() { + if(this.state.piece.num_editions < 1 && this.state.showCreateEditionsDialog) { + return ( +
+ +
+
+ ); + } else { + return (
); + } + }, + + handlePollingSuccess(pieceId, numEditions) { + + // we need to refresh the num_editions property of the actual piece we're looking at + PieceActions.updateProperty({ + key: 'num_editions', + value: numEditions + }); + + // as well as its representation in the collection + // btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion + // list item also uses the firstEdition property which we can only get from the server in that case. + // Therefore we need to at least refetch the changed piece from the server or on our case simply all + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy); + + let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000); + GlobalNotificationActions.appendGlobalNotification(notification); + }, + render() { if('title' in this.state.piece) { return ( + loadPiece={this.loadPiece} + header={ +
+

{this.state.piece.title}

+
+ + + {this.state.piece.num_editions > 0 ? : null} +
+
+ } + subheader={ +
+ +
+ } + buttons={ + + + + + }> + {this.getCreateEditionsDialog()} + + + +
); + } + else { + // jury and no rating yet + return ( +
+ + Submit your rating + +
+ ); + } + } + // participant + return ( +
+ + + +
+ ); + }, + render() { return ( @@ -68,22 +118,11 @@ let AccordionListItemWallet = React.createClass({
{this.props.content.date_created.split('-')[0]}
} - buttons={ -
- - - -
} - > + buttons={this.getPrizeButtons()}> {this.props.children} ); } }); -export default AccordionListItemWallet; +export default AccordionListItemPrize; diff --git a/js/components/whitelabel/prize/components/ascribe_detail/piece_container.js b/js/components/whitelabel/prize/components/ascribe_detail/piece_container.js index b6363868..a455b1a4 100644 --- a/js/components/whitelabel/prize/components/ascribe_detail/piece_container.js +++ b/js/components/whitelabel/prize/components/ascribe_detail/piece_container.js @@ -7,6 +7,9 @@ import StarRating from 'react-star-rating'; import PieceActions from '../../../../../actions/piece_actions'; import PieceStore from '../../../../../stores/piece_store'; +import PieceListStore from '../../../../../stores/piece_list_store'; +import PieceListActions from '../../../../../actions/piece_list_actions'; + import PrizeRatingActions from '../../actions/prize_rating_actions'; import PrizeRatingStore from '../../stores/prize_rating_store'; @@ -19,6 +22,10 @@ import Property from '../../../../../components/ascribe_forms/property'; import InputTextAreaToggable from '../../../../../components/ascribe_forms/input_textarea_toggable'; import CollapsibleParagraph from '../../../../../components/ascribe_collapsible/collapsible_paragraph'; +import DetailProperty from '../../../../ascribe_detail/detail_property'; + +import { mergeOptions } from '../../../../../utils/general_utils'; +import { getLangText } from '../../../../../utils/lang_utils'; /** * This is the component that implements resource/data specific functionality */ @@ -27,10 +34,6 @@ let PieceContainer = React.createClass({ return PieceStore.getState(); }, - onChange(state) { - this.setState(state); - }, - componentDidMount() { PieceStore.listen(this.onChange); PieceActions.fetchOne(this.props.params.pieceId); @@ -42,10 +45,12 @@ let PieceContainer = React.createClass({ // as it will otherwise display wrong/old data once the user loads // the piece detail a second time PieceActions.updatePiece({}); - PieceStore.unlisten(this.onChange); }, + onChange(state) { + this.setState(state); + }, loadPiece() { PieceActions.fetchOne(this.props.params.pieceId); @@ -56,7 +61,19 @@ let PieceContainer = React.createClass({ return ( + loadPiece={this.loadPiece} + header={ +
+

{this.state.piece.title}

+
+ + +
+
+ } + subheader={ + + }>
); @@ -70,23 +87,22 @@ let PieceContainer = React.createClass({ } }); - -let PrizePieceDetails = React.createClass({ +let PrizePieceRatings = React.createClass({ propTypes: { piece: React.PropTypes.object }, getInitialState() { - return PrizeRatingStore.getState(); - }, - - onChange(state) { - this.setState(state); + return mergeOptions( + PieceListStore.getState(), + PrizeRatingStore.getState() + ); }, componentDidMount() { PrizeRatingStore.listen(this.onChange); PrizeRatingActions.fetchOne(this.props.piece.id); + PieceListStore.listen(this.onChange); }, componentWillUnmount() { @@ -96,11 +112,52 @@ let PrizePieceDetails = React.createClass({ // the piece detail a second time PrizeRatingActions.updateRating({}); PrizeRatingStore.unlisten(this.onChange); + PieceListStore.unlisten(this.onChange); + }, + + onChange(state) { + this.setState(state); + if (this.refs.rating) { + this.refs.rating.state.ratingCache = { + pos: this.refs.rating.state.pos, + rating: this.state.currentRating, + caption: this.refs.rating.props.caption, + name: this.refs.rating.props.name + }; + } }, onRatingClick(event, args) { event.preventDefault(); - PrizeRatingActions.createRating(this.props.piece.id, args.rating); + PrizeRatingActions.createRating(this.props.piece.id, args.rating).then( + PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, + this.state.orderBy, this.state.orderAsc, this.state.filterBy) + ); + }, + render(){ + return ( + YOUR VOTE + } + value={ + } + />); + } +}); + +let PrizePieceDetails = React.createClass({ + propTypes: { + piece: React.PropTypes.object }, render() { diff --git a/sass/main.scss b/sass/main.scss index c83379b3..a81f303f 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -387,4 +387,8 @@ hr { .rating-container .rating-stars { width: 25px; color: #000; +} + +.react-rating-caption { + font-size: 1em; } \ No newline at end of file