1
0
mirror of https://github.com/ascribe/onion.git synced 2025-01-03 10:25:08 +01:00

Merge remote-tracking branch 'origin/AD-368-harmonize-functionality-of-ascrib' into AD-527-transition-to-login-form-if-user-

Conflicts:
	js/components/register_piece.js
This commit is contained in:
Tim Daubenschütz 2015-07-02 11:50:39 +02:00
commit 1f6d20fb15
18 changed files with 131 additions and 38 deletions

View File

@ -8,23 +8,34 @@ import PieceListFetcher from '../fetchers/piece_list_fetcher';
class PieceListActions { class PieceListActions {
constructor() { constructor() {
this.generateActions( this.generateActions(
'updatePieceList' 'updatePieceList',
'updatePieceListRequestActions'
); );
} }
fetchPieceList(page, pageSize, search, orderBy, orderAsc) { fetchPieceList(page, pageSize, search, orderBy, orderAsc) {
PieceListFetcher return new Promise((resolve, reject) => {
.fetch(page, pageSize, search, orderBy, orderAsc) PieceListFetcher
.then((res) => { .fetch(page, pageSize, search, orderBy, orderAsc)
this.actions.updatePieceList({ .then((res) => {
page, this.actions.updatePieceList({
pageSize, page,
search, pageSize,
orderBy, search,
orderAsc, orderBy,
'pieceList': res.pieces, orderAsc,
'pieceListCount': res.count 'pieceList': res.pieces,
'pieceListCount': res.count
});
resolve();
}); });
});
}
fetchPieceRequestActions() {
PieceListFetcher
.fetchRequestActions()
.then((res) => {
this.actions.updatePieceListRequestActions(res.piece_ids);
}); });
} }

View File

@ -1,30 +1,64 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import Router from 'react-router';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import requests from '../../utils/requests';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let AccordionListItem = React.createClass({ let AccordionListItem = React.createClass({
mixins: [Router.Navigation],
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
content: React.PropTypes.object, content: React.PropTypes.object,
children: React.PropTypes.object children: React.PropTypes.object
}, },
handleClick(event){
requests.get('piece_first_edition_id', {'piece_id': this.props.content.id})
.then((res) => this.transitionTo('edition', {editionId: res.bitcoin_id}));
console.log(event.target);
},
getGlyphicon(){
if (this.props.content.requestAction){
return (
<OverlayTrigger delay={500} placement="left"
overlay={<Tooltip>You have actions pending in one of your editions</Tooltip>}>
<Glyphicon glyph='bell' />
</OverlayTrigger>);
}
return null;
},
render() { render() {
return ( return (
<div className="row"> <div className="row">
<div className={this.props.className}> <div className={this.props.className}>
<div className="wrapper"> <div className="wrapper">
<div className="col-xs-5 col-sm-5 col-md-4 col-lg-4 thumbnail-wrapper"> <div className="col-xs-4 col-sm-3 col-md-2 col-lg-2 clear-paddings">
<img src={this.props.content.thumbnail} /> <div className="thumbnail-wrapper" onClick={this.handleClick}>
<img src={this.props.content.thumbnail} />
</div>
</div> </div>
<div className="col-xs-7 col-sm-7 col-md-7 col-lg-7 col-md-offset-1 col-lg-offset-1"> <div className="col-xs-8 col-sm-9 col-md-9 col-lg-9 col-md-offset-1 col-lg-offset-1 accordion-list-item-header">
<h1>{this.props.content.title}</h1> <h1 onClick={this.handleClick}>{this.props.content.title}</h1>
<h3>{getLangText('by %s', this.props.content.artist_name)}</h3> <h3>{getLangText('by %s', this.props.content.artist_name)}</h3>
<h3>{this.props.content.date_created.split('-')[0]}</h3> <div>
<span>{this.props.content.date_created.split('-')[0]}</span>
<a href={this.props.content.license_type.url} target="_blank" className="pull-right">
{this.props.content.license_type.code} license
</a>
</div>
</div> </div>
<span style={{'clear': 'both'}}></span> <span style={{'clear': 'both'}}></span>
<div className="request-action-batch">
{this.getGlyphicon()}
</div>
</div> </div>
</div> </div>
{this.props.children} {this.props.children}

View File

@ -139,8 +139,12 @@ let AccordionListItemTableEditions = React.createClass({
), ),
new ColumnModel( new ColumnModel(
(item) => { (item) => {
let content = item.acl;
if (item.request_action){
content = [item.request_action + ' request'];
}
return { return {
'content': item.acl 'content': content
}; }, }; },
'acl', 'acl',
getLangText('Actions'), getLangText('Actions'),

View File

@ -42,11 +42,11 @@ let DeleteButton = React.createClass({
if (availableAcls.indexOf('delete') > -1) { if (availableAcls.indexOf('delete') > -1) {
content = <EditionDeleteForm editions={ this.props.editions }/>; content = <EditionDeleteForm editions={ this.props.editions }/>;
btnDelete = <Button bsStyle="danger" bsSize="small">DELETE</Button>; btnDelete = <Button bsStyle="danger" className="btn-delete" bsSize="small">DELETE</Button>;
} }
else if (availableAcls.indexOf('del_from_collection') > -1){ else if (availableAcls.indexOf('del_from_collection') > -1){
content = <EditionRemoveFromCollectionForm editions={ this.props.editions }/>; content = <EditionRemoveFromCollectionForm editions={ this.props.editions }/>;
btnDelete = <Button bsStyle="danger" bsSize="small">REMOVE FROM COLLECTION</Button>; btnDelete = <Button bsStyle="danger" className="btn-delete" bsSize="small">REMOVE FROM COLLECTION</Button>;
} }
else{ else{
return null; return null;

View File

@ -54,10 +54,10 @@ let RequestActionForm = React.createClass({
let buttons = ( let buttons = (
<span> <span>
<span> <span>
<div id="request_accept" onClick={this.handleRequest} className='btn btn-default btn-sm'>ACCEPT</div> <div id="request_accept" onClick={this.handleRequest} className='btn btn-default btn-sm ascribe-margin-1px'>ACCEPT</div>
</span> </span>
<span> <span>
<div id="request_deny" onClick={this.handleRequest} className='btn btn-default btn-sm'>REJECT</div> <div id="request_deny" onClick={this.handleRequest} className='btn btn-danger btn-delete btn-sm ascribe-margin-1px'>REJECT</div>
</span> </span>
</span> </span>
); );

View File

@ -21,7 +21,7 @@ let PieceListToolbar = React.createClass({
}, },
render() { render() {
let searchIcon = <Glyphicon glyph='search' />; let searchIcon = <Glyphicon glyph='search' className="filter-glyph"/>;
return ( return (
<div className={this.props.className}> <div className={this.props.className}>

View File

@ -8,7 +8,7 @@ import MenuItem from 'react-bootstrap/lib/MenuItem';
let PieceListToolbarFilterWidgetFilter = React.createClass({ let PieceListToolbarFilterWidgetFilter = React.createClass({
render() { render() {
let filterIcon = <Glyphicon glyph='filter' />; let filterIcon = <Glyphicon glyph='filter' className="filter-glyph"/>;
return ( return (
<DropdownButton title={filterIcon}> <DropdownButton title={filterIcon}>

View File

@ -9,7 +9,7 @@ let TableItemAclFiltered = React.createClass({
}, },
render() { render() {
var availableAcls = ['consign', 'loan', 'transfer', 'view']; var availableAcls = ['consign', 'loan', 'transfer', 'view', 'consign request', 'unconsign request', 'loan request'];
let filteredAcls = this.props.content.filter((v) => { let filteredAcls = this.props.content.filter((v) => {
return availableAcls.indexOf(v) > -1; return availableAcls.indexOf(v) > -1;

View File

@ -80,6 +80,7 @@ let Header = React.createClass({
render() { render() {
let account = null; let account = null;
let signup = null; let signup = null;
let collection = null;
if (this.state.currentUser.username){ if (this.state.currentUser.username){
account = ( account = (
<DropdownButton eventKey="1" title={this.state.currentUser.username}> <DropdownButton eventKey="1" title={this.state.currentUser.username}>
@ -91,6 +92,7 @@ let Header = React.createClass({
<MenuItem eventKey="4" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem> <MenuItem eventKey="4" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
</DropdownButton> </DropdownButton>
); );
collection = <NavItemLink to="pieces">COLLECTION</NavItemLink>;
} }
else { else {
account = <NavItemLink to="login">LOGIN</NavItemLink>; account = <NavItemLink to="login">LOGIN</NavItemLink>;
@ -101,11 +103,14 @@ let Header = React.createClass({
<div> <div>
<Navbar <Navbar
brand={ brand={
<Link className="navbar-brand" to="pieces" path="/?page=1"> <Link className="navbar-brand" to="pieces">
{this.getLogo()} {this.getLogo()}
</Link>} </Link>}
toggleNavKey={0}> toggleNavKey={0}>
<CollapsibleNav eventKey={0}> <CollapsibleNav eventKey={0}>
<Nav navbar left>
{collection}
</Nav>
<Nav navbar right> <Nav navbar right>
{account} {account}
{signup} {signup}

View File

@ -33,7 +33,8 @@ let PieceList = React.createClass({
let page = this.props.query.page || 1; let page = this.props.query.page || 1;
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
if (this.state.pieceList.length === 0){ if (this.state.pieceList.length === 0){
PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc); PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, this.state.orderBy, this.state.orderAsc)
.then(PieceListActions.fetchPieceRequestActions());
} }
}, },

View File

@ -58,15 +58,15 @@ let RegisterPiece = React.createClass( {
this.setState(state); this.setState(state);
}, },
handleSuccess(){ handleSuccess(response){
let notification = new GlobalNotificationModel('Piece registration successful', 'success', 10000); let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
// once the user was able to register a piece successfully, we need to make sure to keep // once the user was able to register a piece successfully, we need to make sure to keep
// the piece list up to date // the piece list up to date
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.searchTerm, this.state.orderBy, this.state.orderAsc); PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.searchTerm, this.state.orderBy, this.state.orderAsc);
this.transitionTo('pieces'); this.transitionTo('edition', {editionId: response.edition.bitcoin_id});
}, },
getFormData(){ getFormData(){
@ -111,7 +111,7 @@ let RegisterPiece = React.createClass( {
onChange={this.onLicenseChange} onChange={this.onLicenseChange}
footer={ footer={
<a className="pull-right" href={this.state.licenses[this.state.selectedLicense].url} target="_blank"> <a className="pull-right" href={this.state.licenses[this.state.selectedLicense].url} target="_blank">
Learn more about this license Learn more
</a>}> </a>}>
<select name="license"> <select name="license">
{this.state.licenses.map((license, i) => { {this.state.licenses.map((license, i) => {

View File

@ -227,7 +227,7 @@ let APISettings = React.createClass({
name={app.name} name={app.name}
label={app.name}> label={app.name}>
<div className="row-same-height"> <div className="row-same-height">
<div className="col-xs-6 col-xs-height col-middle"> <div className="no-padding col-xs-6 col-xs-height col-middle">
{'Bearer ' + app.bearer_token.token} {'Bearer ' + app.bearer_token.token}
</div> </div>
<div className="col-xs-6 col-xs-height"> <div className="col-xs-6 col-xs-height">

View File

@ -31,7 +31,9 @@ let apiUrls = {
'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/',
'pieces_list': AppConstants.apiEndpoint + 'pieces/', 'pieces_list': AppConstants.apiEndpoint + 'pieces/',
'pieces_list_request_actions': AppConstants.apiEndpoint + 'pieces/request_actions/',
'user': AppConstants.apiEndpoint + 'users/', 'user': AppConstants.apiEndpoint + 'users/',
'users_login': AppConstants.apiEndpoint + 'users/login/', 'users_login': AppConstants.apiEndpoint + 'users/login/',
'users_logout': AppConstants.apiEndpoint + 'users/logout/', 'users_logout': AppConstants.apiEndpoint + 'users/logout/',

View File

@ -12,6 +12,10 @@ let PieceListFetcher = {
fetch(page, pageSize, search, orderBy, orderAsc) { fetch(page, pageSize, search, orderBy, orderAsc) {
let ordering = generateOrderingQueryParams(orderBy, orderAsc); let ordering = generateOrderingQueryParams(orderBy, orderAsc);
return requests.get('pieces_list', { page, pageSize, search, ordering }); return requests.get('pieces_list', { page, pageSize, search, ordering });
},
fetchRequestActions() {
return requests.get('pieces_list_request_actions');
} }
}; };

View File

@ -85,6 +85,11 @@ class PieceListStore {
this.pieceList = pieceList; this.pieceList = pieceList;
} }
onUpdatePieceListRequestActions(requestActions) {
this.pieceList.forEach((piece) => {
piece.requestAction = requestActions.indexOf(piece.id) > -1;
});
}
} }
export default alt.createStore(PieceListStore, 'PieceListStore'); export default alt.createStore(PieceListStore, 'PieceListStore');

View File

@ -24,6 +24,7 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
width: 110px; width: 110px;
height: 110px; height: 110px;
padding:0; padding:0;
cursor: pointer;
img { img {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
@ -38,10 +39,14 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
h1 { h1 {
margin-top: .3em; margin-top: .3em;
font-size: 2.25em; font-size: 2.25em;
cursor: pointer;
} }
h3 { h3 {
font-size: 1.1em; font-size: 1.1em;
margin: .7em 0 0 0; margin: .2em 0 0 0;
}
a {
color: #666;
} }
} }
} }
@ -84,4 +89,13 @@ span.ascribe-accordion-list-table-toggle {
font-size:.85em; font-size:.85em;
text-align: center; text-align: center;
} }
}
.request-action-batch {
position: absolute;
top: 0px;
right: 0px;
color: #666;
font-size: 1.2em;
padding: 0.3em;
} }

View File

@ -29,6 +29,10 @@ body {
border-radius: 0; border-radius: 0;
} }
html {
overflow-y: scroll;
}
.hidden { .hidden {
display: none; display: none;
} }
@ -92,12 +96,21 @@ body {
vertical-align: middle; vertical-align: middle;
float: none; float: none;
} }
.filter-glyph{
color: white;
}
.no-margin { .no-margin {
margin-right: 0; margin-right: 0;
margin-left: 0; margin-left: 0;
} }
.btn-delete{
background-color: rgba(0,0,0,0);
color: #888;
border: 1px solid rgba(0,0,0,0);
&:hover{
border: 1px solid $ascribe-brand-danger;
}
}
.btn-ascribe, .btn-ascribe-inv { .btn-ascribe, .btn-ascribe-inv {
border: 1px solid #444; border: 1px solid #444;
line-height: 2em; line-height: 2em;
@ -243,7 +256,7 @@ body {
vertical-align: bottom; vertical-align: bottom;
} }
.ascribe-button-list button { .ascribe-button-list button, .ascribe-margin-1px {
margin-right: 1px; margin-right: 1px;
margin-top: 1px; margin-top: 1px;
} }

View File

@ -186,7 +186,7 @@ $input-bg: #fff !default;
$input-bg-disabled: $gray-lighter !default; $input-bg-disabled: $gray-lighter !default;
//** Text color for `<input>`s //** Text color for `<input>`s
$input-color: white !default; $input-color: $gray !default;
//** `<input>` border color //** `<input>` border color
$input-border: #ccc !default; $input-border: #ccc !default;