New: UI Updates, Tag manager, More custom filters (#437)

* New: UI Updates, Tag manager, More custom filters

* fixup! Fix ScanFixture Unit Tests

* Fixed: Sentry Errors from UI don't have release, branch, environment

* Changed: Bump Mobile Detect for New Device Detection

* Fixed: Build on changes to package.json

* fixup! Add MetadataProfile filter option

* fixup! Tag Note, Blacklist, Manual Import

* fixup: Remove connectSection

* fixup: root folder comment
This commit is contained in:
Qstick 2018-08-07 20:57:15 -04:00 committed by GitHub
parent afa78b1d20
commit 6581b3a2c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
198 changed files with 3057 additions and 888 deletions

View file

@ -14,7 +14,7 @@ function createSetSettingValueReducer(section) {
let parsedValue = null;
if (_.isNumber(currentValue)) {
if (_.isNumber(currentValue) && value != null) {
parsedValue = parseInt(value);
} else {
parsedValue = value;

View file

@ -9,6 +9,7 @@ import createSetClientSideCollectionFilterReducer from './Creators/Reducers/crea
import createHandleActions from './Creators/createHandleActions';
import { set } from './baseActions';
import { fetchAlbums } from './albumActions';
import { fetchArtist, filters, filterPredicates } from './artistActions';
//
// Variables
@ -26,9 +27,9 @@ export const defaultState = {
secondarySortKey: 'sortName',
secondarySortDirection: sortDirections.ASCENDING,
selectedFilterKey: 'all',
// filters come from artistActions
filters,
filterPredicates,
customFilters: []
// filterPredicates come from artistActions
};
export const persistState = [

View file

@ -2,7 +2,8 @@ import _ from 'lodash';
import $ from 'jquery';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { filterTypes, sortDirections } from 'Helpers/Props';
import dateFilterPredicate from 'Utilities/Date/dateFilterPredicate';
import { filterTypePredicates, filterTypes, sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import createSetSettingValueReducer from './Creators/Reducers/createSetSettingValueReducer';
import createFetchHandler from './Creators/createFetchHandler';
@ -16,6 +17,95 @@ import { updateItem } from './baseActions';
export const section = 'artist';
export const 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 Tracks',
filters: [
{
key: 'missing',
value: true,
type: filterTypes.EQUAL
}
]
}
];
export const filterPredicates = {
missing: function(item) {
const { statistics = {} } = item;
return statistics.trackCount - statistics.trackFileCount > 0;
},
nextAlbum: function(item, filterValue, type) {
return dateFilterPredicate(item.nextAlbum, filterValue, type);
},
lastAlbum: function(item, filterValue, type) {
return dateFilterPredicate(item.lastAlbum, filterValue, type);
},
added: function(item, filterValue, type) {
return dateFilterPredicate(item.added, filterValue, type);
},
ratings: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
return predicate(item.ratings.value * 10, filterValue);
}
};
//
// State
@ -28,75 +118,6 @@ 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,12 +1,15 @@
import $ from 'jquery';
import { createAction } from 'redux-actions';
import { batchActions } from 'redux-batched-actions';
import { sortDirections } from 'Helpers/Props';
import customFilterHandlers from 'Utilities/customFilterHandlers';
import { filterBuilderTypes, filterBuilderValueTypes, 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 createHandleActions from './Creators/createHandleActions';
import { set, updateItem } from './baseActions';
import { filters, filterPredicates } from './artistActions';
//
// Variables
@ -26,9 +29,58 @@ export const defaultState = {
secondarySortKey: 'sortName',
secondarySortDirection: sortDirections.ASCENDING,
selectedFilterKey: 'all',
// filters come from artistActions
filters,
filterPredicates,
filterBuilderProps: [
{
name: 'monitored',
label: 'Monitored',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL
},
{
name: 'status',
label: 'Status',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.ARTIST_STATUS
},
{
name: 'qualityProfileId',
label: 'Quality Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.QUALITY_PROFILE
},
{
name: 'languageProfileId',
label: 'Language Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.LANGUAGE_PROFILE
},
{
name: 'metadataProfileId',
label: 'Metadata Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.METADATA_PROFILE
},
{
name: 'path',
label: 'Path',
type: filterBuilderTypes.STRING
},
{
name: 'rootFolderPath',
label: 'Root Folder Path',
type: filterBuilderTypes.EXACT
},
{
name: 'tags',
label: 'Tags',
type: filterBuilderTypes.ARRAY,
valueType: filterBuilderValueTypes.TAG
}
],
customFilters: []
// filterPredicates come from artistActions
};
export const persistState = [
@ -45,6 +97,8 @@ export const SET_ARTIST_EDITOR_SORT = 'artistEditor/setArtistEditorSort';
export const SET_ARTIST_EDITOR_FILTER = 'artistEditor/setArtistEditorFilter';
export const SAVE_ARTIST_EDITOR = 'artistEditor/saveArtistEditor';
export const BULK_DELETE_ARTIST = 'artistEditor/bulkDeleteArtist';
export const REMOVE_ARTIST_EDITOR_CUSTOM_FILTER = 'artistEditor/removeArtistEditorCustomFilter';
export const SAVE_ARTIST_EDITOR_CUSTOM_FILTER = 'artistEditor/saveArtistEditorCustomFilter';
//
// Action Creators
@ -53,6 +107,8 @@ export const setArtistEditorSort = createAction(SET_ARTIST_EDITOR_SORT);
export const setArtistEditorFilter = createAction(SET_ARTIST_EDITOR_FILTER);
export const saveArtistEditor = createThunk(SAVE_ARTIST_EDITOR);
export const bulkDeleteArtist = createThunk(BULK_DELETE_ARTIST);
export const removeArtistEditorCustomFilter = createAction(REMOVE_ARTIST_EDITOR_CUSTOM_FILTER);
export const saveArtistEditorCustomFilter = createAction(SAVE_ARTIST_EDITOR_CUSTOM_FILTER);
//
// Action Handlers
@ -137,6 +193,11 @@ export const actionHandlers = handleThunks({
export const reducers = createHandleActions({
[SET_ARTIST_EDITOR_SORT]: createSetClientSideCollectionSortReducer(section),
[SET_ARTIST_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(section)
[SET_ARTIST_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(section),
...createCustomFilterReducers(section, {
[customFilterHandlers.REMOVE]: REMOVE_ARTIST_EDITOR_CUSTOM_FILTER,
[customFilterHandlers.SAVE]: SAVE_ARTIST_EDITOR_CUSTOM_FILTER
})
}, defaultState, section);

View file

@ -1,10 +1,14 @@
import moment from 'moment';
import { createAction } from 'redux-actions';
import { sortDirections } from 'Helpers/Props';
import customFilterHandlers from 'Utilities/customFilterHandlers';
import sortByName from 'Utilities/Array/sortByName';
import { filterBuilderTypes, filterBuilderValueTypes, sortDirections } from 'Helpers/Props';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetClientSideCollectionFilterReducer from './Creators/Reducers/createSetClientSideCollectionFilterReducer';
import createCustomFilterReducers from './Creators/Reducers/createCustomFilterReducers';
import createHandleActions from './Creators/createHandleActions';
import { filters, filterPredicates } from './artistActions';
//
// Variables
@ -137,6 +141,18 @@ export const defaultState = {
isSortable: true,
isVisible: false
},
{
name: 'genres',
label: 'Genres',
isSortable: false,
isVisible: false
},
{
name: 'ratings',
label: 'Rating',
isSortable: true,
isVisible: false
},
{
name: 'tags',
label: 'Tags',
@ -153,10 +169,12 @@ export const defaultState = {
sortPredicates: {
trackProgress: function(item) {
const { statistics = {} } = item;
const {
trackCount = 0,
trackFileCount
} = item.statistics;
} = statistics;
const progress = trackCount ? trackFileCount / trackCount * 100 : 100;
@ -178,22 +196,136 @@ export const defaultState = {
},
albumCount: function(item) {
return item.statistics.albumCount;
const { statistics = {} } = item;
return statistics.albumCount;
},
trackCount: function(item) {
return item.statistics.totalTrackCount;
const { statistics = {} } = item;
return statistics.totalTrackCount;
},
sizeOnDisk: function(item) {
return item.statistics.sizeOnDisk;
const { statistics = {} } = item;
return statistics.sizeOnDisk;
},
ratings: function(item) {
const { ratings = {} } = item;
return ratings.value;
}
},
selectedFilterKey: 'all',
// filters come from artistActions
filters,
filterPredicates,
filterBuilderProps: [
{
name: 'monitored',
label: 'Monitored',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL
},
{
name: 'status',
label: 'Status',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.ARTIST_STATUS
},
{
name: 'qualityProfileId',
label: 'Quality Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.QUALITY_PROFILE
},
{
name: 'languageProfileId',
label: 'Language Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.LANGUAGE_PROFILE
},
{
name: 'metadataProfileId',
label: 'Metadata Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.METADATA_PROFILE
},
{
name: 'nextAlbum',
label: 'Next Album',
type: filterBuilderTypes.DATE,
valueType: filterBuilderValueTypes.DATE
},
{
name: 'lastAlbum',
label: 'Last Album',
type: filterBuilderTypes.DATE,
valueType: filterBuilderValueTypes.DATE
},
{
name: 'added',
label: 'Added',
type: filterBuilderTypes.DATE,
valueType: filterBuilderValueTypes.DATE
},
{
name: 'albumCount',
label: 'Album Count',
type: filterBuilderTypes.NUMBER
},
{
name: 'trackProgress',
label: 'Track Progress',
type: filterBuilderTypes.NUMBER
},
{
name: 'path',
label: 'Path',
type: filterBuilderTypes.STRING
},
{
name: 'sizeOnDisk',
label: 'Size on Disk',
type: filterBuilderTypes.NUMBER
},
{
name: 'genres',
label: 'Genres',
type: filterBuilderTypes.ARRAY,
optionsSelector: function(items) {
const tagList = items.reduce((acc, artist) => {
artist.genres.forEach((genre) => {
acc.push({
id: genre,
name: genre
});
});
return acc;
}, []);
return tagList.sort(sortByName);
}
},
{
name: 'ratings',
label: 'Rating',
type: filterBuilderTypes.NUMBER
},
{
name: 'tags',
label: 'Tags',
type: filterBuilderTypes.ARRAY,
valueType: filterBuilderValueTypes.TAG
}
],
customFilters: []
// filterPredicates come from artistActions
};
export const persistState = [
@ -218,6 +350,8 @@ export const SET_ARTIST_TABLE_OPTION = 'artistIndex/setArtistTableOption';
export const SET_ARTIST_POSTER_OPTION = 'artistIndex/setArtistPosterOption';
export const SET_ARTIST_BANNER_OPTION = 'artistIndex/setArtistBannerOption';
export const SET_ARTIST_OVERVIEW_OPTION = 'artistIndex/setArtistOverviewOption';
export const REMOVE_ARTIST_CUSTOM_FILTER = 'artistIndex/removeArtistCustomFilter';
export const SAVE_ARTIST_CUSTOM_FILTER = 'artistIndex/saveArtistCustomFilter';
//
// Action Creators
@ -229,7 +363,8 @@ export const setArtistTableOption = createAction(SET_ARTIST_TABLE_OPTION);
export const setArtistPosterOption = createAction(SET_ARTIST_POSTER_OPTION);
export const setArtistBannerOption = createAction(SET_ARTIST_BANNER_OPTION);
export const setArtistOverviewOption = createAction(SET_ARTIST_OVERVIEW_OPTION);
export const removeArtistCustomFilter = createAction(REMOVE_ARTIST_CUSTOM_FILTER);
export const saveArtistCustomFilter = createAction(SAVE_ARTIST_CUSTOM_FILTER);
//
// Reducers
@ -278,6 +413,11 @@ export const reducers = createHandleActions({
...payload
}
};
}
},
...createCustomFilterReducers(section, {
[customFilterHandlers.REMOVE]: REMOVE_ARTIST_CUSTOM_FILTER,
[customFilterHandlers.SAVE]: SAVE_ARTIST_CUSTOM_FILTER
})
}, defaultState, section);

View file

@ -4,6 +4,7 @@ import { createThunk, handleThunks } from 'Store/thunks';
import { sortDirections } from 'Helpers/Props';
import createSetTableOptionReducer from './Creators/Reducers/createSetTableOptionReducer';
import createHandleActions from './Creators/createHandleActions';
import createRemoveItemHandler from './Creators/createRemoveItemHandler';
import createServerSideCollectionHandlers from './Creators/createServerSideCollectionHandlers';
//
@ -59,8 +60,8 @@ export const defaultState = {
isVisible: false
},
{
name: 'details',
columnLabel: 'Details',
name: 'actions',
columnLabel: 'Actions',
isVisible: true,
isModifiable: false
}
@ -85,6 +86,7 @@ export const GOTO_LAST_BLACKLIST_PAGE = 'blacklist/gotoBlacklistLastPage';
export const GOTO_BLACKLIST_PAGE = 'blacklist/gotoBlacklistPage';
export const SET_BLACKLIST_SORT = 'blacklist/setBlacklistSort';
export const SET_BLACKLIST_TABLE_OPTION = 'blacklist/setBlacklistTableOption';
export const REMOVE_FROM_BLACKLIST = 'blacklist/removeFromBlacklist';
//
// Action Creators
@ -97,6 +99,7 @@ export const gotoBlacklistLastPage = createThunk(GOTO_LAST_BLACKLIST_PAGE);
export const gotoBlacklistPage = createThunk(GOTO_BLACKLIST_PAGE);
export const setBlacklistSort = createThunk(SET_BLACKLIST_SORT);
export const setBlacklistTableOption = createAction(SET_BLACKLIST_TABLE_OPTION);
export const removeFromBlacklist = createThunk(REMOVE_FROM_BLACKLIST);
//
// Action Handlers
@ -114,7 +117,9 @@ export const actionHandlers = handleThunks({
[serverSideCollectionHandlers.LAST_PAGE]: GOTO_LAST_BLACKLIST_PAGE,
[serverSideCollectionHandlers.EXACT_PAGE]: GOTO_BLACKLIST_PAGE,
[serverSideCollectionHandlers.SORT]: SET_BLACKLIST_SORT
})
}),
[REMOVE_FROM_BLACKLIST]: createRemoveItemHandler(section, '/blacklist')
});
//

View file

@ -287,7 +287,7 @@ export const actionHandlers = handleThunks({
[SET_CALENDAR_VIEW]: function(getState, payload, dispatch) {
const state = getState();
const view = payload.view;
const time = view === calendarViews.FORECAST ?
const time = view === calendarViews.FORECAST || calendarViews.AGENDA ?
moment() :
state.calendar.time;

View file

@ -1,4 +1,3 @@
import _ from 'lodash';
import $ from 'jquery';
import moment from 'moment';
import { createAction } from 'redux-actions';
@ -44,7 +43,7 @@ export const defaultState = {
},
quality: function(item, direction) {
return item.quality.qualityWeight;
return item.quality ? item.quality.qualityWeight : 0;
}
},
@ -144,7 +143,7 @@ export const reducers = createHandleActions({
const id = payload.id;
const newState = Object.assign({}, state);
const items = newState.items;
const index = _.findIndex(items, { id });
const index = items.findIndex((item) => item.id === id);
const item = Object.assign({}, items[index], payload);
newState.items = [...items];
@ -157,7 +156,7 @@ export const reducers = createHandleActions({
const folder = payload.folder;
const recentFolder = { folder, lastUsed: moment().toISOString() };
const recentFolders = [...state.recentFolders];
const index = _.findIndex(recentFolders, { folder });
const index = recentFolders.findIndex((r) => r.folder === folder);
if (index > -1) {
recentFolders.splice(index, 1, recentFolder);
@ -170,7 +169,10 @@ export const reducers = createHandleActions({
[REMOVE_RECENT_FOLDER]: function(state, { payload }) {
const folder = payload.folder;
const recentFolders = _.remove([...state.recentFolders], { folder });
const recentFolders = [...state.recentFolders];
const index = recentFolders.findIndex((r) => r.folder === folder);
recentFolders.splice(index, 1);
return Object.assign({}, state, { recentFolders });
},

View file

@ -16,8 +16,8 @@ export const section = 'oAuth';
export const defaultState = {
authorizing: false,
accessToken: null,
accessTokenSecret: null
result: null,
error: null
};
//
@ -50,9 +50,11 @@ function showOAuthWindow(url) {
const splitQuery = query.substring(1).split('&');
splitQuery.forEach((param) => {
const paramSplit = param.split('=');
if (param) {
const paramSplit = param.split('=');
queryParams[paramSplit[0]] = paramSplit[1];
queryParams[paramSplit[0]] = paramSplit[1];
}
});
onComplete();
@ -70,7 +72,7 @@ export const actionHandlers = handleThunks({
[START_OAUTH]: function(getState, payload, dispatch) {
const actionPayload = {
action: 'startOAuth',
queryParams: { callbackUrl: `${window.location.origin}/oauth.html` },
queryParams: { callbackUrl: `${window.location.origin}${window.Lidarr.urlBase}/oauth.html` },
...payload
};
@ -78,33 +80,36 @@ export const actionHandlers = handleThunks({
authorizing: true
}));
let startResponse = {};
const promise = requestAction(actionPayload)
.then((response) => {
startResponse = response;
return showOAuthWindow(response.oauthUrl);
})
.then((queryParams) => {
return requestAction({
action: 'getOAuthToken',
queryParams,
queryParams: {
...startResponse,
...queryParams
},
...payload
});
})
.then((response) => {
const {
accessToken,
accessTokenSecret
} = response;
dispatch(setOAuthValue({
authorizing: false,
accessToken,
accessTokenSecret
result: response,
error: null
}));
});
promise.fail(() => {
promise.fail((xhr) => {
dispatch(setOAuthValue({
authorizing: false
authorizing: false,
result: null,
error: xhr
}));
});
}

View file

@ -78,16 +78,19 @@ export const defaultState = {
{
name: 'protocol',
label: 'Protocol',
isSortable: true,
isVisible: false
},
{
name: 'indexer',
label: 'Indexer',
isSortable: true,
isVisible: false
},
{
name: 'downloadClient',
label: 'Download Client',
isSortable: true,
isVisible: false
},
{
@ -269,7 +272,7 @@ export const actionHandlers = handleThunks({
promise.done((data) => {
dispatch(batchActions([
dispatch(fetchQueue()),
fetchQueue(),
...ids.map((id) => {
return updateItem({

View file

@ -1,6 +1,7 @@
import $ from 'jquery';
import { createThunk, handleThunks } from 'Store/thunks';
import createFetchHandler from './Creators/createFetchHandler';
import createRemoveItemHandler from './Creators/createRemoveItemHandler';
import createHandleActions from './Creators/createHandleActions';
import { update } from './baseActions';
@ -16,7 +17,14 @@ export const defaultState = {
isFetching: false,
isPopulated: false,
error: null,
items: []
items: [],
details: {
isFetching: false,
isPopulated: false,
error: null,
items: []
}
};
//
@ -24,12 +32,16 @@ export const defaultState = {
export const FETCH_TAGS = 'tags/fetchTags';
export const ADD_TAG = 'tags/addTag';
export const DELETE_TAG = 'tags/deleteTag';
export const FETCH_TAG_DETAILS = 'tags/fetchTagDetails';
//
// Action Creators
export const fetchTags = createThunk(FETCH_TAGS);
export const addTag = createThunk(ADD_TAG);
export const deleteTag = createThunk(DELETE_TAG);
export const fetchTagDetails = createThunk(FETCH_TAG_DETAILS);
//
// Action Handlers
@ -51,7 +63,11 @@ export const actionHandlers = handleThunks({
dispatch(update({ section, data: tags }));
payload.onTagCreated(data);
});
}
},
[DELETE_TAG]: createRemoveItemHandler(section, '/tag'),
[FETCH_TAG_DETAILS]: createFetchHandler('tags.details', '/tag/detail')
});
//

View file

@ -15,7 +15,10 @@ export default function(history) {
middlewares.push(routerMiddleware(history));
middlewares.push(thunk);
return compose(
// eslint-disable-next-line no-underscore-dangle
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
return composeEnhancers(
applyMiddleware(...middlewares),
persistState
);

View file

@ -32,17 +32,20 @@ export default function sentryMiddleware() {
}
const dsn = isProduction ? 'https://c3a5b33e08de4e18b7d0505e942dbc95@sentry.io/216290' :
'https://c3a5b33e08de4e18b7d0505e942dbc95@sentry.io/216290';
'https://baede6f14da54cf48ff431479e400adf@sentry.io/1249427';
Raven.config(dsn).install();
Raven.config(
dsn,
{
environment: isProduction ? 'production' : 'development',
release,
tags: {
branch,
version
},
dataCallback: cleanseData
}
).install();
return createRavenMiddleware(Raven, {
environment: isProduction ? 'production' : 'development',
release,
tags: {
branch,
version
},
dataCallback: cleanseData
});
return createRavenMiddleware(Raven);
}

View file

@ -1,37 +1,7 @@
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().contains(filterValue.toLowerCase());
},
[filterTypes.EQUAL]: function(value, filterValue) {
return value === filterValue;
},
[filterTypes.GREATER_THAN]: function(value, filterValue) {
return value > filterValue;
},
[filterTypes.GREATER_THAN_OR_EQUAL]: function(value, filterValue) {
return value >= filterValue;
},
[filterTypes.LESS_THAN]: function(value, filterValue) {
return value < filterValue;
},
[filterTypes.LESS_THAN_OR_EQUAL]: function(value, filterValue) {
return value <= filterValue;
},
[filterTypes.NOT_EQUAL]: function(value, filterValue) {
return value !== filterValue;
}
};
import { filterTypePredicates, filterTypes, sortDirections } from 'Helpers/Props';
function getSortClause(sortKey, sortDirection, sortPredicates) {
if (sortPredicates && sortPredicates.hasOwnProperty(sortKey)) {
@ -124,10 +94,10 @@ function sort(items, state) {
return _.orderBy(items, clauses, orders);
}
function createClientSideCollectionSelector() {
function createClientSideCollectionSelector(section, uiSection) {
return createSelector(
(state, { section }) => _.get(state, section),
(state, { uiSection }) => _.get(state, uiSection),
(state) => _.get(state, section),
(state) => _.get(state, uiSection),
(sectionState, uiSectionState = {}) => {
const state = Object.assign({}, sectionState, uiSectionState);
@ -137,7 +107,8 @@ function createClientSideCollectionSelector() {
return {
...sectionState,
...uiSectionState,
items: sorted
items: sorted,
totalItems: state.items.length
};
}
);

View file

@ -2,10 +2,10 @@ import _ from 'lodash';
import { createSelector } from 'reselect';
import selectSettings from 'Store/Selectors/selectSettings';
function createProviderSettingsSelector() {
function createProviderSettingsSelector(sectionName) {
return createSelector(
(state, { id }) => id,
(state, { section }) => state.settings[section],
(state) => state.settings[sectionName],
(id, section) => {
if (!id) {
const item = _.isArray(section.schema) ? section.selectedSchema : section.schema;

View file

@ -1,10 +1,9 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import selectSettings from 'Store/Selectors/selectSettings';
function createSettingsSectionSelector() {
function createSettingsSectionSelector(section) {
return createSelector(
(state, { section }) => _.get(state, section),
(state) => state.settings[section],
(sectionSettings) => {
const {
isFetching,

View file

@ -0,0 +1,13 @@
import { createSelector } from 'reselect';
function createTagDetailsSelector() {
return createSelector(
(state, { id }) => id,
(state) => state.tags.details.items,
(id, tagDetails) => {
return tagDetails.find((t) => t.id === id);
}
);
}
export default createTagDetailsSelector;

View file

@ -1,58 +0,0 @@
/* eslint max-params: 0 */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import getDisplayName from 'Helpers/getDisplayName';
function connectSection(mapStateToProps, mapDispatchToProps, mergeProps, options = {}, sectionOptions = {}) {
return function wrap(WrappedComponent) {
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(WrappedComponent);
class Section extends Component {
//
// Control
getWrappedInstance = () => {
if (this._wrappedInstance) {
return this._wrappedInstance.getWrappedInstance();
}
}
//
// Listeners
setWrappedInstanceRef = (ref) => {
this._wrappedInstance = ref;
}
//
// Render
render() {
if (options.withRef) {
return (
<ConnectedComponent
ref={this.setWrappedInstanceRef}
{...sectionOptions}
{...this.props}
/>
);
}
return (
<ConnectedComponent
{...sectionOptions}
{...this.props}
/>
);
}
}
Section.displayName = `Section(${getDisplayName(WrappedComponent)})`;
Section.WrappedComponent = WrappedComponent;
return Section;
};
}
export default connectSection;