1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 09:23:13 +01:00

Merge with master

This commit is contained in:
Brett Sun 2016-01-18 14:15:19 +01:00
commit aa00415a83
28 changed files with 199 additions and 217 deletions

View File

@ -17,23 +17,31 @@ class EditionListActions {
); );
} }
fetchEditionList(pieceId, page, pageSize, orderBy, orderAsc, filterBy) { fetchEditionList({ pieceId, page, pageSize, orderBy, orderAsc, filterBy, maxEdition }) {
if((!orderBy && typeof orderAsc === 'undefined') || !orderAsc) { if ((!orderBy && typeof orderAsc === 'undefined') || !orderAsc) {
orderBy = 'edition_number'; orderBy = 'edition_number';
orderAsc = true; orderAsc = true;
} }
// Taken from: http://stackoverflow.com/a/519157/1263876 // Taken from: http://stackoverflow.com/a/519157/1263876
if((typeof page === 'undefined' || !page) && (typeof pageSize === 'undefined' || !pageSize)) { if ((typeof page === 'undefined' || !page) && (typeof pageSize === 'undefined' || !pageSize)) {
page = 1; page = 1;
pageSize = 10; pageSize = 10;
} }
let itemsToFetch = pageSize;
// If we only want to fetch up to a specified edition, fetch all pages up to it
// as one page and adjust afterwards
if (typeof maxEdition === 'number') {
itemsToFetch = Math.ceil(maxEdition / pageSize) * pageSize;
page = 1;
}
return Q.Promise((resolve, reject) => { return Q.Promise((resolve, reject) => {
EditionListFetcher EditionListFetcher
.fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy) .fetch({ pieceId, page, itemsToFetch, orderBy, orderAsc, filterBy })
.then((res) => { .then((res) => {
if(res && !res.editions) { if (res && !res.editions) {
throw new Error('Piece has no editions to fetch.'); throw new Error('Piece has no editions to fetch.');
} }
@ -44,8 +52,9 @@ class EditionListActions {
orderBy, orderBy,
orderAsc, orderAsc,
filterBy, filterBy,
'editionListOfPiece': res.editions, maxEdition,
'count': res.count count: res.count,
editionListOfPiece: res.editions
}); });
resolve(res); resolve(res);
}) })
@ -54,7 +63,6 @@ class EditionListActions {
reject(err); reject(err);
}); });
}); });
} }
} }

View File

