diff --git a/frontend/src/Store/Selectors/createBookClientSideCollectionItemsSelector.js b/frontend/src/Store/Selectors/createBookClientSideCollectionItemsSelector.js index 9e9eab534..86a7940a1 100644 --- a/frontend/src/Store/Selectors/createBookClientSideCollectionItemsSelector.js +++ b/frontend/src/Store/Selectors/createBookClientSideCollectionItemsSelector.js @@ -1,10 +1,10 @@ import { createSelector, createSelectorCreator, defaultMemoize } from 'reselect'; import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder'; -import createClientSideCollectionSelector from './createClientSideCollectionSelector'; +import createBooksClientSideCollectionSelector from './createBooksClientSideCollectionSelector'; function createUnoptimizedSelector(uiSection) { return createSelector( - createClientSideCollectionSelector('books', uiSection), + createBooksClientSideCollectionSelector(uiSection), (books) => { const items = books.items.map((s) => { const { diff --git a/frontend/src/Store/Selectors/createBooksClientSideCollectionSelector.js b/frontend/src/Store/Selectors/createBooksClientSideCollectionSelector.js new file mode 100644 index 000000000..45513c2db --- /dev/null +++ b/frontend/src/Store/Selectors/createBooksClientSideCollectionSelector.js @@ -0,0 +1,35 @@ +import _ from 'lodash'; +import { createSelector } from 'reselect'; +import filterCollection from 'Utilities/Array/filterCollection'; +import sortCollection from 'Utilities/Array/sortCollection'; +import createCustomFiltersSelector from './createCustomFiltersSelector'; + +function createBooksClientSideCollectionSelector(uiSection) { + return createSelector( + (state) => _.get(state, 'books'), + (state) => _.get(state, 'authors'), + (state) => _.get(state, uiSection), + createCustomFiltersSelector('books', uiSection), + (bookState, authorState, uiSectionState = {}, customFilters) => { + const state = Object.assign({}, bookState, uiSectionState, { customFilters }); + + const books = state.items; + for (const book of books) { + book.author = authorState.items[authorState.itemMap[book.authorId]]; + } + + const filtered = filterCollection(books, state); + const sorted = sortCollection(filtered, state); + + return { + ...bookState, + ...uiSectionState, + customFilters, + items: sorted, + totalItems: state.items.length + }; + } + ); +} + +export default createBooksClientSideCollectionSelector; diff --git a/frontend/src/Store/Selectors/createClientSideCollectionSelector.js b/frontend/src/Store/Selectors/createClientSideCollectionSelector.js index ae1031dca..3f6fbf01e 100644 --- a/frontend/src/Store/Selectors/createClientSideCollectionSelector.js +++ b/frontend/src/Store/Selectors/createClientSideCollectionSelector.js @@ -1,123 +1,8 @@ import _ from 'lodash'; import { createSelector } from 'reselect'; -import { filterTypePredicates, filterTypes, sortDirections } from 'Helpers/Props'; -import findSelectedFilters from 'Utilities/Filter/findSelectedFilters'; - -function getSortClause(sortKey, sortDirection, sortPredicates) { - if (sortPredicates && sortPredicates.hasOwnProperty(sortKey)) { - return function(item) { - return sortPredicates[sortKey](item, sortDirection); - }; - } - - return function(item) { - return item[sortKey]; - }; -} - -function filter(items, state) { - const { - selectedFilterKey, - filters, - customFilters, - filterPredicates - } = state; - - if (!selectedFilterKey) { - return items; - } - - const selectedFilters = findSelectedFilters(selectedFilterKey, filters, customFilters); - - return _.filter(items, (item) => { - let i = 0; - let accepted = true; - - while (accepted && i < selectedFilters.length) { - const { - key, - value, - type = filterTypes.EQUAL - } = selectedFilters[i]; - - if (filterPredicates && filterPredicates.hasOwnProperty(key)) { - const predicate = filterPredicates[key]; - - if (Array.isArray(value)) { - if ( - type === filterTypes.NOT_CONTAINS || - type === filterTypes.NOT_EQUAL - ) { - accepted = value.every((v) => predicate(item, v, type)); - } else { - accepted = value.some((v) => predicate(item, v, type)); - } - } else { - accepted = predicate(item, value, type); - } - } else if (item.hasOwnProperty(key)) { - const predicate = filterTypePredicates[type]; - - if (Array.isArray(value)) { - if ( - type === filterTypes.NOT_CONTAINS || - type === filterTypes.NOT_EQUAL - ) { - accepted = value.every((v) => predicate(item[key], v)); - } else { - accepted = value.some((v) => predicate(item[key], v)); - } - } else { - accepted = predicate(item[key], value); - } - } else { - // Default to false if the filter can't be tested - accepted = false; - } - - i++; - } - - return accepted; - }); -} - -function sort(items, state) { - const { - sortKey, - sortDirection, - sortPredicates, - secondarySortKey, - secondarySortDirection - } = state; - - const clauses = []; - const orders = []; - - clauses.push(getSortClause(sortKey, sortDirection, sortPredicates)); - orders.push(sortDirection === sortDirections.ASCENDING ? 'asc' : 'desc'); - - if (secondarySortKey && - secondarySortDirection && - (sortKey !== secondarySortKey || - sortDirection !== secondarySortDirection)) { - clauses.push(getSortClause(secondarySortKey, secondarySortDirection, sortPredicates)); - orders.push(secondarySortDirection === sortDirections.ASCENDING ? 'asc' : 'desc'); - } - - return _.orderBy(items, clauses, orders); -} - -function createCustomFiltersSelector(type, alternateType) { - return createSelector( - (state) => state.customFilters.items, - (customFilters) => { - return customFilters.filter((customFilter) => { - return customFilter.type === type || customFilter.type === alternateType; - }); - } - ); -} +import filterCollection from 'Utilities/Array/filterCollection'; +import sortCollection from 'Utilities/Array/sortCollection'; +import createCustomFiltersSelector from './createCustomFiltersSelector'; function createClientSideCollectionSelector(section, uiSection) { return createSelector( @@ -127,8 +12,8 @@ function createClientSideCollectionSelector(section, uiSection) { (sectionState, uiSectionState = {}, customFilters) => { const state = Object.assign({}, sectionState, uiSectionState, { customFilters }); - const filtered = filter(state.items, state); - const sorted = sort(filtered, state); + const filtered = filterCollection(state.items, state); + const sorted = sortCollection(filtered, state); return { ...sectionState, diff --git a/frontend/src/Store/Selectors/createCustomFiltersSelector.js b/frontend/src/Store/Selectors/createCustomFiltersSelector.js new file mode 100644 index 000000000..8d8c483f0 --- /dev/null +++ b/frontend/src/Store/Selectors/createCustomFiltersSelector.js @@ -0,0 +1,14 @@ +import { createSelector } from 'reselect'; + +function createCustomFiltersSelector(type, alternateType) { + return createSelector( + (state) => state.customFilters.items, + (customFilters) => { + return customFilters.filter((customFilter) => { + return customFilter.type === type || customFilter.type === alternateType; + }); + } + ); +} + +export default createCustomFiltersSelector; diff --git a/frontend/src/Utilities/Array/filterCollection.js b/frontend/src/Utilities/Array/filterCollection.js new file mode 100644 index 000000000..b8bd03df2 --- /dev/null +++ b/frontend/src/Utilities/Array/filterCollection.js @@ -0,0 +1,72 @@ +import _ from 'lodash'; +import { filterTypePredicates, filterTypes } from 'Helpers/Props'; +import findSelectedFilters from 'Utilities/Filter/findSelectedFilters'; + +function filterCollection(items, state) { + const { + selectedFilterKey, + filters, + customFilters, + filterPredicates + } = state; + + if (!selectedFilterKey) { + return items; + } + + const selectedFilters = findSelectedFilters(selectedFilterKey, filters, customFilters); + + return _.filter(items, (item) => { + let i = 0; + let accepted = true; + + while (accepted && i < selectedFilters.length) { + const { + key, + value, + type = filterTypes.EQUAL + } = selectedFilters[i]; + + if (filterPredicates && filterPredicates.hasOwnProperty(key)) { + const predicate = filterPredicates[key]; + + if (Array.isArray(value)) { + if ( + type === filterTypes.NOT_CONTAINS || + type === filterTypes.NOT_EQUAL + ) { + accepted = value.every((v) => predicate(item, v, type)); + } else { + accepted = value.some((v) => predicate(item, v, type)); + } + } else { + accepted = predicate(item, value, type); + } + } else if (item.hasOwnProperty(key)) { + const predicate = filterTypePredicates[type]; + + if (Array.isArray(value)) { + if ( + type === filterTypes.NOT_CONTAINS || + type === filterTypes.NOT_EQUAL + ) { + accepted = value.every((v) => predicate(item[key], v)); + } else { + accepted = value.some((v) => predicate(item[key], v)); + } + } else { + accepted = predicate(item[key], value); + } + } else { + // Default to false if the filter can't be tested + accepted = false; + } + + i++; + } + + return accepted; + }); +} + +export default filterCollection; diff --git a/frontend/src/Utilities/Array/sortCollection.js b/frontend/src/Utilities/Array/sortCollection.js new file mode 100644 index 000000000..2baa3c5b7 --- /dev/null +++ b/frontend/src/Utilities/Array/sortCollection.js @@ -0,0 +1,42 @@ +import _ from 'lodash'; +import { sortDirections } from 'Helpers/Props'; + +function getSortClause(sortKey, sortDirection, sortPredicates) { + if (sortPredicates && sortPredicates.hasOwnProperty(sortKey)) { + return function(item) { + return sortPredicates[sortKey](item, sortDirection); + }; + } + + return function(item) { + return item[sortKey]; + }; +} + +function sortCollection(items, state) { + const { + sortKey, + sortDirection, + sortPredicates, + secondarySortKey, + secondarySortDirection + } = state; + + const clauses = []; + const orders = []; + + clauses.push(getSortClause(sortKey, sortDirection, sortPredicates)); + orders.push(sortDirection === sortDirections.ASCENDING ? 'asc' : 'desc'); + + if (secondarySortKey && + secondarySortDirection && + (sortKey !== secondarySortKey || + sortDirection !== secondarySortDirection)) { + clauses.push(getSortClause(secondarySortKey, secondarySortDirection, sortPredicates)); + orders.push(secondarySortDirection === sortDirections.ASCENDING ? 'asc' : 'desc'); + } + + return _.orderBy(items, clauses, orders); +} + +export default sortCollection;