New: Custom Filtering for UI (#234)

This commit is contained in:
Qstick 2018-03-14 21:28:46 -04:00 committed by GitHub
parent c6873014c7
commit 7354e02bff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
154 changed files with 3498 additions and 1370 deletions

View file

@ -0,0 +1,65 @@
import customFilterHandlers from 'Utilities/customFilterHandlers';
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
import generateUUIDv4 from 'Utilities/String/generateUUIDv4';
function createRemoveCustomFilterReducer(section) {
return (state, { payload }) => {
const newState = getSectionState(state, section);
const index = newState.customFilters.findIndex((c) => c.key === payload.key);
newState.customFilters = [...newState.customFilters];
newState.customFilters.splice(index, 1);
// Reset the selected filter to the first filter if the selected filter
// is being deleted.
// TODO: Server side collections need to have their collections refetched
if (newState.selectedFilterKey === payload.key) {
newState.selectedFilterKey = newState.filters[0].key;
}
return updateSectionState(state, section, newState);
};
}
function createSaveCustomFilterReducer(section) {
return (state, { payload }) => {
const newState = getSectionState(state, section);
const {
label,
filters
} = payload;
let key = payload.key;
newState.customFilters = [...newState.customFilters];
if (key) {
const index = newState.customFilters.findIndex((c) => c.key === key);
newState.customFilters.splice(index, 1, { key, label, filters });
} else {
key = generateUUIDv4();
newState.customFilters.push({
key,
label,
filters
});
}
// TODO: Server side collections need to have their collections refetched
newState.selectedFilterKey = key;
return updateSectionState(state, section, newState);
};
}
export default function createCustomFilterReducers(section, handlers) {
return {
[handlers[customFilterHandlers.REMOVE]]: createRemoveCustomFilterReducer(section),
[handlers[customFilterHandlers.SAVE]]: createSaveCustomFilterReducer(section)
};
}

View file

@ -1,14 +1,11 @@
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
import { filterTypes } from 'Helpers/Props';
function createSetClientSideCollectionFilterReducer(section) {
return (state, { payload }) => {
const newState = getSectionState(state, section);
newState.filterKey = payload.filterKey;
newState.filterValue = payload.filterValue;
newState.filterType = payload.filterType || filterTypes.EQUAL;
newState.selectedFilterKey = payload.selectedFilterKey;
return updateSectionState(state, section, newState);
};

View file

@ -2,40 +2,40 @@ import $ from 'jquery';
import updateAlbums from 'Utilities/Album/updateAlbums';
import getSectionState from 'Utilities/State/getSectionState';
function createBatchToggleAlbumMonitoredHandler(section) {
return function(payload) {
return function(dispatch, getState) {
const {
albumIds,
function createBatchToggleAlbumMonitoredHandler(section, fetchHandler) {
return function(getState, payload, dispatch) {
const {
albumIds,
monitored
} = payload;
const state = getSectionState(getState(), section, true);
dispatch(updateAlbums(section, state.items, albumIds, {
isSaving: true
}));
const promise = $.ajax({
url: '/album/monitor',
method: 'PUT',
data: JSON.stringify({ albumIds, monitored }),
dataType: 'json'
});
promise.done(() => {
dispatch(updateAlbums(section, state.items, albumIds, {
isSaving: false,
monitored
} = payload;
}));
const state = getSectionState(getState(), section, true);
dispatch(fetchHandler());
});
updateAlbums(dispatch, section, state.items, albumIds, {
isSaving: true
});
const promise = $.ajax({
url: '/album/monitor',
method: 'PUT',
data: JSON.stringify({ albumIds, monitored }),
dataType: 'json'
});
promise.done(() => {
updateAlbums(dispatch, section, state.items, albumIds, {
isSaving: false,
monitored
});
});
promise.fail(() => {
updateAlbums(dispatch, section, state.items, albumIds, {
isSaving: false
});
});
};
promise.fail(() => {
dispatch(updateAlbums(section, state.items, albumIds, {
isSaving: false
}));
});
};
}

View file

@ -1,6 +1,7 @@
import _ from 'lodash';
import $ from 'jquery';
import { batchActions } from 'redux-batched-actions';
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
import getSectionState from 'Utilities/State/getSectionState';
import { set, updateServerSideCollection } from '../baseActions';
@ -15,11 +16,21 @@ function createFetchServerSideCollectionHandler(section, url) {
_.pick(sectionState, [
'pageSize',
'sortDirection',
'sortKey',
'filterKey',
'filterValue'
'sortKey'
]));
const {
selectedFilterKey,
filters,
customFilters
} = sectionState;
const selectedFilters = findSelectedFilters(selectedFilterKey, filters, customFilters);
selectedFilters.forEach((filter) => {
data[filter.key] = filter.value;
});
const promise = $.ajax({
url,
data

View file

@ -1,42 +0,0 @@
import $ from 'jquery';
import updateAlbums from 'Utilities/Album/updateAlbums';
import getSectionState from 'Utilities/State/getSectionState';
function createToggleAlbumMonitoredHandler(section) {
return function(payload) {
return function(dispatch, getState) {
const {
albumId,
monitored
} = payload;
const state = getSectionState(getState(), section, true);
updateAlbums(dispatch, section, state.items, [albumId], {
isSaving: true
});
const promise = $.ajax({
url: `/album/${albumId}`,
method: 'PUT',
data: JSON.stringify({ monitored }),
dataType: 'json'
});
promise.done(() => {
updateAlbums(dispatch, section, state.items, [albumId], {
isSaving: false,
monitored
});
});
promise.fail(() => {
updateAlbums(dispatch, section, state.items, [albumId], {
isSaving: false
});
});
};
};
}
export default createToggleAlbumMonitoredHandler;

View file

@ -46,10 +46,9 @@ export const actionHandlers = handleThunks({
const queryParams = {
pageSize: 1000,
page: 1,
filterKey: 'albumId',
filterValue: payload.albumId,
sortKey: 'date',
sortDirection: sortDirections.DESCENDING
sortDirection: sortDirections.DESCENDING,
albumId: payload.albumId
};
const promise = $.ajax({

View file

@ -2,7 +2,7 @@ import _ from 'lodash';
import $ from 'jquery';
import { createAction } from 'redux-actions';
import getMonitoringOptions from 'Utilities/Artist/getMonitoringOptions';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
@ -25,17 +25,17 @@ export const defaultState = {
sortDirection: sortDirections.ASCENDING,
secondarySortKey: 'sortName',
secondarySortDirection: sortDirections.ASCENDING,
filterKey: null,
filterValue: null,
filterType: filterTypes.EQUAL
selectedFilterKey: 'all',
// filters come from artistActions
customFilters: []
// filterPredicates come from artistActions
};
export const persistState = [
'albumStudio.sortKey',
'albumStudio.sortDirection',
'albumStudio.filterKey',
'albumStudio.filterValue',
'albumStudio.filterType'
'albumStudio.selectedFilterKey',
'albumStudio.customFilters'
];
//

View file

@ -2,7 +2,7 @@ import _ from 'lodash';
import $ from 'jquery';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { sortDirections } from 'Helpers/Props';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createSetSettingValueReducer from './Creators/Reducers/createSetSettingValueReducer';
import createFetchHandler from './Creators/createFetchHandler';
@ -28,6 +28,75 @@ export const defaultState = {
items: [],
sortKey: 'sortName',
sortDirection: sortDirections.ASCENDING,
filters: [
{
key: 'all',
label: 'All',
filters: []
},
{
key: 'monitored',
label: 'Monitored Only',
filters: [
{
key: 'monitored',
value: true,
type: filterTypes.EQUAL
}
]
},
{
key: 'unmonitored',
label: 'Unmonitored Only',
filters: [
{
key: 'monitored',
value: false,
type: filterTypes.EQUAL
}
]
},
{
key: 'continuing',
label: 'Continuing Only',
filters: [
{
key: 'status',
value: 'continuing',
type: filterTypes.EQUAL
}
]
},
{
key: 'ended',
label: 'Ended Only',
filters: [
{
key: 'status',
value: 'ended',
type: filterTypes.EQUAL
}
]
},
{
key: 'missing',
label: 'Missing Albums',
filters: [
{
key: 'missing',
value: true,
type: filterTypes.EQUAL
}
]
}
],
filterPredicates: {
missing: function(item) {
return item.statistics.trackCount - item.statistics.trackFileCount > 0;
}
},
pendingChanges: {}
};

View file

@ -1,7 +1,7 @@
import $ from 'jquery';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
@ -25,17 +25,17 @@ export const defaultState = {
sortDirection: sortDirections.ASCENDING,
secondarySortKey: 'sortName',
secondarySortDirection: sortDirections.ASCENDING,
filterKey: null,
filterValue: null,
filterType: filterTypes.EQUAL
selectedFilterKey: 'all',
// filters come from artistActions
customFilters: []
// filterPredicates come from artistActions
};
export const persistState = [
'artistEditor.sortKey',
'artistEditor.sortDirection',
'artistEditor.filterKey',
'artistEditor.filterValue',
'artistEditor.filterType'
'artistEditor.selectedFilterKey',
'artistEditor.customFilters'
];
//

View file

@ -1,6 +1,6 @@
import moment from 'moment';
import { createAction } from 'redux-actions';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { sortDirections } from 'Helpers/Props';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
@ -19,9 +19,6 @@ export const defaultState = {
sortDirection: sortDirections.ASCENDING,
secondarySortKey: 'sortName',
secondarySortDirection: sortDirections.ASCENDING,
filterKey: null,
filterValue: null,
filterType: filterTypes.EQUAL,
view: 'posters',
posterOptions: {
@ -155,45 +152,41 @@ export const defaultState = {
],
sortPredicates: {
nextAiring: function(item, direction) {
const nextAiring = item.nextAiring;
if (nextAiring) {
return moment(nextAiring).unix();
}
if (direction === sortDirections.DESCENDING) {
return 0;
}
return Number.MAX_VALUE;
},
trackProgress: function(item) {
const {
trackCount = 0,
trackFileCount
} = item;
} = item.statistics;
const progress = trackCount ? trackFileCount / trackCount * 100 : 100;
return progress + trackCount / 1000000;
},
albumCount: function(item) {
return item.statistics.albumCount;
},
trackCount: function(item) {
return item.statistics.totalTrackCount;
},
sizeOnDisk: function(item) {
return item.statistics.sizeOnDisk;
}
},
filterPredicates: {
missing: function(item) {
return item.trackCount - item.trackFileCount > 0;
}
}
selectedFilterKey: 'all',
// filters come from artistActions
customFilters: []
// filterPredicates come from artistActions
};
export const persistState = [
'artistIndex.sortKey',
'artistIndex.sortDirection',
'artistIndex.filterKey',
'artistIndex.filterValue',
'artistIndex.filterType',
'artistIndex.selectedFilterKey',
'artistIndex.customFilters',
'artistIndex.view',
'artistIndex.columns',
'artistIndex.posterOptions',

View file

@ -3,6 +3,7 @@ import $ from 'jquery';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import moment from 'moment';
import { filterTypes } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import * as calendarViews from 'Calendar/calendarViews';
import createHandleActions from './Creators/createHandleActions';
@ -31,16 +32,42 @@ export const defaultState = {
dates: [],
dayCount: 7,
view: window.innerWidth > 768 ? 'week' : 'day',
unmonitored: false,
showUpcoming: true,
error: null,
items: []
items: [],
selectedFilterKey: 'all',
filters: [
{
key: 'all',
label: 'All',
filters: [
{
key: 'unmonitored',
value: false,
type: filterTypes.EQUAL
}
]
},
{
key: 'unmonitored',
label: 'Unmonitored',
filters: [
{
key: 'unmonitored',
value: true,
type: filterTypes.EQUAL
}
]
}
]
};
export const persistState = [
'calendar.view',
'calendar.unmonitored',
'calendar.showUpcoming'
'calendar.showUpcoming',
'calendar.selectedFilterKey'
];
//
@ -48,8 +75,8 @@ export const persistState = [
export const FETCH_CALENDAR = 'calendar/fetchCalendar';
export const SET_CALENDAR_DAYS_COUNT = 'calendar/setCalendarDaysCount';
export const SET_CALENDAR_INCLUDE_UNMONITORED = 'calendar/setCalendarIncludeUnmonitored';
export const SET_CALENDAR_VIEW = 'calendar/setCalendarView';
export const SET_CALENDAR_FILTER = 'calendar/setCalendarFilter';
export const GOTO_CALENDAR_TODAY = 'calendar/gotoCalendarToday';
export const GOTO_CALENDAR_PREVIOUS_RANGE = 'calendar/gotoCalendarPreviousRange';
export const GOTO_CALENDAR_NEXT_RANGE = 'calendar/gotoCalendarNextRange';
@ -155,8 +182,8 @@ function isRangePopulated(start, end, state) {
export const fetchCalendar = createThunk(FETCH_CALENDAR);
export const setCalendarDaysCount = createThunk(SET_CALENDAR_DAYS_COUNT);
export const setCalendarIncludeUnmonitored = createThunk(SET_CALENDAR_INCLUDE_UNMONITORED);
export const setCalendarView = createThunk(SET_CALENDAR_VIEW);
export const setCalendarFilter = createThunk(SET_CALENDAR_FILTER);
export const gotoCalendarToday = createThunk(GOTO_CALENDAR_TODAY);
export const gotoCalendarPreviousRange = createThunk(GOTO_CALENDAR_PREVIOUS_RANGE);
export const gotoCalendarNextRange = createThunk(GOTO_CALENDAR_NEXT_RANGE);
@ -166,9 +193,11 @@ export const clearCalendar = createAction(CLEAR_CALENDAR);
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_CALENDAR]: function(getState, payload, dispatch) {
const state = getState();
const unmonitored = state.calendar.unmonitored;
const selectedFilter = state.calendar.selectedFilterKey;
const unmonitored = state.calendar.filters.find((f) => f.key === selectedFilter).filters[0].value;
const {
time,
@ -245,18 +274,6 @@ export const actionHandlers = handleThunks({
dispatch(fetchCalendar({ time, view }));
},
[SET_CALENDAR_INCLUDE_UNMONITORED]: function(getState, payload, dispatch) {
dispatch(set({
section,
unmonitored: payload.unmonitored
}));
const state = getState();
const { time, view } = state.calendar;
dispatch(fetchCalendar({ time, view }));
},
[SET_CALENDAR_VIEW]: function(getState, payload, dispatch) {
const state = getState();
const view = payload.view;
@ -300,6 +317,18 @@ export const actionHandlers = handleThunks({
const amount = view === calendarViews.FORECAST ? dayCount : 1;
const time = moment(state.calendar.time).add(amount, viewRanges[view]);
dispatch(fetchCalendar({ time, view }));
},
[SET_CALENDAR_FILTER]: function(getState, payload, dispatch) {
dispatch(set({
section,
selectedFilterKey: payload.selectedFilterKey
}));
const state = getState();
const { time, view } = state.calendar;
dispatch(fetchCalendar({ time, view }));
}
});

View file

@ -1,7 +1,7 @@
import $ from 'jquery';
import { createAction } from 'redux-actions';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers';
import { sortDirections } from 'Helpers/Props';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createClearReducer from './Creators/Reducers/createClearReducer';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
@ -24,8 +24,6 @@ export const defaultState = {
pageSize: 20,
sortKey: 'date',
sortDirection: sortDirections.DESCENDING,
filterKey: null,
filterValue: null,
items: [],
columns: [
@ -89,15 +87,80 @@ export const defaultState = {
isVisible: true,
isModifiable: false
}
],
selectedFilterKey: 'all',
filters: [
{
key: 'all',
label: 'All',
filters: []
},
{
key: 'grabbed',
label: 'Grabbed',
filters: [
{
key: 'eventType',
value: '1',
type: filterTypes.EQUAL
}
]
},
{
key: 'imported',
label: 'Imported',
filters: [
{
key: 'eventType',
value: '3',
type: filterTypes.EQUAL
}
]
},
{
key: 'failed',
label: 'Failed',
filters: [
{
key: 'eventType',
value: '4',
type: filterTypes.EQUAL
}
]
},
{
key: 'deleted',
label: 'Deleted',
filters: [
{
key: 'eventType',
value: '5',
type: filterTypes.EQUAL
}
]
},
{
key: 'renamed',
label: 'Renamed',
filters: [
{
key: 'eventType',
value: '6',
type: filterTypes.EQUAL
}
]
}
]
};
export const persistState = [
'history.pageSize',
'history.sortKey',
'history.sortDirection',
'history.filterKey',
'history.filterValue'
'history.selectedFilterKey'
];
//

View file

@ -1,8 +1,11 @@
import $ from 'jquery';
import { createAction } from 'redux-actions';
import { sortDirections } from 'Helpers/Props';
import customFilterHandlers from 'Utilities/customFilterHandlers';
import { filterBuilderTypes, filterBuilderValueTypes, filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
import createCustomFilterReducers from './Creators/Reducers/createCustomFilterReducers';
import createFetchHandler from './Creators/createFetchHandler';
import createHandleActions from './Creators/createHandleActions';
@ -40,9 +43,116 @@ export const defaultState = {
return releaseWeight;
}
}
},
selectedFilterKey: 'all',
filters: [
{
key: 'all',
label: 'All',
filters: []
},
{
key: 'discography-pack',
label: 'Discography',
filters: [
{
key: 'discography',
value: true,
type: filterTypes.EQUAL
}
]
},
{
key: 'not-discography-pack',
label: 'Not Discography',
filters: [
{
key: 'discography',
value: false,
type: filterTypes.EQUAL
}
]
}
],
filterPredicates: {
quality: function(item, value, type) {
const qualityId = item.quality.quality.id;
if (type === filterTypes.EQUAL) {
return qualityId === value;
}
if (type === filterTypes.NOT_EQUAL) {
return qualityId !== value;
}
// Default to false
return false;
}
},
filterBuilderProps: [
{
name: 'title',
label: 'Title',
type: filterBuilderTypes.STRING
},
{
name: 'age',
label: 'Age',
type: filterBuilderTypes.NUMBER
},
{
name: 'protocol',
label: 'Protocol',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.PROTOCOL
},
{
name: 'indexerId',
label: 'Indexer',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.INDEXER
},
{
name: 'size',
label: 'Size',
type: filterBuilderTypes.NUMBER
},
{
name: 'seeders',
label: 'Seeders',
type: filterBuilderTypes.NUMBER
},
{
name: 'leechers',
label: 'Peers',
type: filterBuilderTypes.NUMBER
},
{
name: 'quality',
label: 'Quality',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.QUALITY
},
{
name: 'rejections',
label: 'Rejections',
type: filterBuilderTypes.NUMBER
}
],
customFilters: []
};
export const persistState = [
'releases.selectedFilterKey',
'releases.customFilters'
];
//
// Actions Types
@ -52,6 +162,10 @@ export const SET_RELEASES_SORT = 'releases/setReleasesSort';
export const CLEAR_RELEASES = 'releases/clearReleases';
export const GRAB_RELEASE = 'releases/grabRelease';
export const UPDATE_RELEASE = 'releases/updateRelease';
export const SET_RELEASES_FILTER = 'releases/setReleasesFilter';
export const ADD_RELEASES_CUSTOM_FILTER = 'releases/addReleasesCustomFilter';
export const REMOVE_RELEASES_CUSTOM_FILTER = 'releases/removeReleasesCustomFilter';
export const SAVE_RELEASES_CUSTOM_FILTER = 'releases/saveReleasesCustomFilter';
//
// Action Creators
@ -62,6 +176,10 @@ export const setReleasesSort = createAction(SET_RELEASES_SORT);
export const clearReleases = createAction(CLEAR_RELEASES);
export const grabRelease = createThunk(GRAB_RELEASE);
export const updateRelease = createAction(UPDATE_RELEASE);
export const setReleasesFilter = createAction(SET_RELEASES_FILTER);
export const addReleasesCustomFilter = createAction(ADD_RELEASES_CUSTOM_FILTER);
export const removeReleasesCustomFilter = createAction(REMOVE_RELEASES_CUSTOM_FILTER);
export const saveReleasesCustomFilter = createAction(SAVE_RELEASES_CUSTOM_FILTER);
//
// Helpers
@ -147,6 +265,12 @@ export const reducers = createHandleActions({
return newState;
},
[SET_RELEASES_SORT]: createSetClientSideCollectionSortReducer(section)
[SET_RELEASES_SORT]: createSetClientSideCollectionSortReducer(section),
[SET_RELEASES_FILTER]: createSetClientSideCollectionFilterReducer(section),
...createCustomFilterReducers(section, {
[customFilterHandlers.REMOVE]: REMOVE_RELEASES_CUSTOM_FILTER,
[customFilterHandlers.SAVE]: SAVE_RELEASES_CUSTOM_FILTER
})
}, defaultState, section);

View file

@ -1,7 +1,7 @@
import $ from 'jquery';
import { createAction } from 'redux-actions';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers';
import { sortDirections } from 'Helpers/Props';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import { setAppValue } from 'Store/Actions/appActions';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
@ -73,8 +73,6 @@ export const defaultState = {
pageSize: 50,
sortKey: 'time',
sortDirection: sortDirections.DESCENDING,
filterKey: null,
filterValue: null,
error: null,
items: [],
@ -108,6 +106,49 @@ export const defaultState = {
isVisible: true,
isModifiable: false
}
],
selectedFilterKey: 'all',
filters: [
{
key: 'all',
label: 'All',
filters: []
},
{
key: 'info',
label: 'Info',
filters: [
{
key: 'level',
value: 'info',
type: filterTypes.EQUAL
}
]
},
{
key: 'warn',
label: 'Warn',
filters: [
{
key: 'level',
value: 'warn',
type: filterTypes.EQUAL
}
]
},
{
key: 'error',
label: 'Error',
filters: [
{
key: 'level',
value: 'error',
type: filterTypes.EQUAL
}
]
}
]
},
@ -130,8 +171,7 @@ export const persistState = [
'system.logs.pageSize',
'system.logs.sortKey',
'system.logs.sortDirection',
'system.logs.filterKey',
'system.logs.filterValue'
'system.logs.selectedFilterKey'
];
//

View file

@ -1,6 +1,6 @@
import { createAction } from 'redux-actions';
import serverSideCollectionHandlers from 'Utilities/serverSideCollectionHandlers';
import { sortDirections } from 'Helpers/Props';
import { filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createClearReducer from './Creators/Reducers/createClearReducer';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
@ -23,8 +23,6 @@ export const defaultState = {
pageSize: 20,
sortKey: 'releaseDate',
sortDirection: sortDirections.DESCENDING,
filterKey: 'monitored',
filterValue: 'true',
error: null,
items: [],
@ -68,6 +66,33 @@ export const defaultState = {
isVisible: true,
isModifiable: false
}
],
selectedFilterKey: 'monitored',
filters: [
{
key: 'monitored',
label: 'Monitored',
filters: [
{
key: 'monitored',
value: true,
type: filterTypes.EQUAL
}
]
},
{
key: 'unmonitored',
label: 'Unmonitored',
filters: [
{
key: 'monitored',
value: false,
type: filterTypes.EQUAL
}
]
}
]
},
@ -77,9 +102,6 @@ export const defaultState = {
pageSize: 20,
sortKey: 'releaseDate',
sortDirection: sortDirections.DESCENDING,
filterKey: 'monitored',
filterValue: true,
error: null,
items: [],
columns: [
@ -127,6 +149,33 @@ export const defaultState = {
isVisible: true,
isModifiable: false
}
],
selectedFilterKey: 'monitored',
filters: [
{
key: 'monitored',
label: 'Monitored',
filters: [
{
key: 'monitored',
value: true,
type: filterTypes.EQUAL
}
]
},
{
key: 'unmonitored',
label: 'Unmonitored',
filters: [
{
key: 'monitored',
value: false,
type: filterTypes.EQUAL
}
]
}
]
}
};
@ -135,14 +184,12 @@ export const persistState = [
'wanted.missing.pageSize',
'wanted.missing.sortKey',
'wanted.missing.sortDirection',
'wanted.missing.filterKey',
'wanted.missing.filterValue',
'wanted.missing.selectedFilterKey',
'wanted.missing.columns',
'wanted.cutoffUnmet.pageSize',
'wanted.cutoffUnmet.sortKey',
'wanted.cutoffUnmet.sortDirection',
'wanted.cutoffUnmet.filterKey',
'wanted.cutoffUnmet.filterValue',
'wanted.cutoffUnmet.selectedFilterKey',
'wanted.cutoffUnmet.columns'
];
@ -225,7 +272,7 @@ export const actionHandlers = handleThunks({
}
),
[BATCH_TOGGLE_MISSING_ALBUMS]: createBatchToggleAlbumMonitoredHandler('wanted.missing'),
[BATCH_TOGGLE_MISSING_ALBUMS]: createBatchToggleAlbumMonitoredHandler('wanted.missing', fetchMissing),
...createServerSideCollectionHandlers(
'wanted.cutoffUnmet',
@ -243,7 +290,7 @@ export const actionHandlers = handleThunks({
}
),
[BATCH_TOGGLE_CUTOFF_UNMET_ALBUMS]: createBatchToggleAlbumMonitoredHandler('wanted.cutoffUnmet')
[BATCH_TOGGLE_CUTOFF_UNMET_ALBUMS]: createBatchToggleAlbumMonitoredHandler('wanted.cutoffUnmet', fetchCutoffUnmet)
});

View file

@ -1,10 +1,11 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
import { filterTypes, sortDirections } from 'Helpers/Props';
const filterTypePredicates = {
[filterTypes.CONTAINS]: function(value, filterValue) {
return value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1;
return value.toLowerCase().contains(filterValue.toLowerCase());
},
[filterTypes.EQUAL]: function(value, filterValue) {
@ -46,26 +47,54 @@ function getSortClause(sortKey, sortDirection, sortPredicates) {
function filter(items, state) {
const {
filterKey,
filterValue,
filterType,
selectedFilterKey,
filters,
customFilters,
filterPredicates
} = state;
if (!filterKey || !filterValue) {
if (!selectedFilterKey) {
return items;
}
const selectedFilters = findSelectedFilters(selectedFilterKey, filters, customFilters);
return _.filter(items, (item) => {
if (filterPredicates && filterPredicates.hasOwnProperty(filterKey)) {
return filterPredicates[filterKey](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)) {
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)) {
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++;
}
if (item.hasOwnProperty(filterKey)) {
return filterTypePredicates[filterType](item[filterKey], filterValue);
}
return false;
return accepted;
});
}