1
0
mirror of https://github.com/ascribe/onion.git synced 2024-11-15 09:35:10 +01:00

Merge branch 'AD-540-chunked-edition-list-loading-to-a'

This commit is contained in:
Tim Daubenschütz 2015-07-06 18:35:17 +02:00
commit bc53b4b54c
8 changed files with 166 additions and 57 deletions

View File

@ -15,21 +15,30 @@ class EditionListActions {
); );
} }
fetchEditionList(pieceId, orderBy, orderAsc) { fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc) {
if(!orderBy && typeof orderAsc == 'undefined') { if(!orderBy && typeof orderAsc == 'undefined') {
orderBy = 'edition_number'; orderBy = 'edition_number';
orderAsc = true; orderAsc = true;
} }
// Taken from: http://stackoverflow.com/a/519157/1263876
if(typeof page === 'undefined' && typeof pageSize === 'undefined') {
page = 1;
pageSize = 10;
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
EditionListFetcher EditionListFetcher
.fetch(pieceId, orderBy, orderAsc) .fetch(pieceId, page, pageSize, orderBy, orderAsc)
.then((res) => { .then((res) => {
this.actions.updateEditionList({ this.actions.updateEditionList({
'editionListOfPiece': res.editions,
pieceId, pieceId,
page,
pageSize,
orderBy, orderBy,
orderAsc orderAsc,
'editionListOfPiece': res.editions,
'count': res.count
}); });
resolve(res); resolve(res);
}) })

View File

@ -22,25 +22,22 @@ let AccordionListItemTable = React.createClass({
render() { render() {
if(this.props.show && this.props.itemList) { if(this.props.show && this.props.itemList) {
return ( return (
<div className={this.props.className}> <Table
<Table responsive
responsive className="ascribe-table"
className="ascribe-table" columnList={this.props.columnList}
columnList={this.props.columnList} itemList={this.props.itemList}
itemList={this.props.itemList} changeOrder={this.props.changeOrder}
changeOrder={this.props.changeOrder} orderBy={this.props.orderBy}
orderBy={this.props.orderBy} orderAsc={this.props.orderAsc}>
orderAsc={this.props.orderAsc}> {this.props.itemList.map((item, i) => {
{this.props.itemList.map((item, i) => { return (
return ( <TableItem
<TableItem className="ascribe-table-item-selectable"
className="ascribe-table-item-selectable" key={i} />
key={i} /> );
); })}
})} </Table>
</Table>
{this.props.children}
</div>
); );
} else { } else {
return ( return (

View File

@ -16,6 +16,7 @@ import TableItemCheckbox from '../ascribe_table/table_item_checkbox';
import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered'; import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils';
let AccordionListItemTableEditions = React.createClass({ let AccordionListItemTableEditions = React.createClass({
@ -25,7 +26,12 @@ let AccordionListItemTableEditions = React.createClass({
}, },
getInitialState() { getInitialState() {
return EditionListStore.getState(); return mergeOptions(
EditionListStore.getState(),
{
showMoreLoading: false
}
);
}, },
componentDidMount() { componentDidMount() {
@ -37,6 +43,12 @@ let AccordionListItemTableEditions = React.createClass({
}, },
onChange(state) { onChange(state) {
if(this.state.showMoreLoading) {
this.setState({
showMoreLoading: false
});
}
this.setState(state); this.setState(state);
}, },
@ -67,6 +79,16 @@ let AccordionListItemTableEditions = React.createClass({
} }
}, },
loadFurtherEditions() {
// trigger loading animation
this.setState({
showMoreLoading: true
});
let editionList = this.state.editionList[this.props.parentId];
EditionListActions.fetchEditionList(this.props.parentId, editionList.page + 1, editionList.pageSize);
},
changeEditionListOrder(orderBy, orderAsc) { changeEditionListOrder(orderBy, orderAsc) {
EditionListActions.fetchEditionList(this.props.parentId, orderBy, orderAsc); EditionListActions.fetchEditionList(this.props.parentId, orderBy, orderAsc);
}, },
@ -76,22 +98,31 @@ let AccordionListItemTableEditions = React.createClass({
let allEditionsCount = 0; let allEditionsCount = 0;
let orderBy; let orderBy;
let orderAsc; let orderAsc;
let show; let show = false;
let showExpandOption = false;
let editionsForPiece = this.state.editionList[this.props.parentId];
// here we need to check if all editions of a specific // 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 // piece are already defined. Otherwise .length will throw an error and we'll not
// be notified about it. // be notified about it.
if(this.state.editionList[this.props.parentId]) { if(editionsForPiece) {
selectedEditionsCount = this.filterSelectedEditions().length; selectedEditionsCount = this.filterSelectedEditions().length;
allEditionsCount = this.state.editionList[this.props.parentId].length; allEditionsCount = editionsForPiece.length;
orderBy = this.state.editionList[this.props.parentId].orderBy; orderBy = editionsForPiece.orderBy;
orderAsc = this.state.editionList[this.props.parentId].orderAsc; orderAsc = editionsForPiece.orderAsc;
} }
if(this.props.parentId in this.state.isEditionListOpenForPieceId) { if(this.props.parentId in this.state.isEditionListOpenForPieceId) {
show = this.state.isEditionListOpenForPieceId[this.props.parentId].show; show = this.state.isEditionListOpenForPieceId[this.props.parentId].show;
} }
// if the number of editions in the array is equal to the maximum number of editions,
// then the "Show me more" dialog should be hidden from the user's view
if(editionsForPiece && editionsForPiece.count > editionsForPiece.length) {
showExpandOption = true;
}
let transition = new TransitionModel('edition', 'editionId', 'bitcoin_id'); let transition = new TransitionModel('edition', 'editionId', 'bitcoin_id');
let columnList = [ let columnList = [
@ -155,23 +186,26 @@ let AccordionListItemTableEditions = React.createClass({
) )
]; ];
let loadingSpinner = <span className="glyph-ascribe-spool-chunked ascribe-color spin"/> ;
return ( return (
<div> <div className={this.props.className}>
<AccordionListItemTableToggle
className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable}
message={show && typeof editionsForPiece !== 'undefined' ? <span><span className="glyphicon glyphicon-menu-up" aria-hidden="true" style={{top: 2}}></span> Hide editions</span> : <span><span className="glyphicon glyphicon-menu-down" aria-hidden="true" style={{top: 2}}></span> Show editions {show && typeof editionsForPiece === 'undefined' ? loadingSpinner : null}</span>} />
<AccordionListItemTable <AccordionListItemTable
className={this.props.className}
parentId={this.props.parentId} parentId={this.props.parentId}
itemList={this.state.editionList[this.props.parentId]} itemList={editionsForPiece}
columnList={columnList} columnList={columnList}
show={show} show={show}
orderBy={orderBy} orderBy={orderBy}
orderAsc={orderAsc} orderAsc={orderAsc}
changeOrder={this.changeEditionListOrder}> changeOrder={this.changeEditionListOrder} />
<AccordionListItemTableToggle <AccordionListItemTableToggle
className="ascribe-accordion-list-table-toggle" className="ascribe-accordion-list-table-toggle"
onClick={this.toggleTable} onClick={this.loadFurtherEditions}
show={show} /> message={show && showExpandOption ? <span><span className="glyphicon glyphicon-option-horizontal" aria-hidden="true" style={{top: 3}}></span> Show me more {this.state.showMoreLoading ? loadingSpinner : null}</span> : ''} />
</AccordionListItemTable>
</div> </div>
); );
} }

View File

@ -6,7 +6,10 @@ let AccordionListItemTableToggle = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
show: React.PropTypes.bool message: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.element
])
}, },
render() { render() {
@ -14,7 +17,7 @@ let AccordionListItemTableToggle = React.createClass({
<span <span
className={this.props.className} className={this.props.className}
onClick={this.props.onClick}> onClick={this.props.onClick}>
{this.props.show ? 'Hide all Editions' : 'Show all Editions'} {this.props.message}
</span> </span>
); );
} }

View File

@ -71,7 +71,11 @@ let PieceListBulkModal = React.createClass({
handleSuccess() { handleSuccess() {
this.fetchSelectedPieceEditionList() this.fetchSelectedPieceEditionList()
.forEach((pieceId) => { .forEach((pieceId) => {
EditionListActions.fetchEditionList(pieceId, this.state.orderBy, this.state.orderAsc); let editionsForPiece = this.state.editionList[pieceId];
for(let i = 1; i <= editionsForPiece.page; i++) {
EditionListActions.fetchEditionList(pieceId, i, editionsForPiece.pageSize, editionsForPiece.orderBy, editionsForPiece.orderAsc);
}
}); });
EditionListActions.clearAllEditionSelections(); EditionListActions.clearAllEditionSelections();
}, },

View File

@ -9,9 +9,14 @@ let EditionListFetcher = {
/** /**
* Fetches a list of editions from the API. * Fetches a list of editions from the API.
*/ */
fetch(pieceId, orderBy, orderAsc) { fetch(pieceId, page, pageSize, orderBy, orderAsc) {
let ordering = generateOrderingQueryParams(orderBy, orderAsc); let ordering = generateOrderingQueryParams(orderBy, orderAsc);
return requests.get('editions_list', { 'piece_id': pieceId, ordering }); return requests.get('editions_list', {
'piece_id': pieceId,
page,
pageSize,
ordering
});
} }
}; };

View File

@ -12,27 +12,48 @@ class EditionListStore {
this.bindActions(EditionsListActions); this.bindActions(EditionsListActions);
} }
onUpdateEditionList({pieceId, editionListOfPiece, orderBy, orderAsc}) { onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count}) {
if(this.editionList[pieceId]) {
this.editionList[pieceId].forEach((edition, i) => { /*
// This uses the index of the new editionList for determining the edition. Basically there are two modes an edition list can be updated.
// If the list of editions can be sorted in the future, this needs to be changed!
if (editionListOfPiece[i]) { 1. The elements that have been requested from the server are not yet defined in the store => just assign them
editionListOfPiece[i] = React.addons.update(edition, {$merge: editionListOfPiece[i]}); 2. The elements are already defined => merge current objects with the new ones from the server
}
}); */
for(let i = 0; i < editionListOfPiece.length; i++) {
// if editionList for a specific piece does not exist yet,
// just initialize a new array
if(!this.editionList[pieceId]) {
this.editionList[pieceId] = [];
}
// this is the index formula for accessing an edition of a specific
// page
let storeEditionIndex = (page - 1) * pageSize + i;
let editionsForPieces = this.editionList[pieceId];
// if edition already exists, just merge
if(editionsForPieces[storeEditionIndex]) {
editionsForPieces[storeEditionIndex] = React.addons.update(editionsForPieces[storeEditionIndex], {$merge: editionListOfPiece[i]});
} else {
// if does not exist, assign
editionsForPieces[storeEditionIndex] = editionListOfPiece[i];
}
} }
this.editionList[pieceId] = editionListOfPiece;
/** /**
* orderBy and orderAsc are specific to a single list of editions * page, pageSize, orderBy, orderAsc and count are specific to a single list of editions
* therefore they need to be saved in relation to their parent-piece. * therefore they need to be saved in relation to their parent-piece.
* *
* Default values for both are set in the editon_list-actions. * Default values for both are set in the editon_list_actions.
*/ */
this.editionList[pieceId].page = page;
this.editionList[pieceId].pageSize = pageSize;
this.editionList[pieceId].orderBy = orderBy; this.editionList[pieceId].orderBy = orderBy;
this.editionList[pieceId].orderAsc = orderAsc; this.editionList[pieceId].orderAsc = orderAsc;
this.editionList[pieceId].count = count;
} }
onSelectEdition({pieceId, editionId, toValue}) { onSelectEdition({pieceId, editionId, toValue}) {

View File

@ -284,3 +284,39 @@ html {
margin-right: 1px; margin-right: 1px;
margin-top: 1px; margin-top: 1px;
} }
.spin {
display:inline-block;
-webkit-animation: spin 1s infinite linear;
-moz-animation: spin 1s infinite linear;
-o-animation: spin 1s infinite linear;
-ms-animation: spin 1s infinite linear;
animation: spin 1s infinite linear;
-webkit-transform-origin: 55% 70%;
-moz-transform-origin: 55% 70%;
-o-transform-origin: 55% 70%;
transform-origin: 55% 70%;
width: 10px;
height: 10px;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg);}
100% { -webkit-transform: rotate(360deg);}
}
@-moz-keyframes spin {
0% { -moz-transform: rotate(0deg); }
100% { -moz-transform: rotate(360deg);}
}
@-o-keyframes spin {
0% { -o-transform: rotate(0deg);}
100% { -o-transform: rotate(360deg);}
}
@-ms-keyframes spin {
0% { -ms-transform: rotate(0deg);}
100% { -ms-transform: rotate(360deg);}
}
@-keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg);}
}