1
0
mirror of https://github.com/ascribe/onion.git synced 2025-01-05 11:25:09 +01:00

Merge branch 'master' into AD-410-finish-implementing-piecelist-pag

This commit is contained in:
Tim Daubenschütz 2015-06-04 13:18:32 +02:00
commit aaf393d48a
18 changed files with 196 additions and 52 deletions

View File

@ -11,13 +11,15 @@ class EditionListActions {
); );
} }
fetchEditionList(pieceId) { fetchEditionList(pieceId, orderBy, orderAsc) {
EditionListFetcher EditionListFetcher
.fetch(pieceId) .fetch(pieceId, orderBy, orderAsc)
.then((res) => { .then((res) => {
this.actions.updateEditionList({ this.actions.updateEditionList({
'editionListOfPiece': res.editions, 'editionListOfPiece': res.editions,
pieceId pieceId,
orderBy,
orderAsc
}); });
}) })
.catch((err) => { .catch((err) => {

View File

@ -14,6 +14,26 @@ import alt from './alt';
import fetch from './utils/fetch'; import fetch from './utils/fetch';
import AlertDismissable from './components/ascribe_forms/alert'; import AlertDismissable from './components/ascribe_forms/alert';
/*
Taken from
http://stackoverflow.com/questions/30613447/how-to-debug-reactjss-setstate?noredirect=1#comment49301874_30613447
<remove this in production>
*/
var warn = console.warn;
console.warn = function(warning) {
if (/(setState)/.test(warning)) {
throw new Error(warning);
}
warn.apply(console, arguments);
};
/*
</remove this in production>
*/
fetch.defaults({ fetch.defaults({
urlMap: ApiUrls, urlMap: ApiUrls,

View File

@ -1,9 +1,12 @@
import React from 'react'; import React from 'react';
import Router from 'react-router';
import AccordionListItemTable from './accordion_list_item_table'; import AccordionListItemTable from './accordion_list_item_table';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Link = Router.Link;
let AccordionListItem = React.createClass({ let AccordionListItem = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,

View File

@ -14,7 +14,10 @@ let AccordionListItemTable = React.createClass({
itemList: React.PropTypes.array, itemList: React.PropTypes.array,
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
numOfTableItems: React.PropTypes.number, numOfTableItems: React.PropTypes.number,
show: React.PropTypes.bool show: React.PropTypes.bool,
changeOrder: React.PropTypes.func,
orderBy: React.PropTypes.string,
orderAsc: React.PropTypes.bool
}, },
render() { render() {
@ -23,7 +26,10 @@ let AccordionListItemTable = React.createClass({
<div className={this.props.className}> <div className={this.props.className}>
<Table <Table
columnList={this.props.columnList} columnList={this.props.columnList}
itemList={this.props.itemList}> itemList={this.props.itemList}
changeOrder={this.props.changeOrder}
orderBy={this.props.orderBy}
orderAsc={this.props.orderAsc}>
{this.props.itemList.map((item, i) => { {this.props.itemList.map((item, i) => {
return ( return (
<TableItem <TableItem

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import Router from 'react-router';
import EditionListStore from '../../stores/edition_list_store'; import EditionListStore from '../../stores/edition_list_store';
import EditionListActions from '../../actions/edition_list_actions'; import EditionListActions from '../../actions/edition_list_actions';
@ -7,6 +8,7 @@ import PieceListActions from '../../actions/piece_list_actions';
import AccordionListItemTable from './accordion_list_item_table'; import AccordionListItemTable from './accordion_list_item_table';
import AccordionListItemTableToggle from './accordion_list_item_table_toggle'; import AccordionListItemTableToggle from './accordion_list_item_table_toggle';
import AccordionListItemTableSelectAllEditionsToggle from './accordion_list_item_table_select_all_editions_toggle';
import TableColumnContentModel from '../../models/table_column_content_model'; import TableColumnContentModel from '../../models/table_column_content_model';
@ -17,6 +19,8 @@ import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let Link = Router.Link;
let AccordionListItemTableEditions = React.createClass({ let AccordionListItemTableEditions = React.createClass({
propTypes: { propTypes: {
@ -38,7 +42,7 @@ let AccordionListItemTableEditions = React.createClass({
EditionListStore.listen(this.onChange); EditionListStore.listen(this.onChange);
}, },
componentDidUnmount() { componentWillUnmount() {
EditionListStore.unlisten(this.onChange); EditionListStore.unlisten(this.onChange);
}, },
@ -46,12 +50,40 @@ let AccordionListItemTableEditions = React.createClass({
EditionListActions.selectEdition({pieceId, editionId}); EditionListActions.selectEdition({pieceId, editionId});
}, },
selectAllItems() {
this.state.editionList[this.props.parentId]
.forEach((edition) => {
this.selectItem(this.props.parentId, edition.id);
});
},
filterSelectedEditions() {
let selectedEditions = this.state.editionList[this.props.parentId]
.filter((edition) => edition.selected);
return selectedEditions;
},
toggleTable() { toggleTable() {
PieceListActions.showEditionList(this.props.parentId); PieceListActions.showEditionList(this.props.parentId);
EditionListActions.fetchEditionList(this.props.parentId); EditionListActions.fetchEditionList(this.props.parentId, this.state.orderBy, this.state.orderAsc);
},
changeEditionListOrder(orderBy, orderAsc) {
EditionListActions.fetchEditionList(this.props.parentId, orderBy, orderAsc);
}, },
render() { render() {
let selectedEditionsCount = 0;
let allEditionsCount = 0;
// here we need to check if all editions of a specific
// piece are already defined. Otherwise .length will throw an error and we'll not
// be notified about it.
if(this.state.editionList[this.props.parentId]) {
selectedEditionsCount = this.filterSelectedEditions().length;
allEditionsCount = this.state.editionList[this.props.parentId].length;
}
let columnList = [ let columnList = [
new TableColumnContentModel( new TableColumnContentModel(
(item) => { (item) => {
@ -62,7 +94,10 @@ let AccordionListItemTableEditions = React.createClass({
'selected': item.selected 'selected': item.selected
}}, }},
'', '',
'', <AccordionListItemTableSelectAllEditionsToggle
onChange={this.selectAllItems}
numOfSelectedEditions={selectedEditionsCount}
numOfAllEditions={allEditionsCount}/>,
TableItemCheckbox, TableItemCheckbox,
1, 1,
false false
@ -72,11 +107,12 @@ let AccordionListItemTableEditions = React.createClass({
return { return {
'content': item.edition_number 'content': item.edition_number
}}, }},
'num_editions', 'edition_number',
'#', '#',
TableItemText, TableItemText,
1, 1,
false true,
{to: 'edition', paramsKey: 'editionId', contentKey: 'bitcoin_id'}
), ),
new TableColumnContentModel( new TableColumnContentModel(
(item) => { (item) => {
@ -87,7 +123,8 @@ let AccordionListItemTableEditions = React.createClass({
getLangText('Bitcoin Address'), getLangText('Bitcoin Address'),
TableItemText, TableItemText,
5, 5,
false true,
{to: 'edition', paramsKey: 'editionId', contentKey: 'bitcoin_id'}
), ),
new TableColumnContentModel( new TableColumnContentModel(
(item) => { (item) => {
@ -98,7 +135,8 @@ let AccordionListItemTableEditions = React.createClass({
getLangText('Actions'), getLangText('Actions'),
TableItemAclFiltered, TableItemAclFiltered,
4, 4,
false false,
{to: 'edition', paramsKey: 'editionId', contentKey: 'bitcoin_id'}
) )
]; ];
@ -110,7 +148,10 @@ let AccordionListItemTableEditions = React.createClass({
itemList={this.state.editionList[this.props.parentId]} itemList={this.state.editionList[this.props.parentId]}
columnList={columnList} columnList={columnList}
numOfTableItems={this.props.numOfEditions} numOfTableItems={this.props.numOfEditions}
show={this.props.show}> show={this.props.show}
orderBy={this.state.orderBy}
orderAsc={this.state.orderAsc}
changeOrder={this.changeEditionListOrder}>
<AccordionListItemTableToggle <AccordionListItemTableToggle
className="ascribe-accordion-list-table-toggle" className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable} onClick={this.toggleTable}

View File

@ -0,0 +1,21 @@
import React from 'react';
let AccordionListItemTableSelectAllEditionsToggle = React.createClass({
propTypes: {
onChange: React.PropTypes.func.isRequired,
numOfSelectedEditions: React.PropTypes.number.isRequired,
numOfAllEditions: React.PropTypes.number.isRequired
},
render() {
return (
<input type="checkbox"
onChange={this.props.onChange}
checked={this.props.numOfAllEditions === this.props.numOfSelectedEditions} />
);
}
});
export default AccordionListItemTableSelectAllEditionsToggle;

View File

@ -25,7 +25,7 @@ let ResourceViewer = React.createClass({
mixins: [InjectInHeadMixin], mixins: [InjectInHeadMixin],
componentDidMount() { componentDidMount() {
this.inject('http://antani.com'); //this.inject('http://antani.com');
}, },
render() { render() {

View File

@ -15,7 +15,10 @@ let PieceListBulkModal = React.createClass({
}, },
getInitialState() { getInitialState() {
return EditionListStore.getState(); return {
editions: EditionListStore.getState(),
user: UserStore.getState()
};
}, },
onChange(state) { onChange(state) {
@ -28,7 +31,7 @@ let PieceListBulkModal = React.createClass({
UserStore.listen(this.onChange); UserStore.listen(this.onChange);
}, },
componentDidUnmount() { componentWillUnmount() {
EditionListStore.unlisten(this.onChange); EditionListStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange); UserStore.unlisten(this.onChange);
}, },
@ -37,9 +40,9 @@ let PieceListBulkModal = React.createClass({
let selectedEditionList = []; let selectedEditionList = [];
Object Object
.keys(this.state.editionList) .keys(this.state.editions.editionList)
.forEach((key) => { .forEach((key) => {
let filteredEditionsForPiece = this.state.editionList[key].filter((edition) => edition.selected); let filteredEditionsForPiece = this.state.editions.editionList[key].filter((edition) => edition.selected);
selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece); selectedEditionList = selectedEditionList.concat(filteredEditionsForPiece);
}); });
@ -73,10 +76,6 @@ let PieceListBulkModal = React.createClass({
EditionListActions.clearAllEditionSelections(); EditionListActions.clearAllEditionSelections();
}, },
handleSuccess(){
},
render() { render() {
let availableAcls = this.getAvailableAcls(); let availableAcls = this.getAvailableAcls();
let selectedEditions = this.fetchSelectedEditionList(); let selectedEditions = this.fetchSelectedEditionList();
@ -104,25 +103,25 @@ let PieceListBulkModal = React.createClass({
availableAcls={availableAcls} availableAcls={availableAcls}
action="transfer" action="transfer"
editions={selectedEditions} editions={selectedEditions}
currentUser={this.state.currentUser} currentUser={this.state.user.currentUser}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
<AclButton <AclButton
availableAcls={availableAcls} availableAcls={availableAcls}
action="consign" action="consign"
editions={selectedEditions} editions={selectedEditions}
currentUser={this.state.currentUser} currentUser={this.state.user.currentUser}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
<AclButton <AclButton
availableAcls={availableAcls} availableAcls={availableAcls}
action="loan" action="loan"
editions={selectedEditions} editions={selectedEditions}
currentUser={this.state.currentUser} currentUser={this.state.user.currentUser}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
<AclButton <AclButton
availableAcls={availableAcls} availableAcls={availableAcls}
action="share" action="share"
editions={selectedEditions} editions={selectedEditions}
currentUser={this.state.currentUser} currentUser={this.state.user.currentUser}
handleSuccess={this.handleSuccess} /> handleSuccess={this.handleSuccess} />
</div> </div>
</div> </div>

View File

@ -22,7 +22,7 @@ let PieceListToolbar = React.createClass({
PieceListStore.listen(this.onChange); PieceListStore.listen(this.onChange);
}, },
componentDidUnmount() { componentWillUnmount() {
PieceListStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange);
}, },

View File

@ -9,7 +9,9 @@ let Table = React.createClass({
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
changeOrder: React.PropTypes.func changeOrder: React.PropTypes.func,
orderBy: React.PropTypes.string,
orderAsc: React.PropTypes.bool,
}, },
renderChildren() { renderChildren() {
@ -29,7 +31,6 @@ let Table = React.createClass({
<TableHeader <TableHeader
columnList={this.props.columnList} columnList={this.props.columnList}
itemList={this.props.itemList} itemList={this.props.itemList}
fetchList={this.props.fetchList}
changeOrder={this.props.changeOrder} changeOrder={this.props.changeOrder}
orderAsc={this.props.orderAsc} orderAsc={this.props.orderAsc}
orderBy={this.props.orderBy} /> orderBy={this.props.orderBy} />

View File

@ -6,7 +6,10 @@ let TableHeaderItem = React.createClass({
propTypes: { propTypes: {
columnClasses: React.PropTypes.string.isRequired, columnClasses: React.PropTypes.string.isRequired,
displayName: React.PropTypes.string.isRequired, displayName: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.element
]).isRequired,
columnName: React.PropTypes.string.isRequired, columnName: React.PropTypes.string.isRequired,
canBeOrdered: React.PropTypes.bool, canBeOrdered: React.PropTypes.bool,
changeOrder: React.PropTypes.func, changeOrder: React.PropTypes.func,

View File

@ -1,16 +1,30 @@
import React from 'react'; import React from 'react';
import Router from 'react-router';
import TableColumnContentModel from '../../models/table_column_content_model'; import TableColumnContentModel from '../../models/table_column_content_model';
import TableColumnMixin from '../../mixins/table_column_mixin'; import TableColumnMixin from '../../mixins/table_column_mixin';
let TableItemWrapper = React.createClass({ let TableItemWrapper = React.createClass({
mixins: [TableColumnMixin], mixins: [TableColumnMixin, Router.Navigation],
propTypes: { propTypes: {
columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)), columnList: React.PropTypes.arrayOf(React.PropTypes.instanceOf(TableColumnContentModel)),
columnContent: React.PropTypes.object, columnContent: React.PropTypes.object,
columnWidth: React.PropTypes.number.isRequired columnWidth: React.PropTypes.number.isRequired
}, },
/**
* If a link is defined in columnContent, then we can use
* Router.Navigation.transitionTo to redirect the user
* programmatically
*/
transition(column) {
if(column.link) {
let params = {};
params[column.link.paramsKey] = this.props.columnContent[column.link.contentKey];
this.transitionTo(column.link.to, params);
}
},
render() { render() {
return ( return (
<div> <div>
@ -21,8 +35,13 @@ let TableItemWrapper = React.createClass({
let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth); let columnClass = this.calcColumnClasses(this.props.columnList, i, this.props.columnWidth);
let transition = this.transition.bind(this, column);
return ( return (
<div className={columnClass + ' ascribe-table-item-column'} key={i}> <div
className={columnClass + ' ascribe-table-item-column'}
key={i}
onClick={transition}>
<TypeElement {...typeElementProps} /> <TypeElement {...typeElementProps} />
</div> </div>
); );

View File

@ -11,10 +11,11 @@ import Edition from './edition';
* This is the component that implements resource/data specific functionality * This is the component that implements resource/data specific functionality
*/ */
let EditionContainer = React.createClass({ let EditionContainer = React.createClass({
getInitialState() { getInitialState() {
return {'user': UserStore.getState(), return {
'edition': EditionStore.getState()} 'user': UserStore.getState(),
'edition': EditionStore.getState()
};
}, },
onChange(state) { onChange(state) {
@ -22,29 +23,30 @@ let EditionContainer = React.createClass({
}, },
componentDidMount() { componentDidMount() {
EditionActions.fetchOne(this.props.params.editionId);
EditionStore.listen(this.onChange); EditionStore.listen(this.onChange);
UserActions.fetchCurrentUser();
UserStore.listen(this.onChange); UserStore.listen(this.onChange);
UserActions.fetchCurrentUser();
EditionActions.fetchOne(this.props.params.editionId);
}, },
componentDidUnmount() { componentWillUnmount() {
EditionStore.unlisten(this.onChange); EditionStore.unlisten(this.onChange);
UserStore.unlisten(this.onChange); UserStore.unlisten(this.onChange);
}, },
render() { render() {
if('title' in this.state.edition) { if('title' in this.state.edition) {
return ( return (
<Edition edition={this.state.edition } currentUser={this.state.currentUser}></Edition> <Edition
edition={this.state.edition}
currentUser={this.state.currentUser}>
</Edition>
); );
} else { } else {
return ( return (
<p>Loading</p> <p>Loading</p>
); );
} }
} }
}); });

View File

@ -26,6 +26,10 @@ let Header = React.createClass({
UserActions.fetchCurrentUser(); UserActions.fetchCurrentUser();
}, },
componentWillUnmount() {
UserStore.unlisten(this.onChange);
},
onChange(state) { onChange(state) {
this.setState(state); this.setState(state);
}, },

View File

@ -1,5 +1,7 @@
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';
import { generateOrderingQueryParams } from '../utils/fetch_api_utils';
import AppConstants from '../constants/application_constants'; import AppConstants from '../constants/application_constants';
@ -7,8 +9,9 @@ let EditionListFetcher = {
/** /**
* Fetches a list of editions from the API. * Fetches a list of editions from the API.
*/ */
fetch(pieceId) { fetch(pieceId, orderBy, orderAsc) {
return fetch.get('editions_list', { 'piece_id': pieceId }); let ordering = generateOrderingQueryParams(orderBy, orderAsc);
return fetch.get('editions_list', { 'piece_id': pieceId, ordering });
} }
}; };

View File

@ -1,12 +1,13 @@
class TableColumnContentModel { class TableColumnContentModel {
// ToDo: Add validation for all passed-in parameters // ToDo: Add validation for all passed-in parameters
constructor(transformFn, columnName, displayName, displayType, rowWidth, canBeOrdered) { constructor(transformFn, columnName, displayName, displayType, rowWidth, canBeOrdered, link) {
this.transformFn = transformFn; this.transformFn = transformFn;
this.columnName = columnName; this.columnName = columnName;
this.displayName = displayName; this.displayName = displayName;
this.displayType = displayType; this.displayType = displayType;
this.rowWidth = rowWidth; this.rowWidth = rowWidth;
this.canBeOrdered = canBeOrdered; this.canBeOrdered = canBeOrdered;
this.link = link;
} }
} }

View File

@ -6,10 +6,12 @@ import EditionsListActions from '../actions/edition_list_actions';
class EditionListStore { class EditionListStore {
constructor() { constructor() {
this.editionList = {}; this.editionList = {};
this.orderBy = 'edition_number';
this.orderAsc = true;
this.bindActions(EditionsListActions); this.bindActions(EditionsListActions);
} }
onUpdateEditionList({pieceId, editionListOfPiece}) { onUpdateEditionList({pieceId, editionListOfPiece, orderBy, orderAsc}) {
if(this.editionList[pieceId]) { if(this.editionList[pieceId]) {
this.editionList[pieceId].forEach((edition, i) => { this.editionList[pieceId].forEach((edition, i) => {
// This uses the index of the new editionList for determining the edition. // This uses the index of the new editionList for determining the edition.
@ -18,6 +20,8 @@ class EditionListStore {
}) })
} }
this.editionList[pieceId] = editionListOfPiece; this.editionList[pieceId] = editionListOfPiece;
this.orderBy = orderBy;
this.orderAsc = orderAsc;
} }
onSelectEdition({pieceId, editionId}) { onSelectEdition({pieceId, editionId}) {

View File

@ -43,8 +43,23 @@ class PieceListStore {
this.search = search; this.search = search;
this.orderAsc = orderAsc; this.orderAsc = orderAsc;
this.orderBy = orderBy; this.orderBy = orderBy;
this.pieceList = pieceList;
this.pieceListCount = pieceListCount; this.pieceListCount = pieceListCount;
/**
* The piece list store currently stores the open/close state of a piece list item.
*
* Once a new page is requested, this.pieceList will be overwritten, which means that the
* open/close state of a specific list item will be thrown away.
*
* This means that when opening an editionListTable on a piece, and continuing
* clicking next or back in the pagination, the editionListTable will return to its
* default value, which is "close".
*
* We did not implement this, as we're going to add pagination to pieceList at some
* point anyway. Then, this problem is automatically resolved.
*
*/
this.pieceList = pieceList;
} }
}; };