1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-26 03:06:28 +02:00

Merge branch 'AD-456-ikonotv-branded-page-for-registra' of bitbucket.org:ascribe/onion into AD-456-ikonotv-branded-page-for-registra

This commit is contained in:
Cevo 2015-09-02 14:03:02 +02:00
commit 6555c02f23
15 changed files with 342 additions and 26 deletions

View File

@ -57,7 +57,7 @@ class PieceListActions {
PieceListFetcher
.fetchRequestActions()
.then((res) => {
this.actions.updatePieceListRequestActions(res.piece_ids);
this.actions.updatePieceListRequestActions(res);
})
.catch((err) => console.logGlobal(err));
}

View File

@ -61,12 +61,13 @@ let AccordionListItemWallet = React.createClass({
},
getGlyphicon(){
if (this.props.content.requestAction && this.props.content.requestAction.length > 0) {
if ((this.props.content.request_action && this.props.content.request_action.length > 0) ||
(this.props.content.request_action_editions)){
return (
<OverlayTrigger
delay={500}
placement="left"
overlay={<Tooltip>{getLangText('You have actions pending in one of your editions')}</Tooltip>}>
overlay={<Tooltip>{getLangText('You have actions pending')}</Tooltip>}>
<Glyphicon glyph='bell' color="green"/>
</OverlayTrigger>);
}

View File

@ -36,6 +36,15 @@ let EditionContainer = React.createClass({
EditionActions.fetchOne(this.props.params.editionId);
},
// 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);
}
},
componentWillUnmount() {
// Every time we're leaving the edition detail page,
// just reset the edition that is saved in the edition store

View File

@ -18,14 +18,20 @@ let CreateContractForm = React.createClass({
getInitialState() {
return {
digitalWorkKey: null,
contractKey: null,
isUploadReady: false
};
},
getFormData(){
return {
blob: this.state.contractKey
};
},
submitKey(key) {
this.setState({
digitalWorkKey: key
contractKey: key
});
},
@ -39,6 +45,7 @@ let CreateContractForm = React.createClass({
return (
<Form
url={ApiUrls.ownership_contract}
getFormData={this.getFormData}
buttons={
<button
type="submit"
@ -87,7 +94,7 @@ let CreateContractForm = React.createClass({
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}/>
</Property>
<Property
name='contract_ name'
name='contract_name'
label={getLangText('Contract name')}>
<input
type="text"

View File

@ -6,6 +6,8 @@ import AclButton from './../ascribe_buttons/acl_button';
import ActionPanel from '../ascribe_panel/action_panel';
import Form from './form';
import PieceListActions from '../../actions/piece_list_actions';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
@ -71,12 +73,18 @@ let RequestActionForm = React.createClass({
let notification = new GlobalNotificationModel(message, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
if(this.props.handleSuccess) {
this.props.handleSuccess();
}
this.handleSuccess();
};
},
handleSuccess() {
PieceListActions.fetchPieceRequestActions();
if(this.props.handleSuccess) {
this.props.handleSuccess();
}
},
getContent() {
let pieceOrEditionStr = this.isPiece() ? getLangText('this work%s', '.') : getLangText('this edition%s', '.');
let message = this.props.requestUser + ' ' + getLangText('requests you') + ' ' + this.props.requestAction + ' ' + pieceOrEditionStr;
@ -99,7 +107,7 @@ let RequestActionForm = React.createClass({
buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px'
pieceOrEditions={this.props.pieceOrEditions}
currentUser={this.props.currentUser}
handleSuccess={this.props.handleSuccess} />
handleSuccess={this.handleSuccess} />
);
} else if(this.props.requestAction === 'loan_request') {
return (
@ -110,7 +118,7 @@ let RequestActionForm = React.createClass({
buttonAcceptClassName='inline pull-right btn-sm ascribe-margin-1px'
pieceOrEditions={this.props.pieceOrEditions}
currentUser={this.props.currentUser}
handleSuccess={this.props.handleSuccess} />
handleSuccess={this.handleSuccess} />
);
} else {
return (

View File

@ -0,0 +1,43 @@
'use strict';
import React from 'react';
let GlobalAction = React.createClass({
propTypes: {
requestActions: React.PropTypes.object
},
render() {
let pieceActions = null;
if (this.props.requestActions && this.props.requestActions.pieces){
pieceActions = this.props.requestActions.pieces.map((item) => {
return (
<div className="ascribe-global-action">
{item}
</div>);
});
}
let editionActions = null;
if (this.props.requestActions && this.props.requestActions.editions){
editionActions = Object.keys(this.props.requestActions.editions).map((pieceId) => {
return this.props.requestActions.editions[pieceId].map((item) => {
return (
<div className="ascribe-global-action">
{item}
</div>);
});
});
}
if (pieceActions || editionActions) {
return (
<div className="ascribe-global-action-wrapper">
{pieceActions}
{editionActions}
</div>);
}
return null;
}
});
export default GlobalAction;

View File

@ -3,12 +3,6 @@
import React from 'react';
import Router from 'react-router';
import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store';
import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store';
import EventActions from '../actions/event_actions';
import Nav from 'react-bootstrap/lib/Nav';
import Navbar from 'react-bootstrap/lib/Navbar';
@ -18,6 +12,15 @@ import MenuItem from 'react-bootstrap/lib/MenuItem';
import MenuItemLink from 'react-router-bootstrap/lib/MenuItemLink';
import NavItemLink from 'react-router-bootstrap/lib/NavItemLink';
import UserActions from '../actions/user_actions';
import UserStore from '../stores/user_store';
import WhitelabelActions from '../actions/whitelabel_actions';
import WhitelabelStore from '../stores/whitelabel_store';
import EventActions from '../actions/event_actions';
import HeaderNotifications from './header_notification';
import HeaderNotificationDebug from './header_notification_debug';
import NavRoutesLinks from './nav_routes_links';
@ -41,7 +44,10 @@ let Header = React.createClass({
},
getInitialState() {
return mergeOptions(WhitelabelStore.getState(), UserStore.getState());
return mergeOptions(
WhitelabelStore.getState(),
UserStore.getState()
);
},
componentDidMount() {
@ -96,11 +102,13 @@ let Header = React.createClass({
let navRoutesLinks;
if (this.state.currentUser.username){
account = (
<DropdownButton eventKey="1" title={this.state.currentUser.username}>
<DropdownButton
eventKey="1"
title={this.state.currentUser.username}>
<MenuItemLink eventKey="2" to="settings">{getLangText('Account Settings')}</MenuItemLink>
<MenuItem divider />
<MenuItemLink eventKey="3" to="logout">{getLangText('Log out')}</MenuItemLink>
</DropdownButton>
</DropdownButton>
);
navRoutesLinks = <NavRoutesLinks routes={this.props.routes} navbar right/>;
}
@ -126,6 +134,7 @@ let Header = React.createClass({
{account}
{signup}
</Nav>
<HeaderNotifications />
{navRoutesLinks}
</CollapsibleNav>
</Navbar>

View File

@ -0,0 +1,144 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import MenuItem from 'react-bootstrap/lib/MenuItem';
import Nav from 'react-bootstrap/lib/Nav';
import PieceListStore from '../stores/piece_list_store';
import { mergeOptions } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils';
let Link = Router.Link;
let HeaderNotifications = React.createClass({
getInitialState() {
return mergeOptions(
PieceListStore.getState()
);
},
componentDidMount() {
PieceListStore.listen(this.onChange);
},
componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
},
onChange(state) {
this.setState(state);
},
onSelected(event) {
/*
This is a hack to make the dropdown close after clicking on an item
The function just need to be defined
from https://github.com/react-bootstrap/react-bootstrap/issues/368:
@jvillasante - Have you tried to use onSelect with the DropdownButton?
I don't have a working example that is exactly like yours,
but I just noticed that the Dropdown closes when I've attached an event handler to OnSelect:
<DropdownButton eventKey={3} title="Admin" onSelect={ this.OnSelected } >
onSelected: function(e) {
// doesn't need to have functionality (necessarily) ... just wired up
}
Internally, a call to DropdownButton.setDropDownState(false) is made which will hide the dropdown menu.
So, you should be able to call that directly on the DropdownButton instance as well if needed.
*/
},
render() {
if (this.state.requestActions && this.state.requestActions.length > 0) {
return (
<Nav navbar right>
<DropdownButton
eventKey="1"
title={
<span>
<Glyphicon glyph='envelope' color="green"/>
<span className="notification-amount">({this.state.requestActions.length})</span>
</span>
}
className="notification-menu"
onSelect={this.onSelected}>
{this.state.requestActions.map((pieceOrEdition, i) => {
return (
<MenuItem eventKey={i + 2}>
<NotificationListItem
ref={i}
pieceOrEdition={pieceOrEdition}/>
</MenuItem>);
}
)}
</DropdownButton>
</Nav>
);
}
return null;
}
});
let NotificationListItem = React.createClass({
propTypes: {
pieceOrEdition: React.PropTypes.object
},
getLinkData() {
if(this.props.pieceOrEdition && this.props.pieceOrEdition.parent) {
return {
to: 'edition',
params: {
editionId: this.props.pieceOrEdition.bitcoin_id
}
};
} else {
return {
to: 'piece',
params: {
pieceId: this.props.pieceOrEdition.id
}
};
}
},
render() {
if (this.props.pieceOrEdition) {
return (
<Link {...this.getLinkData()}>
<div className="row notification-wrapper">
<div className="col-xs-4 clear-paddings">
<div className="thumbnail-wrapper">
<img src={this.props.pieceOrEdition.thumbnail.url_safe}/>
</div>
</div>
<div className="col-xs-8 notification-list-item-header">
<h1>{this.props.pieceOrEdition.title}</h1>
<div className="sub-header">by {this.props.pieceOrEdition.artist_name}</div>
<div className="notification-action">
{
this.props.pieceOrEdition.request_action.map((requestAction) => {
return 'Pending ' + requestAction.action + ' request';
})
}
</div>
</div>
</div>
</Link>);
}
return null;
}
});
export default HeaderNotifications;

View File

@ -15,6 +15,7 @@ import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_l
import Pagination from './ascribe_pagination/pagination';
import GlobalAction from './global_action';
import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_modal';
import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
@ -148,6 +149,10 @@ let PieceList = React.createClass({
render() {
let loadingElement = (<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />);
let AccordionListItemType = this.props.accordionListItemType;
//<GlobalAction requestActions={this.state.requestActions} />
return (
<div>
<PieceListToolbar

View File

@ -86,7 +86,6 @@ let PieceContainer = React.createClass({
loadPiece() {
PieceActions.fetchOne(this.props.params.pieceId);
this.setState(this.state);
},
render() {

View File

@ -28,6 +28,7 @@ class PieceListStore {
this.orderBy = 'artist_name';
this.orderAsc = true;
this.filterBy = {};
this.requestActions = {};
this.bindActions(PieceListActions);
}
@ -71,10 +72,8 @@ class PieceListStore {
this.pieceList = pieceList;
}
onUpdatePieceListRequestActions(requestActions) {
this.pieceList.forEach((piece) => {
piece.requestAction = requestActions.indexOf(piece.id) > -1;
});
onUpdatePieceListRequestActions(res) {
this.requestActions = res.actions;
}
onUpdatePropertyForPiece({pieceId, key, value}) {

View File

@ -166,7 +166,7 @@ $ascribe-accordion-list-item-height: 8em;
.request-action-badge {
color: $ascribe-color-green;
font-size: 1.2em;
padding: .3em;
padding: .8em;
position: absolute;
right: 0;
top: 0;

View File

@ -0,0 +1,25 @@
$break-small: 764px;
$break-medium: 991px;
$break-medium: 1200px;
.ascribe-global-action-wrapper {
position: fixed;
width: 100%;
max-width: 500px;
height:3.5em;
left:0;
right: 0;
top:0;
z-index: 2000;
display:table;
margin: 1px auto;
}
.ascribe-global-action {
text-align: center;
padding: 1em;
color: black;
border: 1px solid #cccccc;
background-color: white;
margin-top: 1px;
}

View File

@ -0,0 +1,65 @@
$break-small: 764px;
$break-medium: 991px;
$break-medium: 1200px;
.notification-wrapper {
width: 350px;
height:8em;
padding: 0.3em;
border-bottom: 1px solid #cccccc;
margin: -3px -20px;
// ToDo: Include media queries for thumbnail
.thumbnail-wrapper {
width: 7.4em;
height: 7.4em;
padding:0;
cursor: pointer;
text-align: center;
img {
max-width: 100%;
max-height: 100%;
}
&::before {
content: ' ';
display: inline-block;
vertical-align: middle; /* vertical alignment of the inline element */
height: 100%;
}
}
h1 {
margin-top: 0.3em;
margin-bottom: 0.15em;
font-size: 1.8em;
}
.sub-header{
margin-bottom: 1em;
}
.notification-action{
color: $ascribe-color-green;
}
}
.notification-menu {
.dropdown-menu {
padding: 0 !important;
li a {
padding-top: 0;
}
}
}
.notification-amount {
padding: 0.3em;
font-size: 1.2em;
}
.ascribe-global-action {
text-align: center;
padding: 1em;
color: black;
border: 1px solid #cccccc;
background-color: white;
margin-top: 1px;
}

View File

@ -22,7 +22,9 @@ $BASE_URL: '<%= BASE_URL %>';
@import 'ascribe_media_player';
@import 'ascribe_uploader';
@import 'ascribe_footer';
@import 'ascribe_global_action';
@import 'ascribe_global_notification';
@import 'ascribe_notification_list';
@import 'ascribe_piece_register';
@import 'offset_right';
@import 'ascribe_settings';