1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00
onion/js/components/piece_list.js

358 lines
14 KiB
JavaScript
Raw Normal View History

import React from 'react';
import withRouter from 'react-router/es6/withRouter';
import PieceListStore from '../stores/piece_list_store';
import PieceListActions from '../actions/piece_list_actions';
2015-05-20 15:22:29 +02:00
2015-08-07 13:05:50 +02:00
import EditionListStore from '../stores/edition_list_store';
import EditionListActions from '../actions/edition_list_actions';
import AccordionList from './ascribe_accordion_list/accordion_list';
import AccordionListItemWallet from './ascribe_accordion_list/accordion_list_item_wallet';
import AccordionListItemTableEditions from './ascribe_accordion_list/accordion_list_item_table_editions';
import AclButtonList from './ascribe_buttons/acl_button_list.js';
import DeleteButton from './ascribe_buttons/delete_button';
2015-05-27 13:57:11 +02:00
import Pagination from './ascribe_pagination/pagination';
import PieceListFilterDisplay from './piece_list_filter_display';
import PieceListBulkModal from './ascribe_piece_list_bulk_modal/piece_list_bulk_modal';
2015-06-02 10:10:09 +02:00
import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar';
2015-10-08 11:12:15 +02:00
import AscribeSpinner from './ascribe_spinner';
import { getAvailableAcls } from '../utils/acl_utils';
2015-11-24 19:26:45 +01:00
import { mergeOptions, isShallowEqual } from '../utils/general_utils';
import { getLangText } from '../utils/lang_utils';
import { setDocumentTitle } from '../utils/dom_utils';
2015-05-20 16:44:45 +02:00
const PieceList = React.createClass({
propTypes: {
router: React.PropTypes.object.isRequired,
2015-08-11 17:12:12 +02:00
accordionListItemType: React.PropTypes.func,
bulkModalButtonListType: React.PropTypes.func,
2015-11-24 19:26:45 +01:00
canLoadPieceList: React.PropTypes.bool,
2016-02-05 10:38:59 +01:00
redirectTo: React.PropTypes.shape({
pathname: React.PropTypes.string,
query: React.PropTypes.object
}),
shouldRedirect: React.PropTypes.func,
2015-08-14 14:58:23 +02:00
customSubmitButton: React.PropTypes.element,
2015-12-04 13:52:17 +01:00
customThumbnailPlaceholder: React.PropTypes.func,
2015-08-14 14:58:23 +02:00
filterParams: React.PropTypes.array,
orderParams: React.PropTypes.array,
orderBy: React.PropTypes.string,
// Provided from AscribeApp
whitelabel: React.PropTypes.object.isRequired,
2016-01-11 16:26:36 +01:00
// Provided from router
location: React.PropTypes.object
},
getDefaultProps() {
return {
2015-08-14 14:58:23 +02:00
accordionListItemType: AccordionListItemWallet,
bulkModalButtonListType: AclButtonList,
2015-11-24 19:26:45 +01:00
canLoadPieceList: true,
2015-09-16 23:36:21 +02:00
filterParams: [{
label: getLangText('Show works I can'),
items: [
'acl_transfer',
'acl_consign',
'acl_create_editions'
]
}],
orderParams: ['artist_name', 'title'],
2016-02-05 10:38:59 +01:00
redirectTo: {
pathname: '/register_piece',
query: null
},
shouldRedirect: (pieceCount) => !pieceCount
};
},
2015-05-21 12:12:25 +02:00
getInitialState() {
2015-11-24 19:26:45 +01:00
const pieceListStore = PieceListStore.getState();
const stores = mergeOptions(
2015-11-24 19:26:45 +01:00
pieceListStore,
EditionListStore.getState(),
{
isFilterDirty: false
}
2015-08-07 13:05:50 +02:00
);
2015-11-24 19:26:45 +01:00
// Use the default filters but use the pieceListStore's settings if they're available
stores.filterBy = Object.assign(this.getDefaultFilterBy(), pieceListStore.filterBy);
return stores;
2015-05-21 12:12:25 +02:00
},
componentDidMount() {
2015-05-26 11:47:56 +02:00
PieceListStore.listen(this.onChange);
2015-08-07 13:05:50 +02:00
EditionListStore.listen(this.onChange);
const page = this.props.location.query.page || 1;
2015-11-24 19:26:45 +01:00
if (this.props.canLoadPieceList && (this.state.pieceList.length === 0 || this.state.page !== page)) {
this.loadPieceList({ page });
}
},
componentWillReceiveProps(nextProps) {
let filterBy;
let page = this.props.location.query.page || 1;
// If the user hasn't changed the filter and the new default filter is different
// than the current filter, apply the new default filter
if (!this.state.isFilterDirty) {
const newDefaultFilterBy = this.getDefaultFilterBy(nextProps);
// Only need to check shallowly since the filterBy shouldn't be nested
if (!isShallowEqual(this.state.filterBy, newDefaultFilterBy)) {
filterBy = newDefaultFilterBy;
page = 1;
}
}
// Only load if we are applying a new filter or if it's the first time we can
// load the piece list
if (nextProps.canLoadPieceList && (filterBy || !this.props.canLoadPieceList)) {
this.loadPieceList({ page, filterBy });
}
},
2015-07-13 22:05:16 +02:00
componentDidUpdate() {
const { location: { query }, redirectTo, router, shouldRedirect } = this.props;
const { unfilteredPieceListCount } = this.state;
2016-02-05 10:38:59 +01:00
if (redirectTo && redirectTo.pathname &&
(typeof shouldRedirect === 'function' && shouldRedirect(unfilteredPieceListCount))) {
// FIXME: hack to redirect out of the dispatch cycle
window.setTimeout(() => router.push({
2016-02-05 10:38:59 +01:00
// Occasionally, the back end also sets query parameters for Onion.
// We need to consider this by merging all passed query parameters, as we'll
// otherwise end up in a 404 screen
pathname: redirectTo.pathname,
query: Object.assign({}, query, redirectTo.query)
2016-02-05 10:38:59 +01:00
}), 0);
2015-07-13 22:05:16 +02:00
}
},
2015-05-22 17:11:17 +02:00
componentWillUnmount() {
PieceListStore.unlisten(this.onChange);
2015-08-07 13:05:50 +02:00
EditionListStore.unlisten(this.onChange);
2015-05-22 17:11:17 +02:00
},
2015-05-26 13:14:35 +02:00
onChange(state) {
this.setState(state);
2015-05-22 17:11:17 +02:00
},
2015-11-24 19:26:45 +01:00
getDefaultFilterBy(props = this.props) {
const { filterParams } = props;
const defaultFilterBy = {};
2015-11-24 19:26:45 +01:00
if (filterParams && typeof filterParams.forEach === 'function') {
filterParams.forEach(({ items }) => {
2015-11-24 19:26:45 +01:00
items.forEach((item) => {
if (typeof item === 'object' && item.defaultValue) {
defaultFilterBy[item.key] = true;
}
});
});
2015-11-24 19:26:45 +01:00
}
return defaultFilterBy;
},
2015-05-22 12:58:06 +02:00
paginationGoToPage(page) {
2015-08-07 13:05:50 +02:00
return () => {
// if the users clicks a pager of the pagination,
// the site should go to the top
document.body.scrollTop = document.documentElement.scrollTop = 0;
2015-11-24 19:26:45 +01:00
this.loadPieceList({ page });
2015-08-07 13:05:50 +02:00
};
},
getPagination() {
const currentPage = parseInt(this.props.location.query.page, 10) || 1;
const totalPages = Math.ceil(this.state.pieceListCount / this.state.pageSize);
2015-10-09 02:00:02 +02:00
if (this.state.pieceListCount > 20) {
return (
<Pagination
currentPage={currentPage}
totalPages={totalPages}
goToPage={this.paginationGoToPage} />
);
}
},
searchFor(search) {
const { location: { pathname }, router } = this.props;
this.loadPieceList({ search, page: 1 });
router.push({ pathname, query: { page: 1 } });
2015-06-16 09:27:04 +02:00
},
applyFilterBy(filterBy) {
const { location: { pathname }, router } = this.props;
2015-11-24 19:26:45 +01:00
this.setState({
isFilterDirty: true
});
2015-08-07 13:05:50 +02:00
// first we need to apply the filter on the piece list
this.loadPieceList({ page: 1, filterBy })
2015-11-24 19:26:45 +01:00
.then(() => {
// but also, we need to filter all the open edition lists
this.state.pieceList
.forEach((piece) => {
// but only if they're actually open
2015-12-09 18:02:14 +01:00
const isEditionListOpenForPiece = this.state.isEditionListOpenForPieceId[piece.id];
if (isEditionListOpenForPiece && isEditionListOpenForPiece.show) {
2015-11-24 19:26:45 +01:00
EditionListActions.refreshEditionList({
pieceId: piece.id,
filterBy
});
}
});
});
2015-08-07 13:05:50 +02:00
2015-08-06 13:15:52 +02:00
// we have to redirect the user always to page one as it could be that there is no page two
// for filtered pieces
router.push({ pathname, query: { page: 1 } });
2015-08-06 13:15:52 +02:00
},
applyOrderBy(orderBy) {
2015-12-08 15:09:03 +01:00
const { filterBy, orderAsc, page, pageSize, search } = this.state;
2016-01-18 10:53:01 +01:00
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
2015-05-22 12:58:06 +02:00
},
2015-11-24 19:26:45 +01:00
loadPieceList({ page, filterBy = this.state.filterBy, search = this.state.search }) {
2015-12-08 15:09:03 +01:00
const { orderAsc, pageSize } = this.state;
const orderBy = this.state.orderBy || this.props.orderBy;
2015-11-24 19:26:45 +01:00
2016-01-18 10:53:01 +01:00
return PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
2015-11-24 19:26:45 +01:00
},
fetchSelectedPieceEditionList() {
const filteredPieceIdList = Object.keys(this.state.editionList)
.filter((pieceId) => {
return this.state.editionList[pieceId]
.filter((edition) => edition.selected)
.length;
});
return filteredPieceIdList;
},
fetchSelectedEditionList() {
const selectedEditionList = Object.keys(this.state.editionList)
.reduce((selectedList, pieceId) => {
const selectedEditionsForPiece = this.state.editionList[pieceId]
.filter((edition) => edition.selected);
return selectedList.concat(selectedEditionsForPiece);
}, []);
return selectedEditionList;
},
handleAclSuccess() {
2015-12-08 15:09:03 +01:00
const { filterBy, orderBy, orderAsc, page, pageSize, search } = this.state;
2016-01-18 10:53:01 +01:00
PieceListActions.fetchPieceList({ page, pageSize, search, orderBy, orderAsc, filterBy });
this.fetchSelectedPieceEditionList()
.forEach((pieceId) => {
EditionListActions.refreshEditionList({ pieceId });
});
EditionListActions.clearAllEditionSelections();
},
render() {
const { accordionListItemType: AccordionListItemType,
bulkModalButtonListType: BulkModalButtonListType,
customSubmitButton,
customThumbnailPlaceholder,
filterParams,
2016-02-08 14:08:54 +01:00
orderParams,
whitelabel } = this.props;
2015-12-02 19:31:50 +01:00
const loadingElement = <AscribeSpinner color='dark-blue' size='lg'/>;
const selectedEditions = this.fetchSelectedEditionList();
const availableAcls = getAvailableAcls(selectedEditions, (aclName) => aclName !== 'acl_view');
2015-08-28 15:24:32 +02:00
setDocumentTitle(getLangText('Collection'));
return (
<div>
2015-07-14 18:54:47 +02:00
<PieceListToolbar
className="ascribe-piece-list-toolbar"
2015-08-06 13:15:52 +02:00
searchFor={this.searchFor}
searchQuery={this.state.search}
2015-12-02 19:31:50 +01:00
filterParams={filterParams}
orderParams={orderParams}
2015-08-06 13:15:52 +02:00
filterBy={this.state.filterBy}
2015-08-14 14:58:23 +02:00
orderBy={this.state.orderBy}
applyFilterBy={this.applyFilterBy}
applyOrderBy={this.applyOrderBy}>
{customSubmitButton}
2015-07-14 21:13:15 +02:00
</PieceListToolbar>
2015-10-21 17:37:29 +02:00
<PieceListBulkModal
availableAcls={availableAcls}
selectedEditions={selectedEditions}
className="ascribe-piece-list-bulk-modal">
<BulkModalButtonListType
availableAcls={availableAcls}
handleSuccess={this.handleAclSuccess}
pieceOrEditions={selectedEditions}
whitelabel={whitelabel}
className="text-center ascribe-button-list collapse-group">
<DeleteButton
handleSuccess={this.handleAclSuccess}
2016-02-05 10:38:59 +01:00
editions={selectedEditions} />
</BulkModalButtonListType>
</PieceListBulkModal>
2015-09-16 23:36:21 +02:00
<PieceListFilterDisplay
filterBy={this.state.filterBy}
2016-02-05 10:38:59 +01:00
filterParams={filterParams} />
<AccordionList
className="ascribe-accordion-list"
changeOrder={this.accordionChangeOrder}
itemList={this.state.pieceList}
2015-07-07 09:22:46 +02:00
count={this.state.pieceListCount}
orderBy={this.state.orderBy}
orderAsc={this.state.orderAsc}
search={this.state.search}
searchFor={this.searchFor}
page={this.state.page}
2015-06-16 09:57:14 +02:00
pageSize={this.state.pageSize}
loadingElement={loadingElement}>
{this.state.pieceList.map((piece) => {
return (
<AccordionListItemType
key={piece.id}
className="col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 ascribe-accordion-list-item"
content={piece}
2015-12-02 19:31:50 +01:00
thumbnailPlaceholder={customThumbnailPlaceholder}
whitelabel={whitelabel}>
<AccordionListItemTableEditions
2015-07-09 16:29:10 +02:00
className="ascribe-accordion-list-item-table col-xs-12 col-sm-10 col-md-8 col-lg-8 col-sm-offset-1 col-md-offset-2 col-lg-offset-2"
parentId={piece.id} />
</AccordionListItemType>
);
})}
</AccordionList>
{this.getPagination()}
</div>
);
}
});
export default withRouter(PieceList);