diff --git a/js/actions/edition_list_actions.js b/js/actions/edition_list_actions.js index c54bc6fd..f04b63c8 100644 --- a/js/actions/edition_list_actions.js +++ b/js/actions/edition_list_actions.js @@ -15,21 +15,30 @@ class EditionListActions { ); } - fetchEditionList(pieceId, orderBy, orderAsc) { + fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc) { if(!orderBy && typeof orderAsc == 'undefined') { orderBy = 'edition_number'; 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) => { EditionListFetcher - .fetch(pieceId, orderBy, orderAsc) + .fetch(pieceId, page, pageSize, orderBy, orderAsc) .then((res) => { this.actions.updateEditionList({ - 'editionListOfPiece': res.editions, pieceId, + page, + pageSize, orderBy, - orderAsc + orderAsc, + 'editionListOfPiece': res.editions, + 'count': res.count }); resolve(res); }) diff --git a/js/components/ascribe_accordion_list/accordion_list_item_table.js b/js/components/ascribe_accordion_list/accordion_list_item_table.js index 65101554..ac4e6591 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_table.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_table.js @@ -22,25 +22,22 @@ let AccordionListItemTable = React.createClass({ render() { if(this.props.show && this.props.itemList) { return ( -
- - {this.props.itemList.map((item, i) => { - return ( - - ); - })} -
- {this.props.children} -
+ + {this.props.itemList.map((item, i) => { + return ( + + ); + })} +
); } else { return ( diff --git a/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js b/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js index 7e030f1c..2bb3bd52 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_table_editions.js @@ -16,6 +16,7 @@ import TableItemCheckbox from '../ascribe_table/table_item_checkbox'; import TableItemAclFiltered from '../ascribe_table/table_item_acl_filtered'; import { getLangText } from '../../utils/lang_utils'; +import { mergeOptions } from '../../utils/general_utils'; let AccordionListItemTableEditions = React.createClass({ @@ -25,7 +26,12 @@ let AccordionListItemTableEditions = React.createClass({ }, getInitialState() { - return EditionListStore.getState(); + return mergeOptions( + EditionListStore.getState(), + { + showMoreLoading: false + } + ); }, componentDidMount() { @@ -37,6 +43,12 @@ let AccordionListItemTableEditions = React.createClass({ }, onChange(state) { + if(this.state.showMoreLoading) { + this.setState({ + showMoreLoading: false + }); + } + 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) { EditionListActions.fetchEditionList(this.props.parentId, orderBy, orderAsc); }, @@ -76,22 +98,31 @@ let AccordionListItemTableEditions = React.createClass({ let allEditionsCount = 0; let orderBy; 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 // 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]) { + if(editionsForPiece) { selectedEditionsCount = this.filterSelectedEditions().length; - allEditionsCount = this.state.editionList[this.props.parentId].length; - orderBy = this.state.editionList[this.props.parentId].orderBy; - orderAsc = this.state.editionList[this.props.parentId].orderAsc; + allEditionsCount = editionsForPiece.length; + orderBy = editionsForPiece.orderBy; + orderAsc = editionsForPiece.orderAsc; } if(this.props.parentId in this.state.isEditionListOpenForPieceId) { 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 columnList = [ @@ -155,23 +186,26 @@ let AccordionListItemTableEditions = React.createClass({ ) ]; + let loadingSpinner = ; + return ( -
+
+ Hide editions : Show editions {show && typeof editionsForPiece === 'undefined' ? loadingSpinner : null}} /> - - - + changeOrder={this.changeEditionListOrder} /> + Show me more {this.state.showMoreLoading ? loadingSpinner : null} : ''} />
); } diff --git a/js/components/ascribe_accordion_list/accordion_list_item_table_toggle.js b/js/components/ascribe_accordion_list/accordion_list_item_table_toggle.js index 542027cd..5140c285 100644 --- a/js/components/ascribe_accordion_list/accordion_list_item_table_toggle.js +++ b/js/components/ascribe_accordion_list/accordion_list_item_table_toggle.js @@ -6,7 +6,10 @@ let AccordionListItemTableToggle = React.createClass({ propTypes: { className: React.PropTypes.string, onClick: React.PropTypes.func, - show: React.PropTypes.bool + message: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element + ]) }, render() { @@ -14,7 +17,7 @@ let AccordionListItemTableToggle = React.createClass({ - {this.props.show ? 'Hide all Editions' : 'Show all Editions'} + {this.props.message} ); } diff --git a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js index 16c69689..86241c76 100644 --- a/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js +++ b/js/components/ascribe_piece_list_bulk_modal/piece_list_bulk_modal.js @@ -71,7 +71,11 @@ let PieceListBulkModal = React.createClass({ handleSuccess() { this.fetchSelectedPieceEditionList() .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(); }, diff --git a/js/fetchers/edition_list_fetcher.js b/js/fetchers/edition_list_fetcher.js index 32dc445d..6365a4be 100644 --- a/js/fetchers/edition_list_fetcher.js +++ b/js/fetchers/edition_list_fetcher.js @@ -9,9 +9,14 @@ let EditionListFetcher = { /** * Fetches a list of editions from the API. */ - fetch(pieceId, orderBy, orderAsc) { + fetch(pieceId, page, pageSize, 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 + }); } }; diff --git a/js/stores/edition_list_store.js b/js/stores/edition_list_store.js index 8dbf9104..a59721ba 100644 --- a/js/stores/edition_list_store.js +++ b/js/stores/edition_list_store.js @@ -12,27 +12,48 @@ class EditionListStore { this.bindActions(EditionsListActions); } - onUpdateEditionList({pieceId, editionListOfPiece, orderBy, orderAsc}) { - if(this.editionList[pieceId]) { - this.editionList[pieceId].forEach((edition, i) => { - // This uses the index of the new editionList for determining the edition. - // If the list of editions can be sorted in the future, this needs to be changed! - if (editionListOfPiece[i]) { - editionListOfPiece[i] = React.addons.update(edition, {$merge: editionListOfPiece[i]}); - } - }); + onUpdateEditionList({pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count}) { + + /* + Basically there are two modes an edition list can be updated. + + 1. The elements that have been requested from the server are not yet defined in the store => just assign them + 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. * - * 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].orderAsc = orderAsc; + this.editionList[pieceId].count = count; } onSelectEdition({pieceId, editionId, toValue}) { diff --git a/sass/main.scss b/sass/main.scss index fda26fb0..8725d6f0 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -284,3 +284,39 @@ html { margin-right: 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);} +} \ No newline at end of file