From c270977eb92f3cd87ccb5ca2b20bcbbe38e930cc Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Tue, 24 Nov 2015 19:26:45 +0100 Subject: [PATCH] Fix default filter on piece list --- .../piece_list_toolbar_filter_widget.js | 2 +- js/components/piece_list.js | 121 ++++++++++++------ .../components/lumenus/lumenus_landing.js | 6 +- .../components/market/market_piece_list.js | 33 +++-- js/utils/general_utils.js | 6 + package.json | 1 + 6 files changed, 114 insertions(+), 55 deletions(-) diff --git a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js index cea41e3b..c463330c 100644 --- a/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js +++ b/js/components/ascribe_piece_list_toolbar/piece_list_toolbar_filter_widget.js @@ -28,7 +28,7 @@ let PieceListToolbarFilterWidget = React.createClass({ }, generateFilterByStatement(param) { - const { filterBy } = this.props; + const filterBy = Object.assign({}, this.props.filterBy); if(filterBy) { // we need hasOwnProperty since the values are all booleans diff --git a/js/components/piece_list.js b/js/components/piece_list.js index 1ea64ff6..758edf7c 100644 --- a/js/components/piece_list.js +++ b/js/components/piece_list.js @@ -26,7 +26,7 @@ import PieceListToolbar from './ascribe_piece_list_toolbar/piece_list_toolbar'; import AscribeSpinner from './ascribe_spinner'; import { getAvailableAcls } from '../utils/acl_utils'; -import { mergeOptions } from '../utils/general_utils'; +import { mergeOptions, isShallowEqual } from '../utils/general_utils'; import { getLangText } from '../utils/lang_utils'; import { setDocumentTitle } from '../utils/dom_utils'; @@ -35,6 +35,7 @@ let PieceList = React.createClass({ propTypes: { accordionListItemType: React.PropTypes.func, bulkModalButtonListType: React.PropTypes.func, + canLoadPieceList: React.PropTypes.bool, redirectTo: React.PropTypes.string, customSubmitButton: React.PropTypes.element, filterParams: React.PropTypes.array, @@ -49,6 +50,7 @@ let PieceList = React.createClass({ return { accordionListItemType: AccordionListItemWallet, bulkModalButtonListType: AclButtonList, + canLoadPieceList: true, orderParams: ['artist_name', 'title'], filterParams: [{ label: getLangText('Show works I can'), @@ -62,27 +64,51 @@ let PieceList = React.createClass({ }, getInitialState() { + const pieceListStore = PieceListStore.getState(); const stores = mergeOptions( - PieceListStore.getState(), - EditionListStore.getState() + pieceListStore, + EditionListStore.getState(), + { + isFilterDirty: false + } ); - // Use the default filters but use the stores' settings if they're available - stores.filterBy = Object.assign(this.getDefaultFilterBy(), stores.filterBy); + // Use the default filters but use the pieceListStore's settings if they're available + stores.filterBy = Object.assign(this.getDefaultFilterBy(), pieceListStore.filterBy); return stores; }, componentDidMount() { - let page = this.props.location.query.page || 1; - PieceListStore.listen(this.onChange); EditionListStore.listen(this.onChange); - let orderBy = this.props.orderBy ? this.props.orderBy : this.state.orderBy; - if (this.state.pieceList.length === 0 || this.state.page !== page){ - PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, - orderBy, this.state.orderAsc, this.state.filterBy); + let page = this.props.location.query.page || 1; + 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 }); } }, @@ -102,17 +128,19 @@ let PieceList = React.createClass({ this.setState(state); }, - getDefaultFilterBy() { - const { filterParams } = this.props; + getDefaultFilterBy(props = this.props) { + const { filterParams } = props; const defaultFilterBy = {}; - filterParams.forEach(({ label, items }) => { - items.forEach((item) => { - if (typeof item === 'object' && item.defaultValue) { - defaultFilterBy[item.key] = true; - } + if (filterParams && typeof filterParams.forEach === 'function') { + filterParams.forEach(({ label, items }) => { + items.forEach((item) => { + if (typeof item === 'object' && item.defaultValue) { + defaultFilterBy[item.key] = true; + } + }); }); - }); + } return defaultFilterBy; }, @@ -122,9 +150,7 @@ let PieceList = React.createClass({ // if the users clicks a pager of the pagination, // the site should go to the top document.body.scrollTop = document.documentElement.scrollTop = 0; - PieceListActions.fetchPieceList(page, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, - this.state.filterBy); + this.loadPieceList({ page }); }; }, @@ -143,29 +169,35 @@ let PieceList = React.createClass({ }, searchFor(searchTerm) { - PieceListActions.fetchPieceList(1, this.state.pageSize, searchTerm, this.state.orderBy, - this.state.orderAsc, this.state.filterBy); - this.history.pushState(null, this.props.location.pathname, {page: 1}); + this.loadPieceList({ + page: 1, + search: searchTerm + }); + this.history.pushState(null, this.props.location.pathname, {page: 1}); }, applyFilterBy(filterBy){ - // first we need to apply the filter on the piece list - PieceListActions.fetchPieceList(1, this.state.pageSize, this.state.search, - this.state.orderBy, this.state.orderAsc, filterBy) - .then(() => { - // but also, we need to filter all the open edition lists - this.state.pieceList - .forEach((piece) => { - // but only if they're actually open - if(this.state.isEditionListOpenForPieceId[piece.id].show) { - EditionListActions.refreshEditionList({ - pieceId: piece.id, - filterBy - }); - } + this.setState({ + isFilterDirty: true + }); - }); - }); + // first we need to apply the filter on the piece list + this + .loadPieceList({ page: 1, filterBy }) + .then(() => { + // but also, we need to filter all the open edition lists + this.state.pieceList + .forEach((piece) => { + // but only if they're actually open + if(this.state.isEditionListOpenForPieceId[piece.id].show) { + EditionListActions.refreshEditionList({ + pieceId: piece.id, + filterBy + }); + } + + }); + }); // we have to redirect the user always to page one as it could be that there is no page two // for filtered pieces @@ -177,6 +209,13 @@ let PieceList = React.createClass({ orderBy, this.state.orderAsc, this.state.filterBy); }, + loadPieceList({ page, filterBy = this.state.filterBy, search = this.state.search }) { + let orderBy = this.state.orderBy ? this.state.orderBy : this.props.orderBy; + + return PieceListActions.fetchPieceList(page, this.state.pageSize, search, + orderBy, this.state.orderAsc, filterBy); + }, + fetchSelectedPieceEditionList() { let filteredPieceIdList = Object.keys(this.state.editionList) .filter((pieceId) => { diff --git a/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js b/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js index 279327d5..e68b1781 100644 --- a/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js +++ b/js/components/whitelabel/wallet/components/lumenus/lumenus_landing.js @@ -21,6 +21,10 @@ let LumenusLanding = React.createClass({ ); }, + componentWillMount() { + setDocumentTitle('Lumenus Marketplace'); + }, + componentDidMount() { WhitelabelStore.listen(this.onChange); WhitelabelActions.fetchWhitelabel(); @@ -35,8 +39,6 @@ let LumenusLanding = React.createClass({ }, render() { - setDocumentTitle('Lumenus Marketplace'); - return (
diff --git a/js/components/whitelabel/wallet/components/market/market_piece_list.js b/js/components/whitelabel/wallet/components/market/market_piece_list.js index 90564f2b..64198bb3 100644 --- a/js/components/whitelabel/wallet/components/market/market_piece_list.js +++ b/js/components/whitelabel/wallet/components/market/market_piece_list.js @@ -27,6 +27,10 @@ let MarketPieceList = React.createClass({ ); }, + componentWillMount() { + setDocumentTitle(getLangText('Collection')); + }, + componentDidMount() { UserStore.listen(this.onChange); WhitelabelStore.listen(this.onChange); @@ -46,23 +50,30 @@ let MarketPieceList = React.createClass({ render() { const { currentUser, whitelabel } = this.state; - const isUserAdmin = currentUser.email === whitelabel.user; + let filterParams = undefined; + let canLoadPieceList = false; - setDocumentTitle(getLangText('Collection')); + if (currentUser.email && whitelabel.user) { + canLoadPieceList = true; + const isUserAdmin = currentUser.email === whitelabel.user; + + filterParams = [{ + label: getLangText('Show works I can'), + items: [{ + key: isUserAdmin ? 'acl_transfer' : 'acl_consign', + label: getLangText(isUserAdmin ? 'transfer' : 'consign to %s', whitelabel.name), + defaultValue: true + }] + }]; + } return ( + filterParams={filterParams} + location={this.props.location} /> ); } }); diff --git a/js/utils/general_utils.js b/js/utils/general_utils.js index 8283f8cf..e81a806d 100644 --- a/js/utils/general_utils.js +++ b/js/utils/general_utils.js @@ -1,5 +1,11 @@ 'use strict'; +/** + * Checks shallow equality + * Re-export of shallow from shallow-equals + */ +export { default as isShallowEqual } from 'shallow-equals'; + /** * Takes an object and returns a shallow copy without any keys * that fail the passed in filter function. diff --git a/package.json b/package.json index 63c6d1e0..fbae7243 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "react-star-rating": "~1.3.2", "react-textarea-autosize": "^2.5.2", "reactify": "^1.1.0", + "shallow-equals": "0.0.0", "shmui": "^0.1.0", "spark-md5": "~1.0.0", "uglifyjs": "^2.4.10",