1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-23 22:25:56 -04:00

Use react-query for series

This commit is contained in:
Mark McDowall
2025-11-28 19:37:17 -08:00
parent 49db4a1d76
commit 0521a6c390
91 changed files with 1961 additions and 2173 deletions
@@ -1,9 +1,6 @@
import { orderBy } from 'lodash';
import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import { useSelect } from 'App/Select/SelectContext';
import AppState from 'App/State/AppState';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
@@ -14,8 +11,11 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import Series from 'Series/Series';
import { bulkDeleteSeries, setDeleteOption } from 'Store/Actions/seriesActions';
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
import {
setSeriesDeleteOptions,
useSeriesDeleteOptions,
} from 'Series/seriesOptionsStore';
import useSeries, { useBulkDeleteSeries } from 'Series/useSeries';
import { InputChanged } from 'typings/inputs';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
@@ -25,17 +25,12 @@ export interface DeleteSeriesModalContentProps {
onModalClose(): void;
}
const selectDeleteOptions = createSelector(
(state: AppState) => state.series.deleteOptions,
(deleteOptions) => deleteOptions
);
function DeleteSeriesModalContent({
onModalClose,
}: DeleteSeriesModalContentProps) {
const { addImportListExclusion } = useSelector(selectDeleteOptions);
const allSeries: Series[] = useSelector(createAllSeriesSelector());
const dispatch = useDispatch();
const { addImportListExclusion } = useSeriesDeleteOptions();
const { data: allSeries } = useSeries();
const { bulkDeleteSeries } = useBulkDeleteSeries();
const [deleteFiles, setDeleteFiles] = useState(false);
const { useSelectedIds } = useSelect<Series>();
const seriesIds = useSelectedIds();
@@ -57,25 +52,21 @@ function DeleteSeriesModalContent({
const onDeleteOptionChange = useCallback(
({ name, value }: { name: string; value: boolean }) => {
dispatch(
setDeleteOption({
[name]: value,
})
);
setSeriesDeleteOptions({
[name]: value,
});
},
[dispatch]
[]
);
const onDeleteSeriesConfirmed = useCallback(() => {
setDeleteFiles(false);
dispatch(
bulkDeleteSeries({
seriesIds,
deleteFiles,
addImportListExclusion,
})
);
bulkDeleteSeries({
seriesIds,
deleteFiles,
addImportListExclusion,
});
onModalClose();
}, [
@@ -83,7 +74,7 @@ function DeleteSeriesModalContent({
addImportListExclusion,
setDeleteFiles,
seriesIds,
dispatch,
bulkDeleteSeries,
onModalClose,
]);
@@ -1,6 +1,6 @@
import { orderBy } from 'lodash';
import React, { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { useSelect } from 'App/Select/SelectContext';
import { RENAME_SERIES } from 'Commands/commandNames';
import Alert from 'Components/Alert';
@@ -12,8 +12,8 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { icons, kinds } from 'Helpers/Props';
import Series from 'Series/Series';
import useSeries from 'Series/useSeries';
import { executeCommand } from 'Store/Actions/commandActions';
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
import translate from 'Utilities/String/translate';
import styles from './OrganizeSeriesModalContent.css';
@@ -24,7 +24,7 @@ export interface OrganizeSeriesModalContentProps {
function OrganizeSeriesModalContent({
onModalClose,
}: OrganizeSeriesModalContentProps) {
const allSeries: Series[] = useSelector(createAllSeriesSelector());
const { data: allSeries } = useSeries();
const dispatch = useDispatch();
const { useSelectedIds } = useSelect<Series>();
const seriesIds = useSelectedIds();
@@ -19,12 +19,7 @@ function SeasonDetails(props: SeasonDetailsProps) {
return (
<div className={styles.seasons}>
{latestSeasons.map((season) => {
const {
seasonNumber,
monitored,
statistics,
isSaving = false,
} = season;
const { seasonNumber, monitored, statistics } = season;
return (
<SeasonPassSeason
@@ -33,7 +28,6 @@ function SeasonDetails(props: SeasonDetailsProps) {
seasonNumber={seasonNumber}
monitored={monitored}
statistics={statistics}
isSaving={isSaving}
/>
);
})}
@@ -1,10 +1,9 @@
import classNames from 'classnames';
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import MonitorToggleButton from 'Components/MonitorToggleButton';
import formatSeason from 'Season/formatSeason';
import { Statistics } from 'Series/Series';
import { toggleSeasonMonitored } from 'Store/Actions/seriesActions';
import { useToggleSeasonMonitored } from 'Series/useSeries';
import translate from 'Utilities/String/translate';
import styles from './SeasonPassSeason.css';
@@ -13,7 +12,6 @@ interface SeasonPassSeasonProps {
seasonNumber: number;
monitored: boolean;
statistics: Statistics;
isSaving: boolean;
}
function SeasonPassSeason(props: SeasonPassSeasonProps) {
@@ -26,24 +24,22 @@ function SeasonPassSeason(props: SeasonPassSeasonProps) {
totalEpisodeCount: 0,
percentOfEpisodes: 0,
},
isSaving = false,
} = props;
const { episodeFileCount, totalEpisodeCount, percentOfEpisodes } = statistics;
const dispatch = useDispatch();
const { toggleSeasonMonitored, isTogglingSeasonMonitored } =
useToggleSeasonMonitored(seriesId);
const onSeasonMonitoredPress = useCallback(() => {
dispatch(
toggleSeasonMonitored({ seriesId, seasonNumber, monitored: !monitored })
);
}, [seriesId, seasonNumber, monitored, dispatch]);
toggleSeasonMonitored({ seasonNumber, monitored: !monitored });
}, [seasonNumber, monitored, toggleSeasonMonitored]);
return (
<div className={styles.season}>
<div className={styles.info}>
<MonitorToggleButton
monitored={monitored}
isSaving={isSaving}
isSaving={isTogglingSeasonMonitored}
onPress={onSeasonMonitoredPress}
/>
@@ -1,8 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import { useSelector } from 'react-redux';
import { useSelect } from 'App/Select/SelectContext';
import AppState from 'App/State/AppState';
import { RENAME_SERIES } from 'Commands/commandNames';
import SpinnerButton from 'Components/Link/SpinnerButton';
import PageContentFooter from 'Components/Page/PageContentFooter';
@@ -10,9 +8,10 @@ import usePrevious from 'Helpers/Hooks/usePrevious';
import { kinds } from 'Helpers/Props';
import Series from 'Series/Series';
import {
saveSeriesEditor,
updateSeriesMonitor,
} from 'Store/Actions/seriesActions';
useBulkDeleteSeries,
useSaveSeriesEditor,
useUpdateSeriesMonitor,
} from 'Series/useSeries';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import translate from 'Utilities/String/translate';
import DeleteSeriesModal from './Delete/DeleteSeriesModal';
@@ -31,28 +30,19 @@ interface SavePayload {
moveFiles?: boolean;
}
const seriesEditorSelector = createSelector(
(state: AppState) => state.series,
(series) => {
const { isSaving, isDeleting, deleteError } = series;
return {
isSaving,
isDeleting,
deleteError,
};
}
);
function SeriesIndexSelectFooter() {
const { isSaving, isDeleting, deleteError } =
useSelector(seriesEditorSelector);
const { saveSeriesEditor, isSavingSeriesEditor } = useSaveSeriesEditor();
const { updateSeriesMonitor, isUpdatingSeriesMonitor } =
useUpdateSeriesMonitor();
const { isBulkDeleting, bulkDeleteError } = useBulkDeleteSeries();
const isOrganizingSeries = useSelector(
createCommandExecutingSelector(RENAME_SERIES)
);
const dispatch = useDispatch();
const isSaving = isSavingSeriesEditor || isUpdatingSeriesMonitor;
const isDeleting = isBulkDeleting;
const deleteError = bulkDeleteError;
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isOrganizeModalOpen, setIsOrganizeModalOpen] = useState(false);
@@ -79,14 +69,12 @@ function SeriesIndexSelectFooter() {
setIsSavingSeries(true);
setIsEditModalOpen(false);
dispatch(
saveSeriesEditor({
...payload,
seriesIds,
})
);
saveSeriesEditor({
...payload,
seriesIds,
});
},
[seriesIds, dispatch]
[seriesIds, saveSeriesEditor]
);
const onOrganizePress = useCallback(() => {
@@ -106,19 +94,16 @@ function SeriesIndexSelectFooter() {
}, [setIsTagsModalOpen]);
const onApplyTagsPress = useCallback(
(tags: number[], applyTags: string) => {
(tags: number[], _applyTags: string) => {
setIsSavingTags(true);
setIsTagsModalOpen(false);
dispatch(
saveSeriesEditor({
seriesIds,
tags,
applyTags,
})
);
saveSeriesEditor({
seriesIds,
tags,
});
},
[seriesIds, dispatch]
[seriesIds, saveSeriesEditor]
);
const onMonitoringPress = useCallback(() => {
@@ -134,14 +119,12 @@ function SeriesIndexSelectFooter() {
setIsSavingMonitoring(true);
setIsMonitoringModalOpen(false);
dispatch(
updateSeriesMonitor({
seriesIds,
monitor,
})
);
updateSeriesMonitor({
series: seriesIds.map((id) => ({ id })),
monitoringOptions: { monitor },
});
},
[seriesIds, dispatch]
[seriesIds, updateSeriesMonitor]
);
const onDeletePress = useCallback(() => {
@@ -1,6 +1,5 @@
import { uniq } from 'lodash';
import React, { useCallback, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { useSelect } from 'App/Select/SelectContext';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
@@ -15,7 +14,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds, sizes } from 'Helpers/Props';
import Series from 'Series/Series';
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
import { useMultipleSeries } from 'Series/useSeries';
import { Tag, useTagList } from 'Tags/useTags';
import translate from 'Utilities/String/translate';
import styles from './TagsModalContent.css';
@@ -29,27 +28,24 @@ function TagsModalContent({
onModalClose,
onApplyTagsPress,
}: TagsModalContentProps) {
const allSeries: Series[] = useSelector(createAllSeriesSelector());
const tagList: Tag[] = useTagList();
const [tags, setTags] = useState<number[]>([]);
const [applyTags, setApplyTags] = useState('add');
const { useSelectedIds } = useSelect<Series>();
const seriesIds = useSelectedIds();
const selectedSeries = useMultipleSeries(seriesIds);
const seriesTags = useMemo(() => {
const tags = seriesIds.reduce((acc: number[], id) => {
const s = allSeries.find((s) => s.id === id);
if (s) {
acc.push(...s.tags);
const tags = selectedSeries.reduce((acc: number[], series) => {
if (series) {
acc.push(...series.tags);
}
return acc;
}, []);
return uniq(tags);
}, [allSeries, seriesIds]);
}, [selectedSeries]);
const onTagsChange = useCallback(
({ value }: { value: number[] }) => {