mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-18 21:35:27 -04:00
Use react-query for Metadata
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
import { AppSectionProviderState } from 'App/State/AppSectionState';
|
||||
import Metadata from 'typings/Metadata';
|
||||
|
||||
type MetadataAppState = AppSectionProviderState<Metadata>;
|
||||
|
||||
export default MetadataAppState;
|
||||
@@ -25,7 +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 MetadataAppState from './MetadataAppState';
|
||||
|
||||
type Presets<T> = T & {
|
||||
presets: T[];
|
||||
@@ -136,7 +135,6 @@ interface SettingsAppState {
|
||||
indexers: IndexerAppState;
|
||||
languages: LanguageSettingsAppState;
|
||||
mediaManagement: MediaManagementAppState;
|
||||
metadata: MetadataAppState;
|
||||
naming: NamingAppState;
|
||||
namingExamples: NamingExamplesAppState;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import AppState from 'App/State/AppState';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import Form from 'Components/Form/Form';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
@@ -15,14 +13,10 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import usePrevious from 'Helpers/Hooks/usePrevious';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import {
|
||||
saveMetadata,
|
||||
setMetadataFieldValue,
|
||||
setMetadataValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import Metadata from 'typings/Metadata';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import { useManageMetadata } from '../useMetadata';
|
||||
import styles from './EditMetadataModalContent.css';
|
||||
|
||||
export interface EditMetadataModalContentProps {
|
||||
@@ -36,41 +30,39 @@ function EditMetadataModalContent({
|
||||
advancedSettings,
|
||||
onModalClose,
|
||||
}: EditMetadataModalContentProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { isSaving, saveError, pendingChanges, items } = useSelector(
|
||||
(state: AppState) => state.settings.metadata
|
||||
);
|
||||
const {
|
||||
item,
|
||||
updateValue,
|
||||
updateFieldValue,
|
||||
saveProvider,
|
||||
isSaving,
|
||||
saveError,
|
||||
...otherSettings
|
||||
} = useManageMetadata(id);
|
||||
|
||||
const wasSaving = usePrevious(isSaving);
|
||||
|
||||
const { settings, ...otherSettings } = useMemo(() => {
|
||||
const item = items.find((item) => item.id === id)!;
|
||||
|
||||
return selectSettings(item, pendingChanges, saveError);
|
||||
}, [id, items, pendingChanges, saveError]);
|
||||
|
||||
const { name, enable, fields, message } = settings;
|
||||
const { name, enable, fields, message } = item;
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
({ name, value }: InputChanged) => {
|
||||
// @ts-expect-error not typed
|
||||
dispatch(setMetadataValue({ name, value }));
|
||||
const key = name as keyof Metadata;
|
||||
|
||||
updateValue(key, value as Metadata[typeof key]);
|
||||
},
|
||||
[dispatch]
|
||||
[updateValue]
|
||||
);
|
||||
|
||||
const handleFieldChange = useCallback(
|
||||
({ name, value }: InputChanged) => {
|
||||
// @ts-expect-error not typed
|
||||
dispatch(setMetadataFieldValue({ name, value }));
|
||||
updateFieldValue?.({ [name]: value });
|
||||
},
|
||||
[dispatch]
|
||||
[updateFieldValue]
|
||||
);
|
||||
|
||||
const handleSavePress = useCallback(() => {
|
||||
dispatch(saveMetadata({ id }));
|
||||
}, [id, dispatch]);
|
||||
saveProvider();
|
||||
}, [saveProvider]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wasSaving && !isSaving && !saveError) {
|
||||
|
||||
@@ -1,43 +1,21 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import MetadataAppState from 'App/State/MetadataAppState';
|
||||
import React from 'react';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import { fetchMetadata } from 'Store/Actions/settingsActions';
|
||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||
import MetadataType from 'typings/Metadata';
|
||||
import sortByProp from 'Utilities/Array/sortByProp';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import { useSortedMetadata } from '../useMetadata';
|
||||
import Metadata from './Metadata';
|
||||
import styles from './Metadatas.css';
|
||||
|
||||
function createMetadatasSelector() {
|
||||
return createSelector(
|
||||
createSortedSectionSelector<MetadataType, MetadataAppState>(
|
||||
'settings.metadata',
|
||||
sortByProp('name')
|
||||
),
|
||||
(metadata: MetadataAppState) => metadata
|
||||
);
|
||||
}
|
||||
|
||||
function Metadatas() {
|
||||
const dispatch = useDispatch();
|
||||
const { isFetching, error, items, ...otherProps } = useSelector(
|
||||
createMetadatasSelector()
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchMetadata());
|
||||
}, [dispatch]);
|
||||
const { data: items, isFetching, isFetched, error } = useSortedMetadata();
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('Metadata')}>
|
||||
<PageSectionContent
|
||||
isFetching={isFetching}
|
||||
error={error}
|
||||
errorMessage={translate('MetadataLoadError')}
|
||||
{...otherProps}
|
||||
isFetching={isFetching}
|
||||
isPopulated={isFetched}
|
||||
>
|
||||
<div className={styles.metadatas}>
|
||||
{items.map((item) => {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
SelectedSchema,
|
||||
useProviderSchema,
|
||||
useSelectedSchema,
|
||||
} from 'Settings/useProviderSchema';
|
||||
import {
|
||||
useDeleteProvider,
|
||||
useManageProviderSettings,
|
||||
useProviderSettings,
|
||||
} from 'Settings/useProviderSettings';
|
||||
import Provider from 'typings/Provider';
|
||||
import { sortByProp } from 'Utilities/Array/sortByProp';
|
||||
|
||||
export interface MetadataModel extends Provider {
|
||||
enable: boolean;
|
||||
tags: number[];
|
||||
}
|
||||
|
||||
const PATH = '/metadata';
|
||||
|
||||
export const useMetadataWithIds = (ids: number[]) => {
|
||||
const allMetadata = useMetadataData();
|
||||
|
||||
return allMetadata.filter((metadata) => ids.includes(metadata.id));
|
||||
};
|
||||
|
||||
export const useMetadataItem = (id: number | undefined) => {
|
||||
const { data } = useMetadata();
|
||||
|
||||
if (id === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return data.find((metadata) => metadata.id === id);
|
||||
};
|
||||
|
||||
export const useMetadataData = () => {
|
||||
const { data } = useMetadata();
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useSortedMetadata = () => {
|
||||
const result = useMetadata();
|
||||
|
||||
const sortedData = useMemo(
|
||||
() => result.data.sort(sortByProp('name')),
|
||||
[result.data]
|
||||
);
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: sortedData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useMetadata = () => {
|
||||
return useProviderSettings<MetadataModel>({
|
||||
path: PATH,
|
||||
});
|
||||
};
|
||||
|
||||
export const useManageMetadata = (
|
||||
id: number | undefined,
|
||||
selectedSchema?: SelectedSchema
|
||||
) => {
|
||||
const schema = useSelectedSchema<MetadataModel>(PATH, selectedSchema);
|
||||
|
||||
if (selectedSchema && !schema) {
|
||||
throw new Error('A selected schema is required to manage metadata');
|
||||
}
|
||||
|
||||
const manage = useManageProviderSettings<MetadataModel>(
|
||||
id,
|
||||
selectedSchema && schema
|
||||
? {
|
||||
...schema,
|
||||
name: schema.implementationName || '',
|
||||
enable: true,
|
||||
}
|
||||
: ({} as MetadataModel),
|
||||
PATH
|
||||
);
|
||||
|
||||
return manage;
|
||||
};
|
||||
|
||||
export const useDeleteMetadata = (id: number) => {
|
||||
const result = useDeleteProvider<MetadataModel>(id, PATH);
|
||||
|
||||
return {
|
||||
...result,
|
||||
deleteMetadata: result.deleteProvider,
|
||||
};
|
||||
};
|
||||
|
||||
export const useMetadataSchema = (enabled: boolean = true) => {
|
||||
return useProviderSchema<MetadataModel>(PATH, enabled);
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
import { createAction } from 'redux-actions';
|
||||
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
|
||||
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
|
||||
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||
import { createThunk } from 'Store/thunks';
|
||||
|
||||
//
|
||||
// Variables
|
||||
|
||||
const section = 'settings.metadata';
|
||||
|
||||
//
|
||||
// Actions Types
|
||||
|
||||
export const FETCH_METADATA = 'settings/metadata/fetchMetadata';
|
||||
export const SET_METADATA_VALUE = 'settings/metadata/setMetadataValue';
|
||||
export const SET_METADATA_FIELD_VALUE = 'settings/metadata/setMetadataFieldValue';
|
||||
export const SAVE_METADATA = 'settings/metadata/saveMetadata';
|
||||
|
||||
//
|
||||
// Action Creators
|
||||
|
||||
export const fetchMetadata = createThunk(FETCH_METADATA);
|
||||
export const saveMetadata = createThunk(SAVE_METADATA);
|
||||
|
||||
export const setMetadataValue = createAction(SET_METADATA_VALUE, (payload) => {
|
||||
return {
|
||||
section,
|
||||
...payload
|
||||
};
|
||||
});
|
||||
|
||||
export const setMetadataFieldValue = createAction(SET_METADATA_FIELD_VALUE, (payload) => {
|
||||
return {
|
||||
section,
|
||||
...payload
|
||||
};
|
||||
});
|
||||
|
||||
//
|
||||
// Details
|
||||
|
||||
export default {
|
||||
|
||||
//
|
||||
// State
|
||||
|
||||
defaultState: {
|
||||
isFetching: false,
|
||||
isPopulated: false,
|
||||
error: null,
|
||||
isSaving: false,
|
||||
saveError: null,
|
||||
items: [],
|
||||
pendingChanges: {}
|
||||
},
|
||||
|
||||
//
|
||||
// Action Handlers
|
||||
|
||||
actionHandlers: {
|
||||
[FETCH_METADATA]: createFetchHandler(section, '/metadata'),
|
||||
[SAVE_METADATA]: createSaveProviderHandler(section, '/metadata')
|
||||
},
|
||||
|
||||
//
|
||||
// Reducers
|
||||
|
||||
reducers: {
|
||||
[SET_METADATA_VALUE]: createSetSettingValueReducer(section),
|
||||
[SET_METADATA_FIELD_VALUE]: createSetProviderFieldValueReducer(section)
|
||||
}
|
||||
|
||||
};
|
||||
@@ -16,7 +16,6 @@ import indexerOptions from './Settings/indexerOptions';
|
||||
import indexers from './Settings/indexers';
|
||||
import languages from './Settings/languages';
|
||||
import mediaManagement from './Settings/mediaManagement';
|
||||
import metadata from './Settings/metadata';
|
||||
|
||||
export * from './Settings/autoTaggingSpecifications';
|
||||
export * from './Settings/autoTaggings';
|
||||
@@ -34,7 +33,6 @@ export * from './Settings/indexerOptions';
|
||||
export * from './Settings/indexers';
|
||||
export * from './Settings/languages';
|
||||
export * from './Settings/mediaManagement';
|
||||
export * from './Settings/metadata';
|
||||
|
||||
//
|
||||
// Variables
|
||||
@@ -61,8 +59,7 @@ export const defaultState = {
|
||||
indexerOptions: indexerOptions.defaultState,
|
||||
indexers: indexers.defaultState,
|
||||
languages: languages.defaultState,
|
||||
mediaManagement: mediaManagement.defaultState,
|
||||
metadata: metadata.defaultState
|
||||
mediaManagement: mediaManagement.defaultState
|
||||
};
|
||||
|
||||
export const persistState = [
|
||||
@@ -88,8 +85,7 @@ export const actionHandlers = handleThunks({
|
||||
...indexerOptions.actionHandlers,
|
||||
...indexers.actionHandlers,
|
||||
...languages.actionHandlers,
|
||||
...mediaManagement.actionHandlers,
|
||||
...metadata.actionHandlers
|
||||
...mediaManagement.actionHandlers
|
||||
});
|
||||
|
||||
//
|
||||
@@ -111,7 +107,6 @@ export const reducers = createHandleActions({
|
||||
...indexerOptions.reducers,
|
||||
...indexers.reducers,
|
||||
...languages.reducers,
|
||||
...mediaManagement.reducers,
|
||||
...metadata.reducers
|
||||
...mediaManagement.reducers
|
||||
|
||||
}, defaultState, section);
|
||||
|
||||
Reference in New Issue
Block a user