mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-27 04:21:27 +02:00
New: Project Aphrodite
This commit is contained in:
12
frontend/src/Store/Selectors/createAllMoviesSelector.js
Normal file
12
frontend/src/Store/Selectors/createAllMoviesSelector.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createAllMoviesSelector() {
|
||||
return createSelector(
|
||||
(state) => state.movies,
|
||||
(movies) => {
|
||||
return movies.items;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createAllMoviesSelector;
|
@@ -0,0 +1,137 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import findSelectedFilters from 'Utilities/Filter/findSelectedFilters';
|
||||
import { filterTypePredicates, filterTypes, 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 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)) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createClientSideCollectionSelector(section, uiSection) {
|
||||
return createSelector(
|
||||
(state) => _.get(state, section),
|
||||
(state) => _.get(state, uiSection),
|
||||
createCustomFiltersSelector(section, uiSection),
|
||||
(sectionState, uiSectionState = {}, customFilters) => {
|
||||
const state = Object.assign({}, sectionState, uiSectionState, { customFilters });
|
||||
|
||||
const filtered = filter(state.items, state);
|
||||
const sorted = sort(filtered, state);
|
||||
|
||||
return {
|
||||
...sectionState,
|
||||
...uiSectionState,
|
||||
customFilters,
|
||||
items: sorted,
|
||||
totalItems: state.items.length
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createClientSideCollectionSelector;
|
@@ -0,0 +1,14 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { isCommandExecuting } from 'Utilities/Command';
|
||||
import createCommandSelector from './createCommandSelector';
|
||||
|
||||
function createCommandExecutingSelector(name, contraints = {}) {
|
||||
return createSelector(
|
||||
createCommandSelector(name, contraints),
|
||||
(command) => {
|
||||
return isCommandExecuting(command);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createCommandExecutingSelector;
|
14
frontend/src/Store/Selectors/createCommandSelector.js
Normal file
14
frontend/src/Store/Selectors/createCommandSelector.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { findCommand } from 'Utilities/Command';
|
||||
import createCommandsSelector from './createCommandsSelector';
|
||||
|
||||
function createCommandSelector(name, contraints = {}) {
|
||||
return createSelector(
|
||||
createCommandsSelector(),
|
||||
(commands) => {
|
||||
return findCommand(commands, { name, ...contraints });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createCommandSelector;
|
12
frontend/src/Store/Selectors/createCommandsSelector.js
Normal file
12
frontend/src/Store/Selectors/createCommandsSelector.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createCommandsSelector() {
|
||||
return createSelector(
|
||||
(state) => state.commands,
|
||||
(commands) => {
|
||||
return commands.items;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createCommandsSelector;
|
12
frontend/src/Store/Selectors/createDimensionsSelector.js
Normal file
12
frontend/src/Store/Selectors/createDimensionsSelector.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createDimensionsSelector() {
|
||||
return createSelector(
|
||||
(state) => state.app.dimensions,
|
||||
(dimensions) => {
|
||||
return dimensions;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createDimensionsSelector;
|
15
frontend/src/Store/Selectors/createExistingMovieSelector.js
Normal file
15
frontend/src/Store/Selectors/createExistingMovieSelector.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
||||
|
||||
function createExistingMovieSelector() {
|
||||
return createSelector(
|
||||
(state, { tmdbId }) => tmdbId,
|
||||
createAllMoviesSelector(),
|
||||
(tmdbId, movies) => {
|
||||
return _.some(movies, { tmdbId });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createExistingMovieSelector;
|
@@ -0,0 +1,26 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
||||
|
||||
function createImportMovieItemSelector() {
|
||||
return createSelector(
|
||||
(state, { id }) => id,
|
||||
(state) => state.addMovie,
|
||||
(state) => state.importMovie,
|
||||
createAllMoviesSelector(),
|
||||
(id, addMovie, importMovie, series) => {
|
||||
const item = _.find(importMovie.items, { id }) || {};
|
||||
const selectedMovie = item && item.selectedMovie;
|
||||
const isExistingMovie = !!selectedMovie && _.some(series, { tvdbId: selectedMovie.tvdbId });
|
||||
|
||||
return {
|
||||
defaultMonitor: addMovie.defaults.monitor,
|
||||
defaultQualityProfileId: addMovie.defaults.qualityProfileId,
|
||||
...item,
|
||||
isExistingMovie
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createImportMovieItemSelector;
|
13
frontend/src/Store/Selectors/createMovieCountSelector.js
Normal file
13
frontend/src/Store/Selectors/createMovieCountSelector.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
||||
|
||||
function createMovieCountSelector() {
|
||||
return createSelector(
|
||||
createAllMoviesSelector(),
|
||||
(movies) => {
|
||||
return movies.length;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createMovieCountSelector;
|
17
frontend/src/Store/Selectors/createMovieFileSelector.js
Normal file
17
frontend/src/Store/Selectors/createMovieFileSelector.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createMovieFileSelector() {
|
||||
return createSelector(
|
||||
(state, { movieFileId }) => movieFileId,
|
||||
(state) => state.movieFiles,
|
||||
(movieFileId, movieFiles) => {
|
||||
if (!movieFileId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return movieFiles.items.find((movieFile) => movieFile.id === movieFileId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createMovieFileSelector;
|
15
frontend/src/Store/Selectors/createMovieSelector.js
Normal file
15
frontend/src/Store/Selectors/createMovieSelector.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
||||
|
||||
function createMovieSelector() {
|
||||
return createSelector(
|
||||
(state, { movieId }) => movieId,
|
||||
createAllMoviesSelector(),
|
||||
(movieId, movies) => {
|
||||
return _.find(movies, { id: movieId });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createMovieSelector;
|
19
frontend/src/Store/Selectors/createProfileInUseSelector.js
Normal file
19
frontend/src/Store/Selectors/createProfileInUseSelector.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAllMoviesSelector from './createAllMoviesSelector';
|
||||
|
||||
function createProfileInUseSelector(profileProp) {
|
||||
return createSelector(
|
||||
(state, { id }) => id,
|
||||
createAllMoviesSelector(),
|
||||
(id, series) => {
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _.some(series, { [profileProp]: id });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createProfileInUseSelector;
|
@@ -0,0 +1,63 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
|
||||
function createProviderSettingsSelector(sectionName) {
|
||||
return createSelector(
|
||||
(state, { id }) => id,
|
||||
(state) => state.settings[sectionName],
|
||||
(id, section) => {
|
||||
if (!id) {
|
||||
const item = _.isArray(section.schema) ? section.selectedSchema : section.schema;
|
||||
const settings = selectSettings(Object.assign({ name: '' }, item), section.pendingChanges, section.saveError);
|
||||
|
||||
const {
|
||||
isSchemaFetching: isFetching,
|
||||
isSchemaPopulated: isPopulated,
|
||||
schemaError: error,
|
||||
isSaving,
|
||||
saveError,
|
||||
isTesting,
|
||||
pendingChanges
|
||||
} = section;
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
isSaving,
|
||||
saveError,
|
||||
isTesting,
|
||||
pendingChanges,
|
||||
...settings,
|
||||
item: settings.settings
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
isSaving,
|
||||
saveError,
|
||||
isTesting,
|
||||
pendingChanges
|
||||
} = section;
|
||||
|
||||
const settings = selectSettings(_.find(section.items, { id }), pendingChanges, saveError);
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
isSaving,
|
||||
saveError,
|
||||
isTesting,
|
||||
...settings,
|
||||
item: settings.settings
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createProviderSettingsSelector;
|
14
frontend/src/Store/Selectors/createQualityProfileSelector.js
Normal file
14
frontend/src/Store/Selectors/createQualityProfileSelector.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createQualityProfileSelector() {
|
||||
return createSelector(
|
||||
(state, { qualityProfileId }) => qualityProfileId,
|
||||
(state) => state.settings.qualityProfiles.items,
|
||||
(qualityProfileId, qualityProfiles) => {
|
||||
return _.find(qualityProfiles, { id: qualityProfileId });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createQualityProfileSelector;
|
23
frontend/src/Store/Selectors/createQueueItemSelector.js
Normal file
23
frontend/src/Store/Selectors/createQueueItemSelector.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createQueueItemSelector() {
|
||||
return createSelector(
|
||||
(state, { episodeId }) => episodeId,
|
||||
(state) => state.queue.details.items,
|
||||
(episodeId, details) => {
|
||||
if (!episodeId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return details.find((item) => {
|
||||
if (item.episode) {
|
||||
return item.episode.id === episodeId;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createQueueItemSelector;
|
@@ -0,0 +1,32 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
|
||||
function createSettingsSectionSelector(section) {
|
||||
return createSelector(
|
||||
(state) => state.settings[section],
|
||||
(sectionSettings) => {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
item,
|
||||
pendingChanges,
|
||||
isSaving,
|
||||
saveError
|
||||
} = sectionSettings;
|
||||
|
||||
const settings = selectSettings(item, pendingChanges, saveError);
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
error,
|
||||
isSaving,
|
||||
saveError,
|
||||
...settings
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createSettingsSectionSelector;
|
12
frontend/src/Store/Selectors/createSystemStatusSelector.js
Normal file
12
frontend/src/Store/Selectors/createSystemStatusSelector.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createSystemStatusSelector() {
|
||||
return createSelector(
|
||||
(state) => state.system.status,
|
||||
(status) => {
|
||||
return status.item;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createSystemStatusSelector;
|
13
frontend/src/Store/Selectors/createTagDetailsSelector.js
Normal file
13
frontend/src/Store/Selectors/createTagDetailsSelector.js
Normal 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;
|
12
frontend/src/Store/Selectors/createTagsSelector.js
Normal file
12
frontend/src/Store/Selectors/createTagsSelector.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createTagsSelector() {
|
||||
return createSelector(
|
||||
(state) => state.tags.items,
|
||||
(tags) => {
|
||||
return tags;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createTagsSelector;
|
12
frontend/src/Store/Selectors/createUISettingsSelector.js
Normal file
12
frontend/src/Store/Selectors/createUISettingsSelector.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
function createUISettingsSelector() {
|
||||
return createSelector(
|
||||
(state) => state.settings.ui,
|
||||
(ui) => {
|
||||
return ui.item;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default createUISettingsSelector;
|
104
frontend/src/Store/Selectors/selectSettings.js
Normal file
104
frontend/src/Store/Selectors/selectSettings.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
function getValidationFailures(saveError) {
|
||||
if (!saveError || saveError.status !== 400) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _.cloneDeep(saveError.responseJSON);
|
||||
}
|
||||
|
||||
function mapFailure(failure) {
|
||||
return {
|
||||
message: failure.errorMessage,
|
||||
link: failure.infoLink,
|
||||
detailedMessage: failure.detailedDescription
|
||||
};
|
||||
}
|
||||
|
||||
function selectSettings(item, pendingChanges, saveError) {
|
||||
const validationFailures = getValidationFailures(saveError);
|
||||
|
||||
// Merge all settings from the item along with pending
|
||||
// changes to ensure any settings that were not included
|
||||
// with the item are included.
|
||||
const allSettings = Object.assign({}, item, pendingChanges);
|
||||
|
||||
const settings = _.reduce(allSettings, (result, value, key) => {
|
||||
if (key === 'fields') {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return a flattened value
|
||||
if (key === 'implementationName') {
|
||||
result.implementationName = item[key];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const setting = {
|
||||
value: item[key],
|
||||
errors: _.map(_.remove(validationFailures, (failure) => {
|
||||
return failure.propertyName.toLowerCase() === key.toLowerCase() && !failure.isWarning;
|
||||
}), mapFailure),
|
||||
|
||||
warnings: _.map(_.remove(validationFailures, (failure) => {
|
||||
return failure.propertyName.toLowerCase() === key.toLowerCase() && failure.isWarning;
|
||||
}), mapFailure)
|
||||
};
|
||||
|
||||
if (pendingChanges.hasOwnProperty(key)) {
|
||||
setting.previousValue = setting.value;
|
||||
setting.value = pendingChanges[key];
|
||||
setting.pending = true;
|
||||
}
|
||||
|
||||
result[key] = setting;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
const fields = _.reduce(item.fields, (result, f) => {
|
||||
const field = Object.assign({ pending: false }, f);
|
||||
const hasPendingFieldChange = pendingChanges.fields && pendingChanges.fields.hasOwnProperty(field.name);
|
||||
|
||||
if (hasPendingFieldChange) {
|
||||
field.previousValue = field.value;
|
||||
field.value = pendingChanges.fields[field.name];
|
||||
field.pending = true;
|
||||
}
|
||||
|
||||
field.errors = _.map(_.remove(validationFailures, (failure) => {
|
||||
return failure.propertyName.toLowerCase() === field.name.toLowerCase() && !failure.isWarning;
|
||||
}), mapFailure);
|
||||
|
||||
field.warnings = _.map(_.remove(validationFailures, (failure) => {
|
||||
return failure.propertyName.toLowerCase() === field.name.toLowerCase() && failure.isWarning;
|
||||
}), mapFailure);
|
||||
|
||||
result.push(field);
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
if (fields.length) {
|
||||
settings.fields = fields;
|
||||
}
|
||||
|
||||
const validationErrors = _.filter(validationFailures, (failure) => {
|
||||
return !failure.isWarning;
|
||||
});
|
||||
|
||||
const validationWarnings = _.filter(validationFailures, (failure) => {
|
||||
return failure.isWarning;
|
||||
});
|
||||
|
||||
return {
|
||||
settings,
|
||||
validationErrors,
|
||||
validationWarnings,
|
||||
hasPendingChanges: !_.isEmpty(pendingChanges),
|
||||
hasSettings: !_.isEmpty(settings),
|
||||
pendingChanges
|
||||
};
|
||||
}
|
||||
|
||||
export default selectSettings;
|
Reference in New Issue
Block a user