@ -15,7 +15,7 @@ class PieceListActions {
); );
} }
fetchPieceList(page, pageSize, search, orderBy, orderAsc, filterBy) { fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy }) {
// To prevent flickering on a pagination request, // To prevent flickering on a pagination request,
// we overwrite the piecelist with an empty list before // we overwrite the piecelist with an empty list before
// pieceListCount === -1 defines the loading state // pieceListCount === -1 defines the loading state
@ -34,7 +34,7 @@ class PieceListActions {
// afterwards, we can load the list // afterwards, we can load the list
return Q.Promise((resolve, reject) => { return Q.Promise((resolve, reject) => {
PieceListFetcher PieceListFetcher
.fetch(page, pageSize, search, orderBy, orderAsc, filterBy) .fetch({ page, pageSize, search, orderBy, orderAsc, filterBy })
.then((res) => { .then((res) => {
this.actions.updatePieceList({ this.actions.updatePieceList({
page, page,

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
import 'babel/polyfill'; import 'babel/polyfill';
import 'classlist-polyfill';
import React from 'react'; import React from 'react';
import { Router, Redirect } from 'react-router'; import { Router, Redirect } from 'react-router';

View File

@ -19,9 +19,10 @@ import { getLangText } from '../../utils/lang_utils';
let AccordionListItemEditionWidget = React.createClass({ let AccordionListItemEditionWidget = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string,
piece: React.PropTypes.object.isRequired, piece: React.PropTypes.object.isRequired,
toggleCreateEditionsDialog: React.PropTypes.func.isRequired, toggleCreateEditionsDialog: React.PropTypes.func.isRequired,
className: React.PropTypes.string,
onPollingSuccess: React.PropTypes.func onPollingSuccess: React.PropTypes.func
}, },
@ -50,14 +51,15 @@ let AccordionListItemEditionWidget = React.createClass({
* Calls the store to either show or hide the editionListTable * Calls the store to either show or hide the editionListTable
*/ */
toggleTable() { toggleTable() {
let pieceId = this.props.piece.id; const { piece: { id: pieceId } } = this.props;
let isEditionListOpen = this.state.isEditionListOpenForPieceId[pieceId] ? this.state.isEditionListOpenForPieceId[pieceId].show : false; const { filterBy, isEditionListOpenForPieceId } = this.state;
const isEditionListOpen = isEditionListOpenForPieceId[pieceId] ? isEditionListOpenForPieceId[pieceId].show : false;
if(isEditionListOpen) { if (isEditionListOpen) {
EditionListActions.toggleEditionList(pieceId); EditionListActions.toggleEditionList(pieceId);
} else { } else {
EditionListActions.toggleEditionList(pieceId); EditionListActions.toggleEditionList(pieceId);
EditionListActions.fetchEditionList(pieceId, null, null, null, null, this.state.filterBy); EditionListActions.fetchEditionList({ pieceId, filterBy });
} }
}, },

View File

@ -66,20 +66,28 @@ let AccordionListItemTableEditions = React.createClass({
}, },
filterSelectedEditions() { filterSelectedEditions() {
let selectedEditions = this.state.editionList[this.props.parentId] return this.state
.filter((edition) => edition.selected); .editionList[this.props.parentId]
return selectedEditions; .filter((edition) => edition.selected);
}, },
loadFurtherEditions() { loadFurtherEditions() {
const { parentId: pieceId } = this.props;
const { page, pageSize, orderBy, orderAsc, filterBy } = this.state.editionList[pieceId];
// trigger loading animation // trigger loading animation
this.setState({ this.setState({
showMoreLoading: true showMoreLoading: true
}); });
let editionList = this.state.editionList[this.props.parentId]; EditionListActions.fetchEditionList({
EditionListActions.fetchEditionList(this.props.parentId, editionList.page + 1, editionList.pageSize, pieceId,
editionList.orderBy, editionList.orderAsc, editionList.filterBy); pageSize,
orderBy,
orderAsc,
filterBy,
page: page + 1
});
}, },
render() { render() {
const { className, parentId } = this.props; const { className, parentId } = this.props;

View File

@ -88,11 +88,12 @@ let AccordionListItemWallet = React.createClass({
}, },
onPollingSuccess(pieceId) { onPollingSuccess(pieceId) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
EditionListActions.toggleEditionList(pieceId); EditionListActions.toggleEditionList(pieceId);
let notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000); const notification = new GlobalNotificationModel('Editions successfully created', 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },

View File

@ -28,6 +28,12 @@ let CreateEditionsButton = React.createClass({
EditionListStore.listen(this.onChange); EditionListStore.listen(this.onChange);
}, },
componentDidUpdate() {
if(this.props.piece.num_editions === 0 && typeof this.state.pollingIntervalIndex === 'undefined') {
this.startPolling();
}
},
componentWillUnmount() { componentWillUnmount() {
EditionListStore.unlisten(this.onChange); EditionListStore.unlisten(this.onChange);
clearInterval(this.state.pollingIntervalIndex); clearInterval(this.state.pollingIntervalIndex);
@ -37,28 +43,24 @@ let CreateEditionsButton = React.createClass({
this.setState(state); this.setState(state);
}, },
componentDidUpdate() {
if(this.props.piece.num_editions === 0 && typeof this.state.pollingIntervalIndex === 'undefined') {
this.startPolling();
}
},
startPolling() { startPolling() {
// start polling until editions are defined // start polling until editions are defined
let pollingIntervalIndex = setInterval(() => { let pollingIntervalIndex = setInterval(() => {
// requests, will try to merge the filterBy parameter with other parameters (mergeOptions). // requests, will try to merge the filterBy parameter with other parameters (mergeOptions).
// Therefore it can't but null but instead has to be an empty object // Therefore it can't but null but instead has to be an empty object
EditionListActions.fetchEditionList(this.props.piece.id, null, null, null, null, {}) EditionListActions
.then((res) => { .fetchEditionList({
pieceId: this.props.piece.id,
clearInterval(this.state.pollingIntervalIndex); filterBy: {}
this.props.onPollingSuccess(this.props.piece.id, res.editions[0].num_editions); })
.then((res) => {
}) clearInterval(this.state.pollingIntervalIndex);
.catch((err) => { this.props.onPollingSuccess(this.props.piece.id, res.editions[0].num_editions);
/* Ignore and keep going */ })
}); .catch((err) => {
/* Ignore and keep going */
});
}, 5000); }, 5000);
this.setState({ this.setState({

View File

@ -79,9 +79,10 @@ let EditionActionPanel = React.createClass({
}, },
refreshCollection() { refreshCollection() {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
EditionListActions.refreshEditionList({pieceId: this.props.edition.parent}); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
EditionListActions.refreshEditionList({ pieceId: this.props.edition.parent });
}, },
handleSuccess(response) { handleSuccess(response) {

View File

@ -144,15 +144,18 @@ let PieceContainer = React.createClass({
}, },
handleEditionCreationSuccess() { handleEditionCreationSuccess() {
const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
PieceActions.updateProperty({ key: 'num_editions', value: 0 }); PieceActions.updateProperty({ key: 'num_editions', value: 0 });
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
this.toggleCreateEditionsDialog(); this.toggleCreateEditionsDialog();
}, },
handleDeleteSuccess(response) { handleDeleteSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
// since we're deleting a piece, we just need to close // since we're deleting a piece, we just need to close
// all editions dialogs and not reload them // all editions dialogs and not reload them
@ -181,6 +184,8 @@ let PieceContainer = React.createClass({
}, },
handlePollingSuccess(pieceId, numEditions) { handlePollingSuccess(pieceId, numEditions) {
const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
// we need to refresh the num_editions property of the actual piece we're looking at // we need to refresh the num_editions property of the actual piece we're looking at
PieceActions.updateProperty({ PieceActions.updateProperty({
key: 'num_editions', key: 'num_editions',
@ -191,8 +196,7 @@ let PieceContainer = React.createClass({
// btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion // btw.: It's not sufficient to just set num_editions to numEditions, since a single accordion
// list item also uses the firstEdition property which we can only get from the server in that case. // list item also uses the firstEdition property which we can only get from the server in that case.
// Therefore we need to at least refetch the changed piece from the server or on our case simply all // Therefore we need to at least refetch the changed piece from the server or on our case simply all
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
const notification = new GlobalNotificationModel(getLangText('Editions successfully created'), 'success', 10000); const notification = new GlobalNotificationModel(getLangText('Editions successfully created'), 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);

View File

@ -1,43 +0,0 @@
'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

@ -196,13 +196,14 @@ let PieceList = React.createClass({
this.state.pieceList this.state.pieceList
.forEach((piece) => { .forEach((piece) => {
// but only if they're actually open // but only if they're actually open
if(this.state.isEditionListOpenForPieceId[piece.id].show) { const isEditionListOpenForPiece = this.state.isEditionListOpenForPieceId[piece.id];
if (isEditionListOpenForPiece && isEditionListOpenForPiece.show) {
EditionListActions.refreshEditionList({ EditionListActions.refreshEditionList({
pieceId: piece.id, pieceId: piece.id,
filterBy filterBy
}); });
} }
}); });
}); });
@ -212,15 +213,15 @@ let PieceList = React.createClass({
}, },
applyOrderBy(orderBy) { applyOrderBy(orderBy) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, page, pageSize, search } = this.state;
orderBy, this.state.orderAsc, this.state.filterBy); PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
}, },
loadPieceList({ page, filterBy = this.state.filterBy, search = this.state.search }) { loadPieceList({ page, filterBy = this.state.filterBy, search = this.state.search }) {
const { orderAsc, pageSize } = this.state;
const orderBy = this.state.orderBy || this.props.orderBy; const orderBy = this.state.orderBy || this.props.orderBy;
return PieceListActions.fetchPieceList(page, this.state.pageSize, search, return PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
orderBy, this.state.orderAsc, filterBy);
}, },
fetchSelectedPieceEditionList() { fetchSelectedPieceEditionList() {
@ -246,8 +247,9 @@ let PieceList = React.createClass({
}, },
handleAclSuccess() { handleAclSuccess() {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderBy, orderAsc, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.fetchSelectedPieceEditionList() this.fetchSelectedPieceEditionList()
.forEach((pieceId) => { .forEach((pieceId) => {

View File

@ -65,26 +65,21 @@ let RegisterPiece = React.createClass( {
this.setState(state); this.setState(state);
}, },
handleSuccess(response){ handleSuccess(response) {
const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
const notification = new GlobalNotificationModel(response.notification, 'success', 10000); const 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( PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.page,
this.state.pageSize,
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
this.history.push(`/pieces/${response.piece.id}`); this.history.push(`/pieces/${response.piece.id}`);
}, },
getSpecifyEditions() { getSpecifyEditions() {
if(this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) { if (this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) {
return ( return (
<Property <Property
name="num_editions" name="num_editions"

View File

@ -58,8 +58,9 @@ let AccordionListItemPrize = React.createClass({
}, },
handleSubmitPrizeSuccess(response) { handleSubmitPrizeSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
let notification = new GlobalNotificationModel(response.notification, 'success', 10000); let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
@ -138,8 +139,9 @@ let AccordionListItemPrize = React.createClass({
}, },
refreshPieceData() { refreshPieceData() {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
}, },
onSelectChange(){ onSelectChange(){

View File

@ -301,9 +301,10 @@ let PrizePieceRatings = React.createClass({
}, },
refreshPieceData() { refreshPieceData() {
const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.props.loadPiece(); this.props.loadPiece();
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
}, },
onSelectChange() { onSelectChange() {

View File

@ -52,8 +52,9 @@ let CylandAccordionListItem = React.createClass({
}, },
handleSubmitSuccess(response) { handleSubmitSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
let notification = new GlobalNotificationModel(response.notification, 'success', 10000); let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);

View File

@ -77,8 +77,9 @@ let CylandPieceContainer = React.createClass({
}, },
handleDeleteSuccess(response) { handleDeleteSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
// since we're deleting a piece, we just need to close // since we're deleting a piece, we just need to close
// all editions dialogs and not reload them // all editions dialogs and not reload them

View File

@ -133,14 +133,9 @@ let CylandRegisterPiece = React.createClass({
}, },
refreshPieceList() { refreshPieceList() {
PieceListActions.fetchPieceList( const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.page,
this.state.pageSize, PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
}, },
render() { render() {

View File

@ -53,8 +53,9 @@ let IkonotvAccordionListItem = React.createClass({
}, },
handleSubmitSuccess(response) { handleSubmitSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
let notification = new GlobalNotificationModel(response.notification, 'success', 10000); let notification = new GlobalNotificationModel(response.notification, 'success', 10000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);

View File

@ -78,8 +78,9 @@ let IkonotvPieceContainer = React.createClass({
}, },
handleDeleteSuccess(response) { handleDeleteSuccess(response) {
PieceListActions.fetchPieceList(this.state.page, this.state.pageSize, this.state.search, const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.orderBy, this.state.orderAsc, this.state.filterBy);
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
// since we're deleting a piece, we just need to close // since we're deleting a piece, we just need to close
// all editions dialogs and not reload them // all editions dialogs and not reload them

View File

@ -138,14 +138,9 @@ let IkonotvRegisterPiece = React.createClass({
}, },
refreshPieceList() { refreshPieceList() {
PieceListActions.fetchPieceList( const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.page,
this.state.pageSize, PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
}, },
canSubmit() { canSubmit() {

View File

@ -105,14 +105,9 @@ let MarketRegisterPiece = React.createClass({
}, },
refreshPieceList() { refreshPieceList() {
PieceListActions.fetchPieceList( const { filterBy, orderAsc, orderBy, page, pageSize, search } = this.state;
this.state.page,
this.state.pageSize, PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.state.searchTerm,
this.state.orderBy,
this.state.orderAsc,
this.state.filterBy
);
}, },
render() { render() {

View File

@ -9,10 +9,10 @@ let EditionListFetcher = {
/** /**
* Fetches a list of editions from the API. * Fetches a list of editions from the API.
*/ */
fetch(pieceId, page, pageSize, orderBy, orderAsc, filterBy) { fetch({ pieceId, page, pageSize, orderBy, orderAsc, filterBy }) {
let ordering = generateOrderingQueryParams(orderBy, orderAsc); const ordering = generateOrderingQueryParams(orderBy, orderAsc);
let queryParams = mergeOptions( const queryParams = mergeOptions(
{ {
page, page,
pageSize, pageSize,

View File

@ -10,12 +10,12 @@ let PieceListFetcher = {
* Fetches a list of pieces from the API. * Fetches a list of pieces from the API.
* Can be called with all supplied queryparams the API. * Can be called with all supplied queryparams the API.
*/ */
fetch(page, pageSize, search, orderBy, orderAsc, filterBy) { fetch({ page, pageSize, search, orderBy, orderAsc, filterBy }) {
let ordering = generateOrderingQueryParams(orderBy, orderAsc); const ordering = generateOrderingQueryParams(orderBy, orderAsc);
// filterBy is an object of acl key-value pairs. // filterBy is an object of acl key-value pairs.
// The values are booleans // The values are booleans
let queryParams = mergeOptions( const queryParams = mergeOptions(
{ {
page, page,
pageSize, pageSize,

View File

@ -12,7 +12,11 @@ class EditionListStore {
this.bindActions(EditionsListActions); this.bindActions(EditionsListActions);
} }
onUpdateEditionList({ pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count, filterBy }) { onUpdateEditionList({ pieceId, editionListOfPiece, page, pageSize, orderBy, orderAsc, count, filterBy, maxEdition }) {
// if editionList for a specific piece does not exist yet,
// just initialize a new array
const pieceEditionList = this.editionList[pieceId] || [];
/* /*
Basically there are two modes an edition list can be updated. Basically there are two modes an edition list can be updated.
@ -20,93 +24,107 @@ class EditionListStore {
2. The elements are already defined => merge current objects with the new ones from the server 2. The elements are already defined => merge current objects with the new ones from the server
*/ */
for (let i = 0; i < editionListOfPiece.length; i++) { editionListOfPiece.forEach((updatedEdition, index) => {
// this is the index formula for accessing an edition starting from a specific page
// if editionList for a specific piece does not exist yet, const storeEditionIndex = (page - 1) * pageSize + index;
// 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 edition already exists, just merge
if(editionsForPieces[storeEditionIndex]) { if (pieceEditionList[storeEditionIndex]) {
editionsForPieces[storeEditionIndex] = React.addons.update(editionsForPieces[storeEditionIndex], {$merge: editionListOfPiece[i]}); pieceEditionList[storeEditionIndex] = React.addons.update(pieceEditionList[storeEditionIndex], { $merge: updatedEdition });
} else { } else {
// if does not exist, assign // if does not exist, assign
editionsForPieces[storeEditionIndex] = editionListOfPiece[i]; pieceEditionList[storeEditionIndex] = updatedEdition;
}
});
// Remove editions after specified max by finding the index of the first
// edition larger than the max edition and using that to cut off the rest of the list
if (typeof maxEdition === 'number') {
const largerThanMaxIndex = pieceEditionList.findIndex(edition => edition.edition_number > maxEdition);
if (largerThanMaxIndex !== -1) {
// The API defines inflexible page buckets based on the page number
// and page size, so we cannot just arbitrarily cut off the end of
// a page and expect get the rest of it on the next pagination request.
// Hence, we use the max edition index as a guide for which page to
// cut off to so as to always provide complete pages.
page = Math.ceil(largerThanMaxIndex / pageSize);
// We only want to cut off the list if there are more editions than
// there should be (ie. we're not already at the end of the editions)
const totalPageSize = page * pageSize;
if (pieceEditionList.length > totalPageSize) {
pieceEditionList.length = totalPageSize;
}
} }
} }
const lastEdition = pieceEditionList[pieceEditionList.length - 1];
/** /**
* page, pageSize, orderBy, orderAsc and count 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; pieceEditionList.page = page;
this.editionList[pieceId].pageSize = pageSize; pieceEditionList.pageSize = pageSize;
this.editionList[pieceId].orderBy = orderBy; pieceEditionList.orderBy = orderBy;
this.editionList[pieceId].orderAsc = orderAsc; pieceEditionList.orderAsc = orderAsc;
this.editionList[pieceId].count = count; pieceEditionList.count = count;
this.editionList[pieceId].filterBy = filterBy; pieceEditionList.filterBy = filterBy;
if (pieceEditionList.maxSeen == null || lastEdition.edition_number > pieceEditionList.maxSeen) {
pieceEditionList.maxSeen = lastEdition.edition_number;
}
this.editionList[pieceId] = pieceEditionList;
} }
/** /**
* We often just have to refresh the edition list for a certain pieceId, * We often just have to refresh the edition list for a certain pieceId,
* this method provides exactly that functionality without any side effects * this method provides exactly that functionality without any side effects
*/ */
onRefreshEditionList({ pieceId, filterBy = {} }) { onRefreshEditionList({ pieceId, filterBy }) {
const pieceEditionList = this.editionList[pieceId];
// It may happen that the user enters the site logged in already // It may happen that the user enters the site logged in already
// through /editions // through /editions
// If he then tries to delete a piece/edition and this method is called, // If he then tries to delete a piece/edition and this method is called,
// we'll not be able to refresh his edition list since its not yet there. // we'll not be able to refresh his edition list since its not yet there.
// Therefore we can just return, since there is no data to be refreshed // Therefore we can just return, since there is no data to be refreshed
if(!this.editionList[pieceId]) { if (!this.editionList[pieceId]) {
return; return;
} }
let prevEditionListLength = this.editionList[pieceId].length; if (typeof filterBy !== 'object') {
let prevEditionListPage = this.editionList[pieceId].page; filterBy = pieceEditionList.filterBy;
let prevEditionListPageSize = this.editionList[pieceId].pageSize;
// we can also refresh the edition list using filterBy,
// if we decide not to do that then the old filter will just be applied.
if(filterBy && Object.keys(filterBy).length <= 0) {
filterBy = this.editionList[pieceId].filterBy;
prevEditionListLength = 10;
prevEditionListPage = 1;
prevEditionListPageSize = 10;
} }
const { maxSeen, orderAsc, orderBy, pageSize } = pieceEditionList;
// to clear an array, david walsh recommends to just set it's length to zero // to clear an array, david walsh recommends to just set it's length to zero
// http://davidwalsh.name/empty-array // http://davidwalsh.name/empty-array
this.editionList[pieceId].length = 0; pieceEditionList.length = 0;
// refetch editions with adjusted page size EditionsListActions
EditionsListActions.fetchEditionList(pieceId, 1, prevEditionListLength, .fetchEditionList({
this.editionList[pieceId].orderBy, pieceId,
this.editionList[pieceId].orderAsc, pageSize,
filterBy) orderBy,
.then(() => { orderAsc,
// reset back to the normal pageSize and page filterBy,
this.editionList[pieceId].page = prevEditionListPage; maxSeen,
this.editionList[pieceId].pageSize = prevEditionListPageSize; page: 1
}) })
.catch((err) => { .catch(console.logGlobal);
console.logGlobal(err);
});
} }
onSelectEdition({ pieceId, editionId, toValue }) { onSelectEdition({ pieceId, editionId, toValue }) {
this.editionList[pieceId].forEach((edition) => { this.editionList[pieceId].forEach((edition) => {
// Taken from: http://stackoverflow.com/a/519157/1263876 // Taken from: http://stackoverflow.com/a/519157/1263876
if(typeof toValue !== 'undefined' && edition.id === editionId) { if (typeof toValue !== 'undefined' && edition.id === editionId) {
edition.selected = toValue; edition.selected = toValue;
} else if(edition.id === editionId) { } else if(edition.id === editionId) {
if(edition.selected) { if(edition.selected) {
@ -132,7 +150,6 @@ class EditionListStore {
} }
onToggleEditionList(pieceId) { onToggleEditionList(pieceId) {
this.isEditionListOpenForPieceId[pieceId] = { this.isEditionListOpenForPieceId[pieceId] = {
show: this.isEditionListOpenForPieceId[pieceId] ? !this.isEditionListOpenForPieceId[pieceId].show : true show: this.isEditionListOpenForPieceId[pieceId] ? !this.isEditionListOpenForPieceId[pieceId].show : true
}; };
@ -141,7 +158,7 @@ class EditionListStore {
// the merge fails, as the edition list is not refreshed when closed. // the merge fails, as the edition list is not refreshed when closed.
// Therefore in the case of a filter application when closed, we need to reload the // Therefore in the case of a filter application when closed, we need to reload the
// edition list // edition list
if(!this.isEditionListOpenForPieceId[pieceId].show) { if (!this.isEditionListOpenForPieceId[pieceId].show) {
// to clear an array, david walsh recommends to just set it's length to zero // to clear an array, david walsh recommends to just set it's length to zero
// http://davidwalsh.name/empty-array // http://davidwalsh.name/empty-array

View File

@ -1,10 +0,0 @@
'use strict'
export function onChangeOnce(component, store) {
const onChange = (state) => {
component.setState(state);
store.unlisten(onChange);
};
store.listen(onChange);
}

View File

@ -48,6 +48,7 @@
"browserify": "^9.0.8", "browserify": "^9.0.8",
"browserify-shim": "^3.8.10", "browserify-shim": "^3.8.10",
"camelcase": "^1.2.1", "camelcase": "^1.2.1",
"classlist-polyfill": "^1.0.2",
"classnames": "^1.2.2", "classnames": "^1.2.2",
"compression": "^1.4.4", "compression": "^1.4.4",
"decamelize": "^1.1.1", "decamelize": "^1.1.1",

View File

@ -447,9 +447,9 @@ fieldset[disabled] .btn-secondary.active {
} }
.search-bar > .form-group > .input-group { .search-bar > .form-group > .input-group {
border: 1px solid rgba(0, 0, 0, 0); border: 1px solid $ascribe-blue-border;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
border-color: $ascribe-blue-border; border-color: $ascribe-blue-border-active;
} }
input::-webkit-input-placeholder { input::-webkit-input-placeholder {
color: rgba($ascribe-dark-blue, 0.5); color: rgba($ascribe-dark-blue, 0.5);

View File

@ -5,6 +5,7 @@ $ascribe-light-blue: #D3DEE4;
$ascribe-white: #F8F8F8; $ascribe-white: #F8F8F8;
$ascribe-pink: #D10074; $ascribe-pink: #D10074;
$ascribe-blue-border: rgba(0, 60, 105, 0.1); $ascribe-blue-border: rgba(0, 60, 105, 0.1);
$ascribe-blue-border-active: rgba(0, 60, 105, 0.2);
$ascribe-red-error: rgb(169, 68, 66); $ascribe-red-error: rgb(169, 68, 66);