mirror of
https://github.com/Sonarr/Sonarr
synced 2026-05-01 01:21:35 +02:00
Use react-query for Naming settings
This commit is contained in:
parent
04095df460
commit
7eabd1bb7a
11 changed files with 174 additions and 276 deletions
|
|
@ -7,6 +7,10 @@ import AppSectionState, {
|
|||
PagedAppSectionState,
|
||||
} from 'App/State/AppSectionState';
|
||||
import Language from 'Language/Language';
|
||||
import {
|
||||
NamingSettingsModel,
|
||||
NamingExamples,
|
||||
} from 'Settings/MediaManagement/Naming/useNamingSettings';
|
||||
import AutoTagging, { AutoTaggingSpecification } from 'typings/AutoTagging';
|
||||
import CustomFormat from 'typings/CustomFormat';
|
||||
import CustomFormatSpecification from 'typings/CustomFormatSpecification';
|
||||
|
|
@ -21,8 +25,6 @@ import DownloadClientOptions from 'typings/Settings/DownloadClientOptions';
|
|||
import General from 'typings/Settings/General';
|
||||
import IndexerOptions from 'typings/Settings/IndexerOptions';
|
||||
import MediaManagement from 'typings/Settings/MediaManagement';
|
||||
import NamingConfig from 'typings/Settings/NamingConfig';
|
||||
import NamingExample from 'typings/Settings/NamingExample';
|
||||
import MetadataAppState from './MetadataAppState';
|
||||
|
||||
type Presets<T> = T & {
|
||||
|
|
@ -66,10 +68,10 @@ export interface MediaManagementAppState
|
|||
AppSectionSaveState {}
|
||||
|
||||
export interface NamingAppState
|
||||
extends AppSectionItemState<NamingConfig>,
|
||||
extends AppSectionItemState<NamingSettingsModel>,
|
||||
AppSectionSaveState {}
|
||||
|
||||
export type NamingExamplesAppState = AppSectionItemState<NamingExample>;
|
||||
export type NamingExamplesAppState = AppSectionItemState<NamingExamples>;
|
||||
|
||||
export interface ImportListAppState
|
||||
extends AppSectionState<ImportList>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import React, { useCallback } from 'react';
|
||||
import { SelectProvider, useSelect } from 'App/Select/SelectContext';
|
||||
import AppState from 'App/State/AppState';
|
||||
import CommandNames from 'Commands/CommandNames';
|
||||
import { useExecuteCommand } from 'Commands/useCommands';
|
||||
import Alert from 'Components/Alert';
|
||||
|
|
@ -16,7 +14,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
|||
import { kinds } from 'Helpers/Props';
|
||||
import formatSeason from 'Season/formatSeason';
|
||||
import { useSingleSeries } from 'Series/useSeries';
|
||||
import { fetchNamingSettings } from 'Store/Actions/settingsActions';
|
||||
import { useNamingSettings } from 'Settings/MediaManagement/Naming/useNamingSettings';
|
||||
import { CheckInputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import OrganizePreviewRow from './OrganizePreviewRow';
|
||||
|
|
@ -44,7 +42,6 @@ function OrganizePreviewModalContentInner({
|
|||
seasonNumber,
|
||||
onModalClose,
|
||||
}: OrganizePreviewModalContentProps) {
|
||||
const dispatch = useDispatch();
|
||||
const executeCommand = useExecuteCommand();
|
||||
const {
|
||||
items,
|
||||
|
|
@ -55,10 +52,10 @@ function OrganizePreviewModalContentInner({
|
|||
|
||||
const {
|
||||
isFetching: isNamingFetching,
|
||||
isPopulated: isNamingPopulated,
|
||||
isFetched: isNamingFetched,
|
||||
error: namingError,
|
||||
item: naming,
|
||||
} = useSelector((state: AppState) => state.settings.naming);
|
||||
data: naming,
|
||||
} = useNamingSettings();
|
||||
|
||||
const series = useSingleSeries(seriesId)!;
|
||||
|
||||
|
|
@ -66,7 +63,7 @@ function OrganizePreviewModalContentInner({
|
|||
useSelect<OrganizePreviewModel>();
|
||||
|
||||
const isFetching = isPreviewFetching || isNamingFetching;
|
||||
const isPopulated = isPreviewFetched && isNamingPopulated;
|
||||
const isPopulated = isPreviewFetched && isNamingFetched;
|
||||
const error = previewError || namingError;
|
||||
const { renameEpisodes } = naming;
|
||||
const episodeFormat = naming[`${series.seriesType}EpisodeFormat`];
|
||||
|
|
@ -96,10 +93,6 @@ function OrganizePreviewModalContentInner({
|
|||
onModalClose();
|
||||
}, [seriesId, getSelectedIds, executeCommand, onModalClose]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchNamingSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
import Alert from 'Components/Alert';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Form from 'Components/Form/Form';
|
||||
|
|
@ -19,13 +18,12 @@ import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|||
import {
|
||||
fetchMediaManagementSettings,
|
||||
saveMediaManagementSettings,
|
||||
saveNamingSettings,
|
||||
setMediaManagementSettingsValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
|
||||
import { useIsWindows } from 'System/Status/useSystemStatus';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import isEmpty from 'Utilities/Object/isEmpty';
|
||||
import { SettingsStateChange } from 'typings/Settings/SettingsState';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import Naming from './Naming/Naming';
|
||||
import AddRootFolder from './RootFolder/AddRootFolder';
|
||||
|
|
@ -140,10 +138,9 @@ const seasonPackUpgradeOptions: EnhancedSelectInputValue<string>[] = [
|
|||
function MediaManagement() {
|
||||
const dispatch = useDispatch();
|
||||
const showAdvancedSettings = useShowAdvancedSettings();
|
||||
const hasNamingPendingChanges = !isEmpty(
|
||||
useSelector((state: AppState) => state.settings.naming.pendingChanges)
|
||||
);
|
||||
|
||||
const isWindows = useIsWindows();
|
||||
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
|
|
@ -156,9 +153,24 @@ function MediaManagement() {
|
|||
validationWarnings,
|
||||
} = useSelector(createSettingsSectionSelector(SECTION));
|
||||
|
||||
const [naming, setNaming] = useState<SettingsStateChange>({
|
||||
isSaving: false,
|
||||
hasPendingChanges: false,
|
||||
});
|
||||
|
||||
const saveSettings = useRef<{
|
||||
naming: () => void;
|
||||
}>({
|
||||
naming: () => {},
|
||||
});
|
||||
|
||||
const handleSetNamingSave = useCallback((saveCallback: () => void) => {
|
||||
saveSettings.current.naming = saveCallback;
|
||||
}, []);
|
||||
|
||||
const handleSavePress = useCallback(() => {
|
||||
dispatch(saveMediaManagementSettings());
|
||||
dispatch(saveNamingSettings());
|
||||
saveSettings.current.naming();
|
||||
}, [dispatch]);
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
|
|
@ -180,13 +192,16 @@ function MediaManagement() {
|
|||
return (
|
||||
<PageContent title={translate('MediaManagementSettings')}>
|
||||
<SettingsToolbar
|
||||
isSaving={isSaving}
|
||||
hasPendingChanges={hasNamingPendingChanges || hasPendingChanges}
|
||||
isSaving={isSaving || naming.isSaving}
|
||||
hasPendingChanges={naming.hasPendingChanges || hasPendingChanges}
|
||||
onSavePress={handleSavePress}
|
||||
/>
|
||||
|
||||
<PageContentBody>
|
||||
<Naming />
|
||||
<Naming
|
||||
setChildSave={handleSetNamingSave}
|
||||
onChildStateChange={setNaming}
|
||||
/>
|
||||
|
||||
{isFetching ? (
|
||||
<FieldSet legend={translate('NamingSettings')}>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Form from 'Components/Form/Form';
|
||||
|
|
@ -11,41 +8,23 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
|||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import { EnhancedSelectInputValue } from 'Components/Form/Select/EnhancedSelectInput';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import useDebounce from 'Helpers/Hooks/useDebounce';
|
||||
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||
import { useShowAdvancedSettings } from 'Settings/advancedSettingsStore';
|
||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import {
|
||||
fetchNamingExamples,
|
||||
fetchNamingSettings,
|
||||
setNamingSettingsValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import NamingConfig from 'typings/Settings/NamingConfig';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NamingModal from './NamingModal';
|
||||
import {
|
||||
NamingSettingsModel,
|
||||
useManageNamingSettings,
|
||||
useNamingExamples,
|
||||
} from './useNamingSettings';
|
||||
import styles from './Naming.css';
|
||||
|
||||
const SECTION = 'naming';
|
||||
|
||||
function createNamingSelector() {
|
||||
return createSelector(
|
||||
(state: AppState) => state.settings.namingExamples,
|
||||
createSettingsSectionSelector(SECTION),
|
||||
(namingExamples, sectionSettings) => {
|
||||
return {
|
||||
examples: namingExamples.item,
|
||||
examplesPopulated: namingExamples.isPopulated,
|
||||
...sectionSettings,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
interface NamingModalOptions {
|
||||
name: keyof Pick<
|
||||
NamingConfig,
|
||||
NamingSettingsModel,
|
||||
| 'standardEpisodeFormat'
|
||||
| 'dailyEpisodeFormat'
|
||||
| 'animeEpisodeFormat'
|
||||
|
|
@ -60,51 +39,46 @@ interface NamingModalOptions {
|
|||
additional?: boolean;
|
||||
}
|
||||
|
||||
function Naming() {
|
||||
interface NamingProps {
|
||||
setChildSave: (saveCallback: () => void) => void;
|
||||
onChildStateChange: (state: {
|
||||
isSaving: boolean;
|
||||
hasPendingChanges: boolean;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
function Naming({ setChildSave, onChildStateChange }: NamingProps) {
|
||||
const advancedSettings = useShowAdvancedSettings();
|
||||
const {
|
||||
settings,
|
||||
updateSetting,
|
||||
isFetching,
|
||||
error,
|
||||
settings,
|
||||
hasSettings,
|
||||
examples,
|
||||
examplesPopulated,
|
||||
} = useSelector(createNamingSelector());
|
||||
hasPendingChanges,
|
||||
isSaving,
|
||||
saveSettings,
|
||||
} = useManageNamingSettings();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const debouncedSettings = useDebounce(settings, 300);
|
||||
const { examples } = useNamingExamples(debouncedSettings);
|
||||
const examplesPopulated = !!examples;
|
||||
|
||||
const [isNamingModalOpen, setNamingModalOpen, setNamingModalClosed] =
|
||||
useModalOpenState(false);
|
||||
const [namingModalOptions, setNamingModalOptions] =
|
||||
useState<NamingModalOptions | null>(null);
|
||||
const namingExampleTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchNamingSettings());
|
||||
dispatch(fetchNamingExamples());
|
||||
|
||||
return () => {
|
||||
dispatch(clearPendingChanges({ section: 'settings.naming' }));
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error 'setNamingSettingsValue' isn't typed yet
|
||||
dispatch(setNamingSettingsValue(change));
|
||||
const key = change.name as keyof NamingSettingsModel;
|
||||
|
||||
if (namingExampleTimeout.current) {
|
||||
clearTimeout(namingExampleTimeout.current);
|
||||
}
|
||||
|
||||
namingExampleTimeout.current = setTimeout(() => {
|
||||
dispatch(fetchNamingExamples());
|
||||
}, 1000);
|
||||
updateSetting(key, change.value as NamingSettingsModel[typeof key]);
|
||||
},
|
||||
[dispatch]
|
||||
[updateSetting]
|
||||
);
|
||||
|
||||
const onStandardNamingModalOpenClick = useCallback(() => {
|
||||
const handleStandardNamingModalOpenClick = useCallback(() => {
|
||||
setNamingModalOpen();
|
||||
|
||||
setNamingModalOptions({
|
||||
|
|
@ -115,7 +89,7 @@ function Naming() {
|
|||
});
|
||||
}, [setNamingModalOpen, setNamingModalOptions]);
|
||||
|
||||
const onDailyNamingModalOpenClick = useCallback(() => {
|
||||
const handleDailyNamingModalOpenClick = useCallback(() => {
|
||||
setNamingModalOpen();
|
||||
|
||||
setNamingModalOptions({
|
||||
|
|
@ -127,7 +101,7 @@ function Naming() {
|
|||
});
|
||||
}, [setNamingModalOpen, setNamingModalOptions]);
|
||||
|
||||
const onAnimeNamingModalOpenClick = useCallback(() => {
|
||||
const handleAnimeNamingModalOpenClick = useCallback(() => {
|
||||
setNamingModalOpen();
|
||||
|
||||
setNamingModalOptions({
|
||||
|
|
@ -139,7 +113,7 @@ function Naming() {
|
|||
});
|
||||
}, [setNamingModalOpen, setNamingModalOptions]);
|
||||
|
||||
const onSeriesFolderNamingModalOpenClick = useCallback(() => {
|
||||
const handleSeriesFolderNamingModalOpenClick = useCallback(() => {
|
||||
setNamingModalOpen();
|
||||
|
||||
setNamingModalOptions({
|
||||
|
|
@ -147,7 +121,7 @@ function Naming() {
|
|||
});
|
||||
}, [setNamingModalOpen, setNamingModalOptions]);
|
||||
|
||||
const onSeasonFolderNamingModalOpenClick = useCallback(() => {
|
||||
const handleSeasonFolderNamingModalOpenClick = useCallback(() => {
|
||||
setNamingModalOpen();
|
||||
|
||||
setNamingModalOptions({
|
||||
|
|
@ -156,7 +130,7 @@ function Naming() {
|
|||
});
|
||||
}, [setNamingModalOpen, setNamingModalOptions]);
|
||||
|
||||
const onSpecialsFolderNamingModalOpenClick = useCallback(() => {
|
||||
const handleSpecialsFolderNamingModalOpenClick = useCallback(() => {
|
||||
setNamingModalOpen();
|
||||
|
||||
setNamingModalOptions({
|
||||
|
|
@ -282,6 +256,17 @@ function Naming() {
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onChildStateChange({
|
||||
hasPendingChanges,
|
||||
isSaving,
|
||||
});
|
||||
}, [hasPendingChanges, isSaving, onChildStateChange]);
|
||||
|
||||
useEffect(() => {
|
||||
setChildSave(saveSettings);
|
||||
}, [setChildSave, saveSettings]);
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('EpisodeNaming')}>
|
||||
{isFetching ? <LoadingIndicator /> : null}
|
||||
|
|
@ -358,7 +343,9 @@ function Naming() {
|
|||
type={inputTypes.TEXT}
|
||||
name="standardEpisodeFormat"
|
||||
buttons={
|
||||
<FormInputButton onPress={onStandardNamingModalOpenClick}>
|
||||
<FormInputButton
|
||||
onPress={handleStandardNamingModalOpenClick}
|
||||
>
|
||||
?
|
||||
</FormInputButton>
|
||||
}
|
||||
|
|
@ -380,7 +367,7 @@ function Naming() {
|
|||
type={inputTypes.TEXT}
|
||||
name="dailyEpisodeFormat"
|
||||
buttons={
|
||||
<FormInputButton onPress={onDailyNamingModalOpenClick}>
|
||||
<FormInputButton onPress={handleDailyNamingModalOpenClick}>
|
||||
?
|
||||
</FormInputButton>
|
||||
}
|
||||
|
|
@ -402,7 +389,7 @@ function Naming() {
|
|||
type={inputTypes.TEXT}
|
||||
name="animeEpisodeFormat"
|
||||
buttons={
|
||||
<FormInputButton onPress={onAnimeNamingModalOpenClick}>
|
||||
<FormInputButton onPress={handleAnimeNamingModalOpenClick}>
|
||||
?
|
||||
</FormInputButton>
|
||||
}
|
||||
|
|
@ -430,7 +417,9 @@ function Naming() {
|
|||
type={inputTypes.TEXT}
|
||||
name="seriesFolderFormat"
|
||||
buttons={
|
||||
<FormInputButton onPress={onSeriesFolderNamingModalOpenClick}>
|
||||
<FormInputButton
|
||||
onPress={handleSeriesFolderNamingModalOpenClick}
|
||||
>
|
||||
?
|
||||
</FormInputButton>
|
||||
}
|
||||
|
|
@ -455,7 +444,9 @@ function Naming() {
|
|||
type={inputTypes.TEXT}
|
||||
name="seasonFolderFormat"
|
||||
buttons={
|
||||
<FormInputButton onPress={onSeasonFolderNamingModalOpenClick}>
|
||||
<FormInputButton
|
||||
onPress={handleSeasonFolderNamingModalOpenClick}
|
||||
>
|
||||
?
|
||||
</FormInputButton>
|
||||
}
|
||||
|
|
@ -481,7 +472,9 @@ function Naming() {
|
|||
type={inputTypes.TEXT}
|
||||
name="specialsFolderFormat"
|
||||
buttons={
|
||||
<FormInputButton onPress={onSpecialsFolderNamingModalOpenClick}>
|
||||
<FormInputButton
|
||||
onPress={handleSpecialsFolderNamingModalOpenClick}
|
||||
>
|
||||
?
|
||||
</FormInputButton>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { sizes } from 'Helpers/Props';
|
||||
import NamingConfig from 'typings/Settings/NamingConfig';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NamingOption from './NamingOption';
|
||||
import TokenCase from './TokenCase';
|
||||
import TokenSeparator from './TokenSeparator';
|
||||
import { NamingSettingsModel } from './useNamingSettings';
|
||||
import styles from './NamingModal.css';
|
||||
|
||||
type SeparatorInputOption = Omit<SelectInputOption, 'key'> & {
|
||||
|
|
@ -274,7 +274,7 @@ const originalTokens = [
|
|||
interface NamingModalProps {
|
||||
isOpen: boolean;
|
||||
name: keyof Pick<
|
||||
NamingConfig,
|
||||
NamingSettingsModel,
|
||||
| 'standardEpisodeFormat'
|
||||
| 'dailyEpisodeFormat'
|
||||
| 'animeEpisodeFormat'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
import { keepPreviousData } from '@tanstack/react-query';
|
||||
import { useMemo } from 'react';
|
||||
import useApiQuery from 'Helpers/Hooks/useApiQuery';
|
||||
import { useManageSettings, useSettings } from 'Settings/useSettings';
|
||||
import { PendingSection } from 'typings/pending';
|
||||
import { QueryParams } from 'Utilities/Fetch/getQueryString';
|
||||
|
||||
const PATH = '/settings/naming';
|
||||
const EXAMPLES_PATH = '/settings/naming/examples';
|
||||
|
||||
export interface NamingSettingsModel {
|
||||
renameEpisodes: boolean;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: number;
|
||||
customColonReplacementFormat: string;
|
||||
multiEpisodeStyle: number;
|
||||
standardEpisodeFormat: string;
|
||||
dailyEpisodeFormat: string;
|
||||
animeEpisodeFormat: string;
|
||||
seriesFolderFormat: string;
|
||||
seasonFolderFormat: string;
|
||||
specialsFolderFormat: string;
|
||||
}
|
||||
|
||||
export interface NamingExamples {
|
||||
singleEpisodeExample: string;
|
||||
multiEpisodeExample: string;
|
||||
dailyEpisodeExample: string;
|
||||
animeEpisodeExample: string;
|
||||
animeMultiEpisodeExample: string;
|
||||
seriesFolderExample: string;
|
||||
seasonFolderExample: string;
|
||||
specialsFolderExample: string;
|
||||
}
|
||||
|
||||
export const useNamingSettings = () => {
|
||||
return useSettings<NamingSettingsModel>(PATH);
|
||||
};
|
||||
|
||||
export const useManageNamingSettings = () => {
|
||||
return useManageSettings<NamingSettingsModel>(PATH);
|
||||
};
|
||||
|
||||
export const useNamingExamples = (
|
||||
settings: PendingSection<NamingSettingsModel>
|
||||
) => {
|
||||
const queryParams = useMemo<QueryParams>(() => {
|
||||
return Object.entries(settings).reduce((acc, [key, value]) => {
|
||||
if (typeof value === 'object' && 'value' in value) {
|
||||
acc[key] = value.value;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as QueryParams);
|
||||
}, [settings]);
|
||||
|
||||
const { data, error, isFetching } = useApiQuery<NamingExamples>({
|
||||
path: EXAMPLES_PATH,
|
||||
method: 'GET',
|
||||
queryParams,
|
||||
queryOptions: {
|
||||
placeholderData: keepPreviousData,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
examples: data,
|
||||
isExamplesFetching: isFetching,
|
||||
examplesError: error,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import { createAction } from 'redux-actions';
|
||||
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
|
||||
import createSaveHandler from 'Store/Actions/Creators/createSaveHandler';
|
||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||
import { createThunk } from 'Store/thunks';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
||||
const section = 'settings.naming';
|
||||
|
||||
//
|
||||
// Actions Types
|
||||
|
||||
export const FETCH_NAMING_SETTINGS = 'settings/naming/fetchNamingSettings';
|
||||
export const SAVE_NAMING_SETTINGS = 'settings/naming/saveNamingSettings';
|
||||
export const SET_NAMING_SETTINGS_VALUE = 'settings/naming/setNamingSettingsValue';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchNamingSettings = createThunk(FETCH_NAMING_SETTINGS);
|
||||
export const saveNamingSettings = createThunk(SAVE_NAMING_SETTINGS);
|
||||
export const setNamingSettingsValue = createAction(SET_NAMING_SETTINGS_VALUE, (payload) => {
|
||||
return {
|
||||
section,
|
||||
...payload
|
||||
};
|
||||
});
|
||||
|
||||
//
|
||||
// Details
|
||||
|
||||
export default {
|
||||
|
||||
//
|
||||
// State
|
||||
|
||||
defaultState: {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
pendingChanges: {},
|
||||
isSaving: false,
|
||||
saveError: null,
|
||||
item: {}
|
||||
},
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
actionHandlers: {
|
||||
[FETCH_NAMING_SETTINGS]: createFetchHandler(section, '/config/naming'),
|
||||
[SAVE_NAMING_SETTINGS]: createSaveHandler(section, '/config/naming')
|
||||
},
|
||||
|
||||
//
|
||||
// Reducers
|
||||
|
||||
reducers: {
|
||||
[SET_NAMING_SETTINGS_VALUE]: createSetSettingValueReducer(section)
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
import { batchActions } from 'redux-batched-actions';
|
||||
import { set, update } from 'Store/Actions/baseActions';
|
||||
import { createThunk } from 'Store/thunks';
|
||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
||||
const section = 'settings.namingExamples';
|
||||
|
||||
//
|
||||
// Actions Types
|
||||
|
||||
export const FETCH_NAMING_EXAMPLES = 'settings/namingExamples/fetchNamingExamples';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchNamingExamples = createThunk(FETCH_NAMING_EXAMPLES);
|
||||
|
||||
//
|
||||
// Details
|
||||
|
||||
export default {
|
||||
|
||||
//
|
||||
// State
|
||||
|
||||
defaultState: {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
item: {}
|
||||
},
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
actionHandlers: {
|
||||
[FETCH_NAMING_EXAMPLES]: function(getState, payload, dispatch) {
|
||||
dispatch(set({ section, isFetching: true }));
|
||||
|
||||
const naming = getState().settings.naming;
|
||||
|
||||
const promise = createAjaxRequest({
|
||||
url: '/config/naming/examples',
|
||||
data: Object.assign({}, naming.item, naming.pendingChanges)
|
||||
}).request;
|
||||
|
||||
promise.done((data) => {
|
||||
dispatch(batchActions([
|
||||
update({ section, data }),
|
||||
|
||||
set({
|
||||
section,
|
||||
isFetching: false,
|
||||
isPopulated: true,
|
||||
error: null
|
||||
})
|
||||
]));
|
||||
});
|
||||
|
||||
promise.fail((xhr) => {
|
||||
dispatch(set({
|
||||
section,
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: xhr
|
||||
}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Reducers
|
||||
|
||||
reducers: {}
|
||||
|
||||
};
|
||||
|
|
@ -17,8 +17,6 @@ import indexers from './Settings/indexers';
|
|||
import languages from './Settings/languages';
|
||||
import mediaManagement from './Settings/mediaManagement';
|
||||
import metadata from './Settings/metadata';
|
||||
import naming from './Settings/naming';
|
||||
import namingExamples from './Settings/namingExamples';
|
||||
|
||||
export * from './Settings/autoTaggingSpecifications';
|
||||
export * from './Settings/autoTaggings';
|
||||
|
|
@ -37,8 +35,6 @@ export * from './Settings/indexers';
|
|||
export * from './Settings/languages';
|
||||
export * from './Settings/mediaManagement';
|
||||
export * from './Settings/metadata';
|
||||
export * from './Settings/naming';
|
||||
export * from './Settings/namingExamples';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
|
@ -66,9 +62,7 @@ export const defaultState = {
|
|||
indexers: indexers.defaultState,
|
||||
languages: languages.defaultState,
|
||||
mediaManagement: mediaManagement.defaultState,
|
||||
metadata: metadata.defaultState,
|
||||
naming: naming.defaultState,
|
||||
namingExamples: namingExamples.defaultState
|
||||
metadata: metadata.defaultState
|
||||
};
|
||||
|
||||
export const persistState = [
|
||||
|
|
@ -95,9 +89,7 @@ export const actionHandlers = handleThunks({
|
|||
...indexers.actionHandlers,
|
||||
...languages.actionHandlers,
|
||||
...mediaManagement.actionHandlers,
|
||||
...metadata.actionHandlers,
|
||||
...naming.actionHandlers,
|
||||
...namingExamples.actionHandlers
|
||||
...metadata.actionHandlers
|
||||
});
|
||||
|
||||
//
|
||||
|
|
@ -120,8 +112,6 @@ export const reducers = createHandleActions({
|
|||
...indexers.reducers,
|
||||
...languages.reducers,
|
||||
...mediaManagement.reducers,
|
||||
...metadata.reducers,
|
||||
...naming.reducers,
|
||||
...namingExamples.reducers
|
||||
...metadata.reducers
|
||||
|
||||
}, defaultState, section);
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
export default interface NamingConfig {
|
||||
renameEpisodes: boolean;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: number;
|
||||
customColonReplacementFormat: string;
|
||||
multiEpisodeStyle: number;
|
||||
standardEpisodeFormat: string;
|
||||
dailyEpisodeFormat: string;
|
||||
animeEpisodeFormat: string;
|
||||
seriesFolderFormat: string;
|
||||
seasonFolderFormat: string;
|
||||
specialsFolderFormat: string;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
export default interface NamingExample {
|
||||
singleEpisodeExample: string;
|
||||
multiEpisodeExample: string;
|
||||
dailyEpisodeExample: string;
|
||||
animeEpisodeExample: string;
|
||||
animeMultiEpisodeExample: string;
|
||||
seriesFolderExample: string;
|
||||
seasonFolderExample: string;
|
||||
specialsFolderExample: string;
|
||||
}
|
||||
Loading…
Reference in a new issue