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

Typings cleanup and improvements

This commit is contained in:
Mark McDowall
2023-04-04 09:21:34 -07:00
parent 5326a102e2
commit b2c43fb2a6
92 changed files with 1019 additions and 346 deletions
@@ -7,8 +7,8 @@ import SelectEpisodeModalContent, {
interface SelectEpisodeModalProps {
isOpen: boolean;
selectedIds: number[] | string[];
seriesId: number;
seasonNumber: number;
seriesId?: number;
seasonNumber?: number;
selectedDetails?: string;
isAnime: boolean;
modalTitle: string;
@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import EpisodesAppState from 'App/State/EpisodesAppState';
import TextInput from 'Components/Form/TextInput';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
@@ -14,12 +15,15 @@ import TableBody from 'Components/Table/TableBody';
import Episode from 'Episode/Episode';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds, scrollDirections } from 'Helpers/Props';
import SortDirection from 'Helpers/Props/SortDirection';
import {
clearEpisodes,
fetchEpisodes,
setEpisodesSort,
} from 'Store/Actions/episodeSelectionActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import { CheckInputChanged } from 'typings/inputs';
import { SelectStateInputProps } from 'typings/props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import SelectEpisodeRow from './SelectEpisodeRow';
@@ -47,7 +51,7 @@ const columns = [
function episodesSelector() {
return createSelector(
createClientSideCollectionSelector('episodeSelection'),
(episodes) => {
(episodes: EpisodesAppState) => {
return episodes;
}
);
@@ -60,8 +64,8 @@ export interface SelectedEpisode {
interface SelectEpisodeModalContentProps {
selectedIds: number[] | string[];
seriesId: number;
seasonNumber: number;
seriesId?: number;
seasonNumber?: number;
selectedDetails?: string;
isAnime: boolean;
sortKey?: string;
@@ -100,26 +104,26 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
const filterEpisodeNumber = parseInt(filter);
const errorMessage = getErrorMessage(error, 'Unable to load episodes');
const selectedCount = selectedIds.length;
const selectedEpisodesCount = getSelectedIds(selectState).length;
const selectedEpisodesCount = getSelectedIds(selectedState).length;
const selectionIsValid =
selectedEpisodesCount > 0 && selectedEpisodesCount % selectedCount === 0;
const onFilterChange = useCallback(
({ value }) => {
({ value }: { value: string }) => {
setFilter(value.toLowerCase());
},
[setFilter]
);
const onSelectAllChange = useCallback(
({ value }) => {
({ value }: CheckInputChanged) => {
setSelectState({ type: value ? 'selectAll' : 'unselectAll', items });
},
[items, setSelectState]
);
const onSelectedChange = useCallback(
({ id, value, shiftKey = false }) => {
({ id, value, shiftKey = false }: SelectStateInputProps) => {
setSelectState({
type: 'toggleSelected',
items,
@@ -132,7 +136,7 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
);
const onSortPress = useCallback(
(newSortKey, newSortDirection) => {
(newSortKey: string, newSortDirection: SortDirection) => {
dispatch(
setEpisodesSort({
sortKey: newSortKey,
@@ -144,9 +148,9 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
);
const onEpisodesSelectWrapper = useCallback(() => {
const episodeIds = getSelectedIds(selectedState);
const episodeIds: number[] = getSelectedIds(selectedState);
const selectedEpisodes = items.reduce((acc, item) => {
const selectedEpisodes = items.reduce((acc: Episode[], item) => {
if (episodeIds.indexOf(item.id) > -1) {
acc.push(item);
}
@@ -167,7 +171,7 @@ function SelectEpisodeModalContent(props: SelectEpisodeModalContentProps) {
);
return {
fileId,
fileId: fileId as number,
episodes,
};
});
@@ -1,6 +1,7 @@
import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import * as commandNames from 'Commands/commandNames';
import PathInputConnector from 'Components/Form/PathInputConnector';
import Icon from 'Components/Icon';
@@ -18,7 +19,6 @@ import {
removeRecentFolder,
} from 'Store/Actions/interactiveImportActions';
import translate from 'Utilities/String/translate';
import RecentFolder from './RecentFolder';
import RecentFolderRow from './RecentFolderRow';
import styles from './InteractiveImportSelectFolderModalContent.css';
@@ -49,9 +49,9 @@ function InteractiveImportSelectFolderModalContent(
const { modalTitle, onFolderSelect, onModalClose } = props;
const [folder, setFolder] = useState('');
const dispatch = useDispatch();
const recentFolders: RecentFolder[] = useSelector(
const recentFolders = useSelector(
createSelector(
(state) => state.interactiveImport.recentFolders,
(state: AppState) => state.interactiveImport.recentFolders,
(recentFolders) => {
return recentFolders;
}
@@ -59,14 +59,14 @@ function InteractiveImportSelectFolderModalContent(
);
const onPathChange = useCallback(
({ value }) => {
({ value }: { value: string }) => {
setFolder(value);
},
[setFolder]
);
const onRecentPathPress = useCallback(
(value) => {
(value: string) => {
setFolder(value);
},
[setFolder]
@@ -91,8 +91,8 @@ function InteractiveImportSelectFolderModalContent(
}, [folder, onFolderSelect, dispatch]);
const onRemoveRecentFolderPress = useCallback(
(f) => {
dispatch(removeRecentFolder({ folder: f }));
(folderToRemove: string) => {
dispatch(removeRecentFolder({ folder: folderToRemove }));
},
[dispatch]
);
+1 -5
View File
@@ -1,7 +1,3 @@
enum ImportMode {
Auto = 'auto',
Move = 'move',
Copy = 'copy',
}
type ImportMode = 'auto' | 'move' | 'copy' | 'chooseImportMode';
export default ImportMode;
@@ -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}
@@ -27,6 +27,7 @@ import {
reprocessInteractiveImportItems,
updateInteractiveImportItem,
} from 'Store/Actions/interactiveImportActions';
import { SelectStateInputProps } from 'typings/props';
import Rejection from 'typings/Rejection';
import formatBytes from 'Utilities/Number/formatBytes';
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
@@ -40,6 +41,10 @@ type SelectType =
| 'quality'
| 'language';
type SelectedChangeProps = SelectStateInputProps & {
hasEpisodeFileId: boolean;
};
interface InteractiveImportRowProps {
id: number;
allowSeriesChange: boolean;
@@ -58,7 +63,7 @@ interface InteractiveImportRowProps {
isReprocessing?: boolean;
isSelected?: boolean;
modalTitle: string;
onSelectedChange(...args: unknown[]): void;
onSelectedChange(result: SelectedChangeProps): void;
onValidRowChange(id: number, isValid: boolean): void;
}
@@ -88,7 +93,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
const dispatch = useDispatch();
const isSeriesColumnVisible = useMemo(
() => columns.find((c) => c.name === 'series').isVisible,
() => columns.find((c) => c.name === 'series')?.isVisible ?? false,
[columns]
);
@@ -110,6 +115,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
id,
hasEpisodeFileId: !!episodeFileId,
value: true,
shiftKey: false,
});
}
},
@@ -143,7 +149,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
]);
const onSelectedChangeWrapper = useCallback(
(result) => {
(result: SelectedChangeProps) => {
onSelectedChange({
...result,
hasEpisodeFileId: !!episodeFileId,
@@ -158,6 +164,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
id,
hasEpisodeFileId: !!episodeFileId,
value: true,
shiftKey: false,
});
}
}, [id, episodeFileId, isSelected, onSelectedChange]);
@@ -312,9 +319,10 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
);
});
const requiresSeasonNumber = isNaN(Number(seasonNumber));
const showSeriesPlaceholder = isSelected && !series;
const showSeasonNumberPlaceholder =
isSelected && !!series && isNaN(seasonNumber) && !isReprocessing;
isSelected && !!series && requiresSeasonNumber && !isReprocessing;
const showEpisodeNumbersPlaceholder =
isSelected && Number.isInteger(seasonNumber) && !episodes.length;
const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
@@ -364,9 +372,11 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
</TableRowCellButton>
<TableRowCellButton
isDisabled={!series || isNaN(seasonNumber)}
isDisabled={!series || requiresSeasonNumber}
title={
series && !isNaN(seasonNumber) ? 'Click to change episode' : undefined
series && !requiresSeasonNumber
? 'Click to change episode'
: undefined
}
onPress={onSelectEpisodePress}
>
@@ -456,7 +466,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
<SelectSeasonModal
isOpen={selectModalOpen === 'season'}
seriesId={series && series.id}
seriesId={series?.id}
modalTitle={modalTitle}
onSeasonSelect={onSeasonSelect}
onModalClose={onSelectModalClose}
@@ -465,7 +475,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
<SelectEpisodeModal
isOpen={selectModalOpen === 'episode'}
selectedIds={[id]}
seriesId={series && series.id}
seriesId={series?.id}
isAnime={isAnime}
seasonNumber={seasonNumber}
selectedDetails={relativePath}
@@ -3,6 +3,19 @@ import Episode from 'Episode/Episode';
import Language from 'Language/Language';
import { QualityModel } from 'Quality/Quality';
import Series from 'Series/Series';
import Rejection from 'typings/Rejection';
export interface InteractiveImportCommandOptions {
path: string;
folderName: string;
seriesId: number;
episodeIds: number[];
releaseGroup?: string;
quality: QualityModel;
languages: Language[];
downloadId?: string;
episodeFileId?: number;
}
interface InteractiveImport extends ModelBase {
path: string;
@@ -18,7 +31,7 @@ interface InteractiveImport extends ModelBase {
episodes: Episode[];
qualityWeight: number;
customFormats: object[];
rejections: string[];
rejections: Rejection[];
episodeFileId?: number;
}
@@ -27,8 +27,8 @@ function InteractiveImportModal(props: InteractiveImportModalProps) {
const previousIsOpen = usePrevious(isOpen);
const onFolderSelect = useCallback(
(f) => {
setFolderPath(f);
(path: string) => {
setFolderPath(path);
},
[setFolderPath]
);
@@ -1,6 +1,7 @@
import React, { useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import { LanguageSettingsAppState } from 'App/State/SettingsAppState';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
@@ -25,11 +26,12 @@ interface SelectLanguageModalContentProps {
function createFilteredLanguagesSelector() {
return createSelector(createLanguagesSelector(), (languages) => {
const { isFetching, isPopulated, error, items } = languages;
const { isFetching, isPopulated, error, items } =
languages as LanguageSettingsAppState;
const filterItems = ['Any', 'Original'];
const filteredLanguages = items.filter(
(lang) => !filterItems.includes(lang.name)
(lang: Language) => !filterItems.includes(lang.name)
);
return {
@@ -51,7 +53,7 @@ function SelectLanguageModalContent(props: SelectLanguageModalContentProps) {
const [languageIds, setLanguageIds] = useState(props.languageIds);
const onLanguageChange = useCallback(
({ value, name }) => {
({ name, value }: { name: string; value: boolean }) => {
const changedId = parseInt(name);
let newLanguages = [...languageIds];
@@ -1,6 +1,8 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import { Error } from 'App/State/AppSectionState';
import AppState from 'App/State/AppState';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
@@ -12,22 +14,32 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props';
import { QualityModel } from 'Quality/Quality';
import Quality, { QualityModel } from 'Quality/Quality';
import { fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import { CheckInputChanged } from 'typings/inputs';
import getQualities from 'Utilities/Quality/getQualities';
function createQualitySchemeSelctor() {
interface QualitySchemaState {
isFetching: boolean;
isPopulated: boolean;
error: Error;
items: Quality[];
}
function createQualitySchemaSelector() {
return createSelector(
(state) => state.settings.qualityProfiles,
(qualityProfiles) => {
(state: AppState) => state.settings.qualityProfiles,
(qualityProfiles): QualitySchemaState => {
const { isSchemaFetching, isSchemaPopulated, schemaError, schema } =
qualityProfiles;
const items = getQualities(schema.items) as Quality[];
return {
isFetching: isSchemaFetching,
isPopulated: isSchemaPopulated,
error: schemaError,
items: getQualities(schema.items),
items,
};
}
);
@@ -50,7 +62,7 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
const [real, setReal] = useState(props.real);
const { isFetching, isPopulated, error, items } = useSelector(
createQualitySchemeSelctor()
createQualitySchemaSelector()
);
const dispatch = useDispatch();
@@ -72,28 +84,28 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
}, [items]);
const onQualityChange = useCallback(
({ value }) => {
({ value }: { value: string }) => {
setQualityId(parseInt(value));
},
[setQualityId]
);
const onProperChange = useCallback(
({ value }) => {
({ value }: CheckInputChanged) => {
setProper(value);
},
[setProper]
);
const onRealChange = useCallback(
({ value }) => {
({ value }: CheckInputChanged) => {
setReal(value);
},
[setReal]
);
const onQualitySelectWrapper = useCallback(() => {
const quality = items.find((item) => item.id === qualityId);
const quality = items.find((item) => item.id === qualityId) as Quality;
const revision = {
version: proper ? 2 : 1,
@@ -25,7 +25,7 @@ function SelectReleaseGroupModalContent(
const [releaseGroup, setReleaseGroup] = useState(props.releaseGroup);
const onReleaseGroupChange = useCallback(
({ value }) => {
({ value }: { value: string }) => {
setReleaseGroup(value);
},
[setReleaseGroup]
@@ -5,8 +5,8 @@ import SelectSeasonModalContent from './SelectSeasonModalContent';
interface SelectSeasonModalProps {
isOpen: boolean;
modalTitle: string;
seriesId: number;
onSeasonSelect(seasonNumber): void;
seriesId?: number;
onSeasonSelect(seasonNumber: number): void;
onModalClose(): void;
}
@@ -5,20 +5,21 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { Season } from 'Series/Series';
import { createSeriesSelectorForHook } from 'Store/Selectors/createSeriesSelector';
import SelectSeasonRow from './SelectSeasonRow';
interface SelectSeasonModalContentProps {
seriesId: number;
seriesId?: number;
modalTitle: string;
onSeasonSelect(seasonNumber): void;
onSeasonSelect(seasonNumber: number): void;
onModalClose(): void;
}
function SelectSeasonModalContent(props: SelectSeasonModalContentProps) {
const { seriesId, modalTitle, onSeasonSelect, onModalClose } = props;
const series = useSelector(createSeriesSelectorForHook(seriesId));
const seasons = useMemo(() => {
const seasons = useMemo<Season[]>(() => {
return series.seasons.slice(0).reverse();
}, [series]);
@@ -22,11 +22,11 @@ interface SelectSeriesModalContentProps {
function SelectSeriesModalContent(props: SelectSeriesModalContentProps) {
const { modalTitle, onSeriesSelect, onModalClose } = props;
const allSeries = useSelector(createAllSeriesSelector());
const allSeries: Series[] = useSelector(createAllSeriesSelector());
const [filter, setFilter] = useState('');
const onFilterChange = useCallback(
({ value }) => {
({ value }: { value: string }) => {
setFilter(value);
},
[setFilter]
@@ -34,7 +34,7 @@ function SelectSeriesModalContent(props: SelectSeriesModalContentProps) {
const onSeriesSelectWrapper = useCallback(
(seriesId: number) => {
const series = allSeries.find((s) => s.id === seriesId);
const series = allSeries.find((s) => s.id === seriesId) as Series;
onSeriesSelect(series);
},