diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index 9fa3e09ae..da206ac4a 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -7,7 +7,6 @@ import AppSectionState, { PagedAppSectionState, } from 'App/State/AppSectionState'; import Language from 'Language/Language'; -import { QualityProfileModel } from 'Settings/Profiles/Quality/useQualityProfiles'; import AutoTagging, { AutoTaggingSpecification } from 'typings/AutoTagging'; import CustomFormat from 'typings/CustomFormat'; import CustomFormatSpecification from 'typings/CustomFormatSpecification'; @@ -19,7 +18,6 @@ import ImportListOptionsSettings from 'typings/ImportListOptionsSettings'; import Indexer from 'typings/Indexer'; import IndexerFlag from 'typings/IndexerFlag'; import Notification from 'typings/Notification'; -import QualityDefinition from 'typings/QualityDefinition'; import DownloadClientOptions from 'typings/Settings/DownloadClientOptions'; import General from 'typings/Settings/General'; import IndexerOptions from 'typings/Settings/IndexerOptions'; @@ -100,14 +98,6 @@ export interface NotificationAppState AppSectionSaveState, AppSectionSchemaState> {} -export interface QualityDefinitionsAppState - extends AppSectionState, - AppSectionSaveState { - pendingChanges: { - [key: number]: Partial; - }; -} - export interface CustomFormatAppState extends AppSectionState, AppSectionDeleteState, @@ -155,7 +145,6 @@ interface SettingsAppState { naming: NamingAppState; namingExamples: NamingExamplesAppState; notifications: NotificationAppState; - qualityDefinitions: QualityDefinitionsAppState; } export default SettingsAppState; diff --git a/frontend/src/Components/SignalRListener.tsx b/frontend/src/Components/SignalRListener.tsx index ac70fbc47..ce6ca51bf 100644 --- a/frontend/src/Components/SignalRListener.tsx +++ b/frontend/src/Components/SignalRListener.tsx @@ -15,7 +15,6 @@ import { EpisodeFile } from 'EpisodeFile/EpisodeFile'; import { PagedQueryResponse } from 'Helpers/Hooks/usePagedApiQuery'; import Series from 'Series/Series'; import { removeItem, updateItem } from 'Store/Actions/baseActions'; -import { fetchQualityDefinitions } from 'Store/Actions/settingsActions'; import { repopulatePage } from 'Utilities/pagePopulator'; import SignalRLogger from 'Utilities/SignalRLogger'; @@ -291,7 +290,11 @@ function SignalRListener() { } if (name === 'qualitydefinition') { - dispatch(fetchQualityDefinitions()); + if (version < 5) { + return; + } + + queryClient.invalidateQueries({ queryKey: ['/qualitydefinition'] }); return; } diff --git a/frontend/src/Helpers/Hooks/usePendingChangesStore.ts b/frontend/src/Helpers/Hooks/usePendingChangesStore.ts index 9db4155e6..0ffeb1f08 100644 --- a/frontend/src/Helpers/Hooks/usePendingChangesStore.ts +++ b/frontend/src/Helpers/Hooks/usePendingChangesStore.ts @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { create, useStore } from 'zustand'; import { useShallow } from 'zustand/react/shallow'; @@ -23,34 +23,40 @@ export const usePendingChangesStore = ( }); }); - const setPendingChange = (key: K, value: T[K]) => { - store.setState((state) => ({ - ...state, - pendingChanges: { - ...state.pendingChanges, - [key]: value, - }, - })); - }; - - const unsetPendingChange = (key: K) => { - store.setState((state) => { - const newPendingChanges = { ...state.pendingChanges }; - delete newPendingChanges[key]; - - return { + const setPendingChange = useCallback( + (key: K, value: T[K]) => { + store.setState((state) => ({ ...state, - pendingChanges: newPendingChanges, - }; - }); - }; + pendingChanges: { + ...state.pendingChanges, + [key]: value, + }, + })); + }, + [store] + ); - const clearPendingChanges = () => { + const unsetPendingChange = useCallback( + (key: K) => { + store.setState((state) => { + const newPendingChanges = { ...state.pendingChanges }; + delete newPendingChanges[key]; + + return { + ...state, + pendingChanges: newPendingChanges, + }; + }); + }, + [store] + ); + + const clearPendingChanges = useCallback(() => { store.setState((state) => ({ ...state, pendingChanges: {}, })); - }; + }, [store]); const pendingChanges = useStore( store, diff --git a/frontend/src/Helpers/Hooks/usePendingItemsStore.ts b/frontend/src/Helpers/Hooks/usePendingItemsStore.ts new file mode 100644 index 000000000..50b7ede91 --- /dev/null +++ b/frontend/src/Helpers/Hooks/usePendingItemsStore.ts @@ -0,0 +1,121 @@ +import { useCallback, useMemo, useState } from 'react'; +import { create, useStore } from 'zustand'; +import { useShallow } from 'zustand/react/shallow'; + +interface PendingItemsStore { + pendingItems: Map>; +} + +export const usePendingItemsStore = () => { + // eslint-disable-next-line react/hook-use-state + const [store] = useState(() => { + return create>()((_set) => { + return { + pendingItems: new Map(), + }; + }); + }); + + const setPendingItem = useCallback( + (id: number, key: K, value: T[K], originalItem?: T) => { + store.setState((state) => { + const newPendingItems = new Map(state.pendingItems); + const existingChanges = newPendingItems.get(id) || {}; + + // If the value matches the original, remove it from pending changes + if (originalItem && originalItem[key] === value) { + const { [key]: removed, ...rest } = existingChanges; + + if (Object.keys(rest).length === 0) { + newPendingItems.delete(id); + } else { + newPendingItems.set(id, rest); + } + } else { + newPendingItems.set(id, { + ...existingChanges, + [key]: value, + }); + } + + return { + ...state, + pendingItems: newPendingItems, + }; + }); + }, + [store] + ); + + const unsetPendingItem = useCallback( + (id: number) => { + store.setState((state) => { + const newPendingItems = new Map(state.pendingItems); + newPendingItems.delete(id); + + return { + ...state, + pendingItems: newPendingItems, + }; + }); + }, + [store] + ); + + const clearPendingItems = useCallback(() => { + store.setState((state) => ({ + ...state, + pendingItems: new Map(), + })); + }, [store]); + + const pendingItems = useStore( + store, + useShallow((state) => state.pendingItems) + ); + + const getItemsWithPendingChanges = useCallback( + (originalItems: T[]): T[] => { + return originalItems.map((originalItem) => { + const pendingChanges = pendingItems.get(originalItem.id); + + return pendingChanges + ? { ...originalItem, ...pendingChanges } + : originalItem; + }); + }, + [pendingItems] + ); + + const hasPendingChanges = useMemo(() => { + return pendingItems.size > 0; + }, [pendingItems]); + + const getPendingChangesForSave = useCallback( + (originalItems: T[]): T[] => { + return originalItems.reduce((acc, originalItem) => { + const pendingChanges = pendingItems.get(originalItem.id); + + if (pendingChanges) { + acc.push({ + ...originalItem, + ...pendingChanges, + }); + } + + return acc; + }, []); + }, + [pendingItems] + ); + + return { + store, + setPendingItem, + unsetPendingItem, + clearPendingItems, + getItemsWithPendingChanges, + getPendingChangesForSave, + hasPendingChanges, + }; +}; diff --git a/frontend/src/Quality/QualityDefinition.ts b/frontend/src/Quality/QualityDefinitionModel.ts similarity index 59% rename from frontend/src/Quality/QualityDefinition.ts rename to frontend/src/Quality/QualityDefinitionModel.ts index 47b134cf3..a776da3f3 100644 --- a/frontend/src/Quality/QualityDefinition.ts +++ b/frontend/src/Quality/QualityDefinitionModel.ts @@ -1,11 +1,11 @@ +import ModelBase from 'App/ModelBase'; import Quality from './Quality'; -export default interface QualityDefinition { +export default interface QualityDefinitionModel extends ModelBase { quality: Quality; title: string; weight: number; minSize: number; maxSize: number; preferredSize: number; - id: number; } diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.tsx b/frontend/src/Settings/Quality/Definition/QualityDefinition.tsx index f25cf0e6c..2d243884c 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinition.tsx +++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.tsx @@ -1,31 +1,29 @@ import React, { useCallback } from 'react'; -import { useDispatch } from 'react-redux'; import TextInput from 'Components/Form/TextInput'; import Quality from 'Quality/Quality'; -import { setQualityDefinitionValue } from 'Store/Actions/settingsActions'; +import { useManageQualityDefinitions } from './useQualityDefinitions'; import styles from './QualityDefinition.css'; interface QualityDefinitionProps { id: number; quality: Quality; title: string; + updateDefinition: ReturnType< + typeof useManageQualityDefinitions + >['updateDefinition']; } -function QualityDefinition(props: QualityDefinitionProps) { - const { id, quality, title } = props; - const dispatch = useDispatch(); - +function QualityDefinition({ + id, + quality, + title, + updateDefinition, +}: QualityDefinitionProps) { const handleTitleChange = useCallback( ({ value }: { value: string }) => { - dispatch( - setQualityDefinitionValue({ - id, - name: 'title', - value, - }) - ); + updateDefinition(id, 'title', value); }, - [id, dispatch] + [id, updateDefinition] ); return ( diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinitions.tsx b/frontend/src/Settings/Quality/Definition/QualityDefinitions.tsx index a843d2225..cc243b7b8 100644 --- a/frontend/src/Settings/Quality/Definition/QualityDefinitions.tsx +++ b/frontend/src/Settings/Quality/Definition/QualityDefinitions.tsx @@ -1,42 +1,17 @@ -import { isEmpty } from 'lodash'; +import { useQueryClient } from '@tanstack/react-query'; import React, { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { createSelector } from 'reselect'; -import AppState from 'App/State/AppState'; import FieldSet from 'Components/FieldSet'; import PageSectionContent from 'Components/Page/PageSectionContent'; import usePrevious from 'Helpers/Hooks/usePrevious'; -import { - fetchQualityDefinitions, - saveQualityDefinitions, -} from 'Store/Actions/settingsActions'; import { OnChildStateChange, SetChildSave, } from 'typings/Settings/SettingsState'; import translate from 'Utilities/String/translate'; import QualityDefinition from './QualityDefinition'; +import { useManageQualityDefinitions } from './useQualityDefinitions'; import styles from './QualityDefinitions.css'; -function createQualityDefinitionsSelector() { - return createSelector( - (state: AppState) => state.settings.qualityDefinitions, - (qualityDefinitions) => { - const items = qualityDefinitions.items.map((item) => { - const pendingChanges = qualityDefinitions.pendingChanges[item.id] || {}; - - return Object.assign({}, item, pendingChanges); - }); - - return { - ...qualityDefinitions, - items, - hasPendingChanges: !isEmpty(qualityDefinitions.pendingChanges), - }; - } - ); -} - interface QualityDefinitionsProps { isResettingQualityDefinitions: boolean; setChildSave: SetChildSave; @@ -48,21 +23,27 @@ function QualityDefinitions({ setChildSave, onChildStateChange, }: QualityDefinitionsProps) { - const dispatch = useDispatch(); - const { items, isFetching, isPopulated, isSaving, error, hasPendingChanges } = - useSelector(createQualityDefinitionsSelector()); + const queryClient = useQueryClient(); + const { + items, + isFetching, + isFetched, + isSaving, + error, + hasPendingChanges, + updateDefinition, + saveQualityDefinitions, + } = useManageQualityDefinitions(); const wasResettingQualityDefinitions = usePrevious( isResettingQualityDefinitions ); useEffect(() => { - dispatch(fetchQualityDefinitions()); - setChildSave(() => { - dispatch(saveQualityDefinitions()); + saveQualityDefinitions(); }); - }, [dispatch, setChildSave]); + }, [saveQualityDefinitions, setChildSave]); useEffect(() => { onChildStateChange({ @@ -73,16 +54,20 @@ function QualityDefinitions({ useEffect(() => { if (wasResettingQualityDefinitions && !isResettingQualityDefinitions) { - dispatch(fetchQualityDefinitions()); + queryClient.invalidateQueries({ queryKey: ['/qualitydefinition'] }); } - }, [isResettingQualityDefinitions, wasResettingQualityDefinitions, dispatch]); + }, [ + isResettingQualityDefinitions, + wasResettingQualityDefinitions, + queryClient, + ]); return (
@@ -92,7 +77,13 @@ function QualityDefinitions({
{items.map((item) => { - return ; + return ( + + ); })}
diff --git a/frontend/src/Settings/Quality/Definition/useQualityDefinitions.ts b/frontend/src/Settings/Quality/Definition/useQualityDefinitions.ts new file mode 100644 index 000000000..e81179753 --- /dev/null +++ b/frontend/src/Settings/Quality/Definition/useQualityDefinitions.ts @@ -0,0 +1,99 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; +import useApiMutation from 'Helpers/Hooks/useApiMutation'; +import useApiQuery from 'Helpers/Hooks/useApiQuery'; +import { usePendingItemsStore } from 'Helpers/Hooks/usePendingItemsStore'; +import QualityDefinitionModel from 'Quality/QualityDefinitionModel'; +import { useSaveSettings } from 'Settings/useSettings'; + +const PATH = '/qualitydefinition'; +const DEFAULT_QUALITY_DEFINITIONS: QualityDefinitionModel[] = []; + +export const useQualityDefinitions = () => { + const result = useApiQuery({ + path: PATH, + }); + + return { + ...result, + data: result.data ?? DEFAULT_QUALITY_DEFINITIONS, + }; +}; + +export const useSaveQualityDefinitions = (onSuccess?: () => void) => { + const queryClient = useQueryClient(); + + const { mutate, isPending, error } = useApiMutation< + QualityDefinitionModel[], + QualityDefinitionModel[] + >({ + path: PATH, + method: 'PUT', + mutationOptions: { + onSuccess: (updatedSettings: QualityDefinitionModel[]) => { + queryClient.setQueryData( + [PATH], + updatedSettings + ); + onSuccess?.(); + }, + }, + }); + + return { + save: mutate, + isSaving: isPending, + saveError: error, + }; +}; + +export const useManageQualityDefinitions = () => { + const { data, isFetching, isFetched, error } = useQualityDefinitions(); + const { + setPendingItem, + clearPendingItems, + getItemsWithPendingChanges, + getPendingChangesForSave, + hasPendingChanges, + } = usePendingItemsStore(); + + const { save, isSaving, saveError } = useSaveSettings( + PATH, + clearPendingItems + ); + + const settings = useMemo(() => { + return { + items: getItemsWithPendingChanges(data), + hasPendingChanges, + }; + }, [data, getItemsWithPendingChanges, hasPendingChanges]); + + const saveQualityDefinitions = useCallback(() => { + const updatedSettings = getPendingChangesForSave(data); + save(updatedSettings); + }, [data, getPendingChangesForSave, save]); + + const updateDefinition = useCallback( + ( + id: number, + key: keyof QualityDefinitionModel, + value: QualityDefinitionModel[K] + ) => { + const originalItem = data.find((def) => def.id === id); + setPendingItem(id, key, value, originalItem); + }, + [data, setPendingItem] + ); + + return { + ...settings, + updateDefinition, + saveQualityDefinitions, + isFetching, + isFetched, + isSaving, + error, + saveError, + }; +}; diff --git a/frontend/src/Settings/Quality/Quality.tsx b/frontend/src/Settings/Quality/Quality.tsx index cf90d8dea..921c62206 100644 --- a/frontend/src/Settings/Quality/Quality.tsx +++ b/frontend/src/Settings/Quality/Quality.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useRef, useState } from 'react'; import CommandNames from 'Commands/CommandNames'; -import { useCommandExecuting } from 'Commands/useCommands'; +import { useCommandExecuting, useExecuteCommand } from 'Commands/useCommands'; +import ConfirmModal from 'Components/Modal/ConfirmModal'; import PageContent from 'Components/Page/PageContent'; import PageContentBody from 'Components/Page/PageContentBody'; import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; @@ -13,9 +14,9 @@ import { } from 'typings/Settings/SettingsState'; import translate from 'Utilities/String/translate'; import QualityDefinitions from './Definition/QualityDefinitions'; -import ResetQualityDefinitionsModal from './Reset/ResetQualityDefinitionsModal'; function Quality() { + const executeCommand = useExecuteCommand(); const isResettingQualityDefinitions = useCommandExecuting( CommandNames.ResetQualityDefinitions ); @@ -51,6 +52,15 @@ function Quality() { setIsConfirmQualityDefinitionResetModalOpen(false); }, []); + const handleResetQualityDefinitionsConfirmed = useCallback(() => { + executeCommand({ + name: CommandNames.ResetQualityDefinitions, + resetTitles: true, + }); + + setIsConfirmQualityDefinitionResetModalOpen(false); + }, [executeCommand]); + const handleSavePress = useCallback(() => { saveDefinitions.current?.(); }, []); @@ -68,13 +78,13 @@ function Quality() { label={translate('ResetDefinitions')} iconName={icons.REFRESH} isSpinning={isResettingQualityDefinitions} + isDisabled={isResettingQualityDefinitions} onPress={handleResetQualityDefinitionsPress} /> } onSavePress={handleSavePress} /> - - ); diff --git a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModal.tsx b/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModal.tsx deleted file mode 100644 index 311506086..000000000 --- a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModal.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import Modal from 'Components/Modal/Modal'; -import { sizes } from 'Helpers/Props'; -import ResetQualityDefinitionsModalContent from './ResetQualityDefinitionsModalContent'; - -interface ResetQualityDefinitionsModalProps { - isOpen: boolean; - onModalClose: () => void; -} - -function ResetQualityDefinitionsModal({ - isOpen, - onModalClose, -}: ResetQualityDefinitionsModalProps) { - return ( - - - - ); -} - -export default ResetQualityDefinitionsModal; diff --git a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.css b/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.css deleted file mode 100644 index 99c50adbe..000000000 --- a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.css +++ /dev/null @@ -1,3 +0,0 @@ -.messageContainer { - margin-bottom: 20px; -} diff --git a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.css.d.ts b/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.css.d.ts deleted file mode 100644 index 651320174..000000000 --- a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.css.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -// This file is automatically generated. -// Please do not change this file! -interface CssExports { - 'messageContainer': string; -} -export const cssExports: CssExports; -export default cssExports; diff --git a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.tsx b/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.tsx deleted file mode 100644 index 50ddc20e5..000000000 --- a/frontend/src/Settings/Quality/Reset/ResetQualityDefinitionsModalContent.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import CommandNames from 'Commands/CommandNames'; -import { useCommandExecuting, useExecuteCommand } from 'Commands/useCommands'; -import FormGroup from 'Components/Form/FormGroup'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import FormLabel from 'Components/Form/FormLabel'; -import Button from 'Components/Link/Button'; -import ModalBody from 'Components/Modal/ModalBody'; -import ModalContent from 'Components/Modal/ModalContent'; -import ModalFooter from 'Components/Modal/ModalFooter'; -import ModalHeader from 'Components/Modal/ModalHeader'; -import { inputTypes, kinds } from 'Helpers/Props'; -import { InputChanged } from 'typings/inputs'; -import translate from 'Utilities/String/translate'; -import styles from './ResetQualityDefinitionsModalContent.css'; - -interface ResetQualityDefinitionsModalContentProps { - onModalClose: () => void; -} - -function ResetQualityDefinitionsModalContent({ - onModalClose, -}: ResetQualityDefinitionsModalContentProps) { - const executeCommand = useExecuteCommand(); - const isResettingQualityDefinitions = useCommandExecuting( - CommandNames.ResetQualityDefinitions - ); - - const [resetDefinitionTitles, setResetDefinitionTitles] = useState(false); - - const handleResetDefinitionTitlesChange = useCallback( - ({ value }: InputChanged) => { - setResetDefinitionTitles(value); - }, - [] - ); - - const handleResetQualityDefinitionsConfirmed = useCallback(() => { - const resetTitles = resetDefinitionTitles; - - setResetDefinitionTitles(false); - - executeCommand({ - name: CommandNames.ResetQualityDefinitions, - resetTitles, - }); - onModalClose(); - }, [resetDefinitionTitles, executeCommand, onModalClose]); - - return ( - - {translate('ResetQualityDefinitions')} - - -
- {translate('ResetQualityDefinitionsMessageText')} -
- - - {translate('ResetTitles')} - - - -
- - - - - - -
- ); -} - -export default ResetQualityDefinitionsModalContent; diff --git a/frontend/src/Store/Actions/Settings/qualityDefinitions.js b/frontend/src/Store/Actions/Settings/qualityDefinitions.js deleted file mode 100644 index c1ac33e6a..000000000 --- a/frontend/src/Store/Actions/Settings/qualityDefinitions.js +++ /dev/null @@ -1,137 +0,0 @@ -import _ from 'lodash'; -import { createAction } from 'redux-actions'; -import { batchActions } from 'redux-batched-actions'; -import { clearPendingChanges, set, update } from 'Store/Actions/baseActions'; -import createFetchHandler from 'Store/Actions/Creators/createFetchHandler'; -import createSaveHandler from 'Store/Actions/Creators/createSaveHandler'; -import { createThunk } from 'Store/thunks'; -import createAjaxRequest from 'Utilities/createAjaxRequest'; -import getSectionState from 'Utilities/State/getSectionState'; -import updateSectionState from 'Utilities/State/updateSectionState'; - -// -// Variables - -const section = 'settings.qualityDefinitions'; - -// -// Actions Types - -export const FETCH_QUALITY_DEFINITIONS = 'settings/qualityDefinitions/fetchQualityDefinitions'; -export const SAVE_QUALITY_DEFINITIONS = 'settings/qualityDefinitions/saveQualityDefinitions'; -export const SET_QUALITY_DEFINITION_VALUE = 'settings/qualityDefinitions/setQualityDefinitionValue'; - -// -// Action Creators - -export const fetchQualityDefinitions = createThunk(FETCH_QUALITY_DEFINITIONS); -export const saveQualityDefinitions = createThunk(SAVE_QUALITY_DEFINITIONS); - -export const setQualityDefinitionValue = createAction(SET_QUALITY_DEFINITION_VALUE); - -// -// Details - -export default { - - // - // State - - defaultState: { - isFetching: false, - isPopulated: false, - error: null, - items: [], - isSaving: false, - saveError: null, - pendingChanges: {} - }, - - // - // Action Handlers - - actionHandlers: { - [FETCH_QUALITY_DEFINITIONS]: createFetchHandler(section, '/qualitydefinition'), - [SAVE_QUALITY_DEFINITIONS]: createSaveHandler(section, '/qualitydefinition'), - - [SAVE_QUALITY_DEFINITIONS]: function(getState, payload, dispatch) { - const qualityDefinitions = getState().settings.qualityDefinitions; - - const upatedDefinitions = Object.keys(qualityDefinitions.pendingChanges).map((key) => { - const id = parseInt(key); - const pendingChanges = qualityDefinitions.pendingChanges[id] || {}; - const item = _.find(qualityDefinitions.items, { id }); - - return Object.assign({}, item, pendingChanges); - }); - - // If there is nothing to save don't bother isSaving - if (!upatedDefinitions || !upatedDefinitions.length) { - return; - } - - dispatch(set({ - section, - isSaving: true - })); - - const promise = createAjaxRequest({ - method: 'PUT', - url: '/qualitydefinition/update', - data: JSON.stringify(upatedDefinitions), - contentType: 'application/json', - dataType: 'json' - }).request; - - promise.done((data) => { - dispatch(batchActions([ - set({ - section, - isSaving: false, - saveError: null - }), - - update({ section, data }), - clearPendingChanges({ section }) - ])); - }); - - promise.fail((xhr) => { - dispatch(set({ - section, - isSaving: false, - saveError: xhr - })); - }); - } - }, - - // - // Reducers - - reducers: { - [SET_QUALITY_DEFINITION_VALUE]: function(state, { payload }) { - const { id, name, value } = payload; - const newState = getSectionState(state, section); - newState.pendingChanges = _.cloneDeep(newState.pendingChanges); - - const pendingState = newState.pendingChanges[id] || {}; - const currentValue = _.find(newState.items, { id })[name]; - - if (currentValue === value) { - delete pendingState[name]; - } else { - pendingState[name] = value; - } - - if (_.isEmpty(pendingState)) { - delete newState.pendingChanges[id]; - } else { - newState.pendingChanges[id] = pendingState; - } - - return updateSectionState(state, section, newState); - } - } - -}; diff --git a/frontend/src/Store/Actions/settingsActions.js b/frontend/src/Store/Actions/settingsActions.js index bf3ade40b..1b3c9c3b2 100644 --- a/frontend/src/Store/Actions/settingsActions.js +++ b/frontend/src/Store/Actions/settingsActions.js @@ -20,7 +20,6 @@ import metadata from './Settings/metadata'; import naming from './Settings/naming'; import namingExamples from './Settings/namingExamples'; import notifications from './Settings/notifications'; -import qualityDefinitions from './Settings/qualityDefinitions'; export * from './Settings/autoTaggingSpecifications'; export * from './Settings/autoTaggings'; @@ -42,7 +41,6 @@ export * from './Settings/metadata'; export * from './Settings/naming'; export * from './Settings/namingExamples'; export * from './Settings/notifications'; -export * from './Settings/qualityDefinitions'; // // Variables @@ -73,8 +71,7 @@ export const defaultState = { metadata: metadata.defaultState, naming: naming.defaultState, namingExamples: namingExamples.defaultState, - notifications: notifications.defaultState, - qualityDefinitions: qualityDefinitions.defaultState + notifications: notifications.defaultState }; export const persistState = [ @@ -104,8 +101,7 @@ export const actionHandlers = handleThunks({ ...metadata.actionHandlers, ...naming.actionHandlers, ...namingExamples.actionHandlers, - ...notifications.actionHandlers, - ...qualityDefinitions.actionHandlers + ...notifications.actionHandlers }); // @@ -131,7 +127,6 @@ export const reducers = createHandleActions({ ...metadata.reducers, ...naming.reducers, ...namingExamples.reducers, - ...notifications.reducers, - ...qualityDefinitions.reducers + ...notifications.reducers }, defaultState, section); diff --git a/frontend/src/typings/QualityDefinition.ts b/frontend/src/typings/QualityDefinition.ts deleted file mode 100644 index 373f2b181..000000000 --- a/frontend/src/typings/QualityDefinition.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Quality from 'Quality/Quality'; - -interface QualityDefinition { - id: number; - quality: Quality; - title: string; - weight: number; -} - -export default QualityDefinition; diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 46b5a8aa2..2f84253d6 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -1817,7 +1817,6 @@ "ResetDefinitions": "Reset Definitions", "ResetQualityDefinitions": "Reset Quality Definitions", "ResetQualityDefinitionsMessageText": "Are you sure you want to reset quality definitions?", - "ResetTitles": "Reset Titles", "Restart": "Restart", "RestartLater": "I'll restart later", "RestartNow": "Restart Now",