1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-18 21:35:27 -04:00
Files
Sonarr/frontend/src/Series/Index/Select/SeriesIndexSelectFooter.tsx
T
2026-03-16 16:54:31 -07:00

264 lines
7.8 KiB
TypeScript

import React, { useCallback, useEffect, useState } from 'react';
import { useSelect } from 'App/Select/SelectContext';
import CommandNames from 'Commands/CommandNames';
import { useCommandExecuting } from 'Commands/useCommands';
import SpinnerButton from 'Components/Link/SpinnerButton';
import PageContentFooter from 'Components/Page/PageContentFooter';
import usePrevious from 'Helpers/Hooks/usePrevious';
import { kinds } from 'Helpers/Props';
import Series from 'Series/Series';
import {
useBulkDeleteSeries,
useSaveSeriesEditor,
useUpdateSeriesMonitor,
} from 'Series/useSeries';
import translate from 'Utilities/String/translate';
import DeleteSeriesModal from './Delete/DeleteSeriesModal';
import DeleteSeriesFilesModal from './Delete/Files/DeleteSeriesFilesModal';
import EditSeriesModal from './Edit/EditSeriesModal';
import OrganizeSeriesModal from './Organize/OrganizeSeriesModal';
import ChangeMonitoringModal from './SeasonPass/ChangeMonitoringModal';
import TagsModal from './Tags/TagsModal';
import styles from './SeriesIndexSelectFooter.css';
interface SavePayload {
monitored?: boolean;
qualityProfileId?: number;
seriesType?: string;
seasonFolder?: boolean;
rootFolderPath?: string;
moveFiles?: boolean;
}
function SeriesIndexSelectFooter() {
const { saveSeriesEditor, isSavingSeriesEditor } = useSaveSeriesEditor();
const { updateSeriesMonitor, isUpdatingSeriesMonitor } =
useUpdateSeriesMonitor();
const { isBulkDeleting, bulkDeleteError } = useBulkDeleteSeries();
const isDeleteFilesCommandExecuting = useCommandExecuting(
CommandNames.DeleteSeriesFiles
);
const isOrganizingSeries = useCommandExecuting(CommandNames.RenameSeries);
const isSaving = isSavingSeriesEditor || isUpdatingSeriesMonitor;
const isDeleting = isBulkDeleting;
const deleteError = bulkDeleteError;
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [isOrganizeModalOpen, setIsOrganizeModalOpen] = useState(false);
const [isTagsModalOpen, setIsTagsModalOpen] = useState(false);
const [isMonitoringModalOpen, setIsMonitoringModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isDeleteFilesModalOpen, setIsDeleteFilesModalOpen] = useState(false);
const [isSavingSeries, setIsSavingSeries] = useState(false);
const [isSavingTags, setIsSavingTags] = useState(false);
const [isSavingMonitoring, setIsSavingMonitoring] = useState(false);
const previousIsDeleting = usePrevious(isDeleting);
const { selectedCount, unselectAll, useSelectedIds } = useSelect<Series>();
const seriesIds = useSelectedIds();
const onEditPress = useCallback(() => {
setIsEditModalOpen(true);
}, [setIsEditModalOpen]);
const onEditModalClose = useCallback(() => {
setIsEditModalOpen(false);
}, [setIsEditModalOpen]);
const onSavePress = useCallback(
(payload: SavePayload) => {
setIsSavingSeries(true);
setIsEditModalOpen(false);
saveSeriesEditor({
...payload,
seriesIds,
});
},
[seriesIds, saveSeriesEditor]
);
const onOrganizePress = useCallback(() => {
setIsOrganizeModalOpen(true);
}, [setIsOrganizeModalOpen]);
const onOrganizeModalClose = useCallback(() => {
setIsOrganizeModalOpen(false);
}, [setIsOrganizeModalOpen]);
const onTagsPress = useCallback(() => {
setIsTagsModalOpen(true);
}, [setIsTagsModalOpen]);
const onTagsModalClose = useCallback(() => {
setIsTagsModalOpen(false);
}, [setIsTagsModalOpen]);
const onApplyTagsPress = useCallback(
(tags: number[], _applyTags: string) => {
setIsSavingTags(true);
setIsTagsModalOpen(false);
saveSeriesEditor({
seriesIds,
tags,
});
},
[seriesIds, saveSeriesEditor]
);
const onMonitoringPress = useCallback(() => {
setIsMonitoringModalOpen(true);
}, [setIsMonitoringModalOpen]);
const onMonitoringClose = useCallback(() => {
setIsMonitoringModalOpen(false);
}, [setIsMonitoringModalOpen]);
const onMonitoringSavePress = useCallback(
(monitor: string) => {
setIsSavingMonitoring(true);
setIsMonitoringModalOpen(false);
updateSeriesMonitor({
series: seriesIds.map((id) => ({ id })),
monitoringOptions: { monitor },
});
},
[seriesIds, updateSeriesMonitor]
);
const onDeletePress = useCallback(() => {
setIsDeleteModalOpen(true);
}, [setIsDeleteModalOpen]);
const onDeleteModalClose = useCallback(() => {
setIsDeleteModalOpen(false);
}, []);
const onDeleteFilesPress = useCallback(() => {
setIsDeleteFilesModalOpen(true);
}, []);
const onDeleteFilesModalClose = useCallback(() => {
setIsDeleteFilesModalOpen(false);
}, []);
useEffect(() => {
if (!isSaving) {
setIsSavingSeries(false);
setIsSavingTags(false);
setIsSavingMonitoring(false);
}
}, [isSaving]);
useEffect(() => {
if (previousIsDeleting && !isDeleting && !deleteError) {
unselectAll();
}
}, [previousIsDeleting, isDeleting, deleteError, unselectAll]);
const anySelected = selectedCount > 0;
return (
<PageContentFooter className={styles.footer}>
<div className={styles.buttons}>
<div className={styles.actionButtons}>
<SpinnerButton
isSpinning={isSaving && isSavingSeries}
isDisabled={!anySelected || isOrganizingSeries}
onPress={onEditPress}
>
{translate('Edit')}
</SpinnerButton>
<SpinnerButton
kind={kinds.WARNING}
isSpinning={isOrganizingSeries}
isDisabled={!anySelected || isOrganizingSeries}
onPress={onOrganizePress}
>
{translate('RenameFiles')}
</SpinnerButton>
<SpinnerButton
isSpinning={isSaving && isSavingTags}
isDisabled={!anySelected || isOrganizingSeries}
onPress={onTagsPress}
>
{translate('SetTags')}
</SpinnerButton>
<SpinnerButton
isSpinning={isSaving && isSavingMonitoring}
isDisabled={!anySelected || isOrganizingSeries}
onPress={onMonitoringPress}
>
{translate('UpdateMonitoring')}
</SpinnerButton>
</div>
<div className={styles.deleteButtons}>
<SpinnerButton
kind={kinds.DANGER}
isSpinning={isDeleting}
isDisabled={!anySelected || isDeleting}
onPress={onDeletePress}
>
{translate('Delete')}
</SpinnerButton>
<SpinnerButton
kind={kinds.DANGER}
isSpinning={isDeleteFilesCommandExecuting}
isDisabled={!anySelected || isDeleteFilesCommandExecuting}
onPress={onDeleteFilesPress}
>
{translate('DeleteFiles')}
</SpinnerButton>
</div>
</div>
<div className={styles.selected}>
{translate('CountSeriesSelected', { count: selectedCount })}
</div>
<EditSeriesModal
isOpen={isEditModalOpen}
onSavePress={onSavePress}
onModalClose={onEditModalClose}
/>
<TagsModal
isOpen={isTagsModalOpen}
onApplyTagsPress={onApplyTagsPress}
onModalClose={onTagsModalClose}
/>
<ChangeMonitoringModal
isOpen={isMonitoringModalOpen}
onSavePress={onMonitoringSavePress}
onModalClose={onMonitoringClose}
/>
<OrganizeSeriesModal
isOpen={isOrganizeModalOpen}
onModalClose={onOrganizeModalClose}
/>
<DeleteSeriesModal
isOpen={isDeleteModalOpen}
onModalClose={onDeleteModalClose}
/>
<DeleteSeriesFilesModal
isOpen={isDeleteFilesModalOpen}
onModalClose={onDeleteFilesModalClose}
/>
</PageContentFooter>
);
}
export default SeriesIndexSelectFooter;