mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-19 21:46:43 -04:00
Typings cleanup and improvements
This commit is contained in:
@@ -2,6 +2,8 @@ import { cloneDeep, without } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
import InteractiveImportAppState from 'App/State/InteractiveImportAppState';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import SelectInput from 'Components/Form/SelectInput';
|
||||
import Icon from 'Components/Icon';
|
||||
@@ -20,16 +22,24 @@ import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import Column from 'Components/Table/Column';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { EpisodeFile } from 'EpisodeFile/EpisodeFile';
|
||||
import usePrevious from 'Helpers/Hooks/usePrevious';
|
||||
import useSelectState from 'Helpers/Hooks/useSelectState';
|
||||
import { align, icons, kinds, scrollDirections } from 'Helpers/Props';
|
||||
import SelectEpisodeModal from 'InteractiveImport/Episode/SelectEpisodeModal';
|
||||
import { SelectedEpisode } from 'InteractiveImport/Episode/SelectEpisodeModalContent';
|
||||
import ImportMode from 'InteractiveImport/ImportMode';
|
||||
import InteractiveImport, {
|
||||
InteractiveImportCommandOptions,
|
||||
} from 'InteractiveImport/InteractiveImport';
|
||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
||||
import SelectSeriesModal from 'InteractiveImport/Series/SelectSeriesModal';
|
||||
import Language from 'Language/Language';
|
||||
import { QualityModel } from 'Quality/Quality';
|
||||
import Series from 'Series/Series';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import {
|
||||
deleteEpisodeFiles,
|
||||
@@ -44,6 +54,8 @@ import {
|
||||
updateInteractiveImportItems,
|
||||
} from 'Store/Actions/interactiveImportActions';
|
||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||
import { SortCallback } from 'typings/callbacks';
|
||||
import { SelectStateInputProps } from 'typings/props';
|
||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||
@@ -59,6 +71,13 @@ type SelectType =
|
||||
| 'quality'
|
||||
| 'language';
|
||||
|
||||
type FilterExistingFiles = 'all' | 'new';
|
||||
|
||||
// TODO: This feels janky to do, but not sure of a better way currently
|
||||
type OnSelectedChangeCallback = React.ComponentProps<
|
||||
typeof InteractiveImportRow
|
||||
>['onSelectedChange'];
|
||||
|
||||
const COLUMNS = [
|
||||
{
|
||||
name: 'relativePath',
|
||||
@@ -125,25 +144,23 @@ const COLUMNS = [
|
||||
},
|
||||
];
|
||||
|
||||
const filterExistingFilesOptions = {
|
||||
ALL: 'all',
|
||||
NEW: 'new',
|
||||
};
|
||||
|
||||
const importModeOptions = [
|
||||
{ key: 'chooseImportMode', value: 'Choose Import Mode', disabled: true },
|
||||
{ key: 'move', value: 'Move Files' },
|
||||
{ key: 'copy', value: 'Hardlink/Copy Files' },
|
||||
];
|
||||
|
||||
function isSameEpisodeFile(file, originalFile) {
|
||||
function isSameEpisodeFile(
|
||||
file: InteractiveImport,
|
||||
originalFile?: InteractiveImport
|
||||
) {
|
||||
const { series, seasonNumber, episodes } = file;
|
||||
|
||||
if (!originalFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!originalFile.series || series.id !== originalFile.series.id) {
|
||||
if (!originalFile.series || series?.id !== originalFile.series.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -155,8 +172,8 @@ function isSameEpisodeFile(file, originalFile) {
|
||||
}
|
||||
|
||||
const episodeFilesInfoSelector = createSelector(
|
||||
(state) => state.episodeFiles.isDeleting,
|
||||
(state) => state.episodeFiles.deleteError,
|
||||
(state: AppState) => state.episodeFiles.isDeleting,
|
||||
(state: AppState) => state.episodeFiles.deleteError,
|
||||
(isDeleting, deleteError) => {
|
||||
return {
|
||||
isDeleting,
|
||||
@@ -166,7 +183,7 @@ const episodeFilesInfoSelector = createSelector(
|
||||
);
|
||||
|
||||
const importModeSelector = createSelector(
|
||||
(state) => state.interactiveImport.importMode,
|
||||
(state: AppState) => state.interactiveImport.importMode,
|
||||
(importMode) => {
|
||||
return importMode;
|
||||
}
|
||||
@@ -178,7 +195,6 @@ interface InteractiveImportModalContentProps {
|
||||
seasonNumber?: number;
|
||||
showSeries?: boolean;
|
||||
allowSeriesChange?: boolean;
|
||||
autoSelectRow?: boolean;
|
||||
showDelete?: boolean;
|
||||
showImportMode?: boolean;
|
||||
showFilterExistingFiles?: boolean;
|
||||
@@ -200,7 +216,6 @@ function InteractiveImportModalContent(
|
||||
seriesId,
|
||||
seasonNumber,
|
||||
allowSeriesChange = true,
|
||||
autoSelectRow = true,
|
||||
showSeries = true,
|
||||
showFilterExistingFiles = false,
|
||||
showDelete = false,
|
||||
@@ -221,16 +236,18 @@ function InteractiveImportModalContent(
|
||||
originalItems,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
} = useSelector(createClientSideCollectionSelector('interactiveImport'));
|
||||
}: InteractiveImportAppState = useSelector(
|
||||
createClientSideCollectionSelector('interactiveImport')
|
||||
);
|
||||
|
||||
const { isDeleting, deleteError } = useSelector(episodeFilesInfoSelector);
|
||||
const importMode = useSelector(importModeSelector);
|
||||
|
||||
const [invalidRowsSelected, setInvalidRowsSelected] = useState([]);
|
||||
const [invalidRowsSelected, setInvalidRowsSelected] = useState<number[]>([]);
|
||||
const [
|
||||
withoutEpisodeFileIdRowsSelected,
|
||||
setWithoutEpisodeFileIdRowsSelected,
|
||||
] = useState([]);
|
||||
] = useState<number[]>([]);
|
||||
const [selectModalOpen, setSelectModalOpen] = useState<SelectType | null>(
|
||||
null
|
||||
);
|
||||
@@ -253,16 +270,20 @@ function InteractiveImportModalContent(
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const columns: Column[] = useMemo(() => {
|
||||
const result = cloneDeep(COLUMNS);
|
||||
const result: Column[] = cloneDeep(COLUMNS);
|
||||
|
||||
if (!showSeries) {
|
||||
result.find((c) => c.name === 'series').isVisible = false;
|
||||
const seriesColumn = result.find((c) => c.name === 'series');
|
||||
|
||||
if (seriesColumn) {
|
||||
seriesColumn.isVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [showSeries]);
|
||||
|
||||
const selectedIds = useMemo(() => {
|
||||
const selectedIds: number[] = useMemo(() => {
|
||||
return getSelectedIds(selectedState);
|
||||
}, [selectedState]);
|
||||
|
||||
@@ -317,13 +338,13 @@ function InteractiveImportModalContent(
|
||||
}, [previousIsDeleting, isDeleting, deleteError, onModalClose]);
|
||||
|
||||
const onSelectAllChange = useCallback(
|
||||
({ value }) => {
|
||||
({ value }: SelectStateInputProps) => {
|
||||
setSelectState({ type: value ? 'selectAll' : 'unselectAll', items });
|
||||
},
|
||||
[items, setSelectState]
|
||||
);
|
||||
|
||||
const onSelectedChange = useCallback(
|
||||
const onSelectedChange = useCallback<OnSelectedChangeCallback>(
|
||||
({ id, value, hasEpisodeFileId, shiftKey = false }) => {
|
||||
setSelectState({
|
||||
type: 'toggleSelected',
|
||||
@@ -365,7 +386,7 @@ function InteractiveImportModalContent(
|
||||
const onConfirmDelete = useCallback(() => {
|
||||
setIsConfirmDeleteModalOpen(false);
|
||||
|
||||
const episodeFileIds = items.reduce((acc, item) => {
|
||||
const episodeFileIds = items.reduce((acc: number[], item) => {
|
||||
if (selectedIds.indexOf(item.id) > -1 && item.episodeFileId) {
|
||||
acc.push(item.episodeFileId);
|
||||
}
|
||||
@@ -381,11 +402,10 @@ function InteractiveImportModalContent(
|
||||
}, [setIsConfirmDeleteModalOpen]);
|
||||
|
||||
const onImportSelectedPress = useCallback(() => {
|
||||
const finalImportMode =
|
||||
downloadId || !showImportMode ? ImportMode.Auto : importMode;
|
||||
const finalImportMode = downloadId || !showImportMode ? 'auto' : importMode;
|
||||
|
||||
const existingFiles = [];
|
||||
const files = [];
|
||||
const existingFiles: Partial<EpisodeFile>[] = [];
|
||||
const files: InteractiveImportCommandOptions[] = [];
|
||||
|
||||
if (finalImportMode === 'chooseImportMode') {
|
||||
setInteractiveImportErrorMessage('An import mode must be selected');
|
||||
@@ -511,16 +531,18 @@ function InteractiveImportModalContent(
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
const onSortPress = useCallback(
|
||||
const onSortPress = useCallback<SortCallback>(
|
||||
(sortKey, sortDirection) => {
|
||||
dispatch(setInteractiveImportSort({ sortKey, sortDirection }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const onFilterExistingFilesChange = useCallback(
|
||||
const onFilterExistingFilesChange = useCallback<
|
||||
(value: FilterExistingFiles) => void
|
||||
>(
|
||||
(value) => {
|
||||
const filter = value !== filterExistingFilesOptions.ALL;
|
||||
const filter = value !== 'all';
|
||||
|
||||
setFilterExistingFiles(filter);
|
||||
|
||||
@@ -536,14 +558,18 @@ function InteractiveImportModalContent(
|
||||
[downloadId, seriesId, folder, setFilterExistingFiles, dispatch]
|
||||
);
|
||||
|
||||
const onImportModeChange = useCallback(
|
||||
const onImportModeChange = useCallback<
|
||||
({ value }: { value: ImportMode }) => void
|
||||
>(
|
||||
({ value }) => {
|
||||
dispatch(setInteractiveImportMode({ importMode: value }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const onSelectModalSelect = useCallback(
|
||||
const onSelectModalSelect = useCallback<
|
||||
({ value }: { value: SelectType }) => void
|
||||
>(
|
||||
({ value }) => {
|
||||
setSelectModalOpen(value);
|
||||
},
|
||||
@@ -555,7 +581,7 @@ function InteractiveImportModalContent(
|
||||
}, [setSelectModalOpen]);
|
||||
|
||||
const onSeriesSelect = useCallback(
|
||||
(series) => {
|
||||
(series: Series) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
@@ -573,7 +599,7 @@ function InteractiveImportModalContent(
|
||||
);
|
||||
|
||||
const onSeasonSelect = useCallback(
|
||||
(seasonNumber) => {
|
||||
(seasonNumber: number) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
@@ -590,7 +616,7 @@ function InteractiveImportModalContent(
|
||||
);
|
||||
|
||||
const onEpisodesSelect = useCallback(
|
||||
(episodes) => {
|
||||
(episodes: SelectedEpisode[]) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
@@ -606,7 +632,7 @@ function InteractiveImportModalContent(
|
||||
);
|
||||
|
||||
const onReleaseGroupSelect = useCallback(
|
||||
(releaseGroup) => {
|
||||
(releaseGroup: string) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
@@ -622,7 +648,7 @@ function InteractiveImportModalContent(
|
||||
);
|
||||
|
||||
const onLanguagesSelect = useCallback(
|
||||
(newLanguages) => {
|
||||
(newLanguages: Language[]) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
@@ -638,7 +664,7 @@ function InteractiveImportModalContent(
|
||||
);
|
||||
|
||||
const onQualitySelect = useCallback(
|
||||
(quality) => {
|
||||
(quality: QualityModel) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
@@ -653,7 +679,7 @@ function InteractiveImportModalContent(
|
||||
[selectedIds, dispatch]
|
||||
);
|
||||
|
||||
const orderedSelectedIds = items.reduce((acc, file) => {
|
||||
const orderedSelectedIds = items.reduce((acc: number[], file) => {
|
||||
if (selectedIds.includes(file.id)) {
|
||||
acc.push(file.id);
|
||||
}
|
||||
@@ -690,7 +716,7 @@ function InteractiveImportModalContent(
|
||||
|
||||
<MenuContent>
|
||||
<SelectedMenuItem
|
||||
name={filterExistingFilesOptions.ALL}
|
||||
name={'all'}
|
||||
isSelected={!filterExistingFiles}
|
||||
onPress={onFilterExistingFilesChange}
|
||||
>
|
||||
@@ -698,7 +724,7 @@ function InteractiveImportModalContent(
|
||||
</SelectedMenuItem>
|
||||
|
||||
<SelectedMenuItem
|
||||
name={filterExistingFilesOptions.NEW}
|
||||
name={'new'}
|
||||
isSelected={filterExistingFiles}
|
||||
onPress={onFilterExistingFilesChange}
|
||||
>
|
||||
@@ -733,7 +759,6 @@ function InteractiveImportModalContent(
|
||||
isSelected={selectedState[item.id]}
|
||||
{...item}
|
||||
allowSeriesChange={allowSeriesChange}
|
||||
autoSelectRow={autoSelectRow}
|
||||
columns={columns}
|
||||
modalTitle={modalTitle}
|
||||
onSelectedChange={onSelectedChange}
|
||||
|
||||
Reference in New Issue
Block a user