Convert store selectors to Typescript

This commit is contained in:
Bogdan 2023-07-30 13:04:17 +03:00
parent efd5e92ca5
commit e51b85449d
30 changed files with 216 additions and 141 deletions

View file

@ -5,6 +5,7 @@ import IndexerAppState, {
} from './IndexerAppState';
import IndexerStatsAppState from './IndexerStatsAppState';
import SettingsAppState from './SettingsAppState';
import SystemAppState from './SystemAppState';
import TagsAppState from './TagsAppState';
interface FilterBuilderPropOption {
@ -46,6 +47,7 @@ interface AppState {
indexerStatus: IndexerStatusAppState;
indexers: IndexerAppState;
settings: SettingsAppState;
system: SystemAppState;
tags: TagsAppState;
}

View file

@ -28,7 +28,9 @@ export interface IndexerIndexAppState {
interface IndexerAppState
extends AppSectionState<Indexer>,
AppSectionDeleteState,
AppSectionSaveState {}
AppSectionSaveState {
itemMap: Record<number, number>;
}
export type IndexerStatusAppState = AppSectionState<IndexerStatus>;

View file

@ -0,0 +1,10 @@
import AppSectionState, {
AppSectionDeleteState,
} from 'App/State/AppSectionState';
import Release from 'typings/Release';
interface ReleaseAppState
extends AppSectionState<Release>,
AppSectionDeleteState {}
export default ReleaseAppState;

View file

@ -1,5 +1,6 @@
import AppSectionState, {
AppSectionDeleteState,
AppSectionItemState,
AppSectionSaveState,
} from 'App/State/AppSectionState';
import Application from 'typings/Application';
@ -26,14 +27,14 @@ export interface NotificationAppState
extends AppSectionState<Notification>,
AppSectionDeleteState {}
export type UiSettingsAppState = AppSectionState<UiSettings>;
export type UiSettingsAppState = AppSectionItemState<UiSettings>;
interface SettingsAppState {
appProfiles: AppProfileAppState;
applications: ApplicationAppState;
downloadClients: DownloadClientAppState;
notifications: NotificationAppState;
uiSettings: UiSettingsAppState;
ui: UiSettingsAppState;
}
export default SettingsAppState;

View file

@ -0,0 +1,10 @@
import SystemStatus from 'typings/SystemStatus';
import { AppSectionItemState } from './AppSectionState';
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
interface SystemAppState {
status: SystemStatusAppState;
}
export default SystemAppState;

View file

@ -1,12 +1,28 @@
import ModelBase from 'App/ModelBase';
import AppSectionState, {
AppSectionDeleteState,
AppSectionSaveState,
} from 'App/State/AppSectionState';
export interface Tag extends ModelBase {
label: string;
}
interface TagsAppState extends AppSectionState<Tag>, AppSectionDeleteState {}
export interface TagDetail extends ModelBase {
label: string;
applicationIds: number[];
indexerIds: number[];
indexerProxyIds: number[];
notificationIds: number[];
}
export interface TagDetailAppState
extends AppSectionState<TagDetail>,
AppSectionDeleteState,
AppSectionSaveState {}
interface TagsAppState extends AppSectionState<Tag>, AppSectionDeleteState {
details: TagDetailAppState;
}
export default TagsAppState;

View file

@ -6,6 +6,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import Indexer from 'Indexer/Indexer';
import { deleteIndexer } from 'Store/Actions/indexerActions';
import { createIndexerSelectorForHook } from 'Store/Selectors/createIndexerSelector';
import translate from 'Utilities/String/translate';
@ -18,7 +19,9 @@ interface DeleteIndexerModalContentProps {
function DeleteIndexerModalContent(props: DeleteIndexerModalContentProps) {
const { indexerId, onModalClose } = props;
const { name } = useSelector(createIndexerSelectorForHook(indexerId));
const { name } = useSelector(
createIndexerSelectorForHook(indexerId)
) as Indexer;
const dispatch = useDispatch();
const onConfirmDelete = useCallback(() => {

View file

@ -12,6 +12,7 @@ import { icons } from 'Helpers/Props';
import DeleteIndexerModal from 'Indexer/Delete/DeleteIndexerModal';
import EditIndexerModalConnector from 'Indexer/Edit/EditIndexerModalConnector';
import createIndexerIndexItemSelector from 'Indexer/Index/createIndexerIndexItemSelector';
import Indexer from 'Indexer/Indexer';
import IndexerTitleLink from 'Indexer/IndexerTitleLink';
import { SelectStateInputProps } from 'typings/props';
import firstCharToUpper from 'Utilities/String/firstCharToUpper';
@ -47,7 +48,7 @@ function IndexerIndexRow(props: IndexerIndexRowProps) {
fields,
added,
capabilities,
} = indexer;
} = indexer as Indexer;
const baseUrl =
fields.find((field) => field.name === 'baseUrl')?.value ??

View file

@ -1,5 +1,4 @@
import { createSelector } from 'reselect';
import Indexer from 'Indexer/Indexer';
import createIndexerAppProfileSelector from 'Store/Selectors/createIndexerAppProfileSelector';
import { createIndexerSelectorForHook } from 'Store/Selectors/createIndexerSelector';
import createIndexerStatusSelector from 'Store/Selectors/createIndexerStatusSelector';
@ -11,7 +10,7 @@ function createIndexerIndexItemSelector(indexerId: number) {
createIndexerAppProfileSelector(indexerId),
createIndexerStatusSelector(indexerId),
createUISettingsSelector(),
(indexer: Indexer, appProfile, status, uiSettings) => {
(indexer, appProfile, status, uiSettings) => {
return {
indexer,
appProfile,

View file

@ -25,11 +25,13 @@ export interface IndexerCapabilities extends ModelBase {
}
export interface IndexerField extends ModelBase {
order: number;
name: string;
label: string;
advanced: boolean;
type: string;
value: string;
privacy: string;
}
interface Indexer extends ModelBase {
@ -54,6 +56,11 @@ interface Indexer extends ModelBase {
capabilities: IndexerCapabilities;
indexerUrls: string[];
legacyUrls: string[];
appProfileId: number;
implementationName: string;
implementation: string;
configContract: string;
infoLink: string;
}
export default Indexer;

View file

@ -29,7 +29,7 @@ import styles from './IndexerInfoModalContent.css';
function createIndexerInfoItemSelector(indexerId: number) {
return createSelector(
createIndexerSelectorForHook(indexerId),
(indexer: Indexer) => {
(indexer?: Indexer) => {
return {
indexer,
};
@ -58,7 +58,7 @@ function IndexerInfoModalContent(props: IndexerInfoModalContentProps) {
tags,
protocol,
capabilities,
} = indexer;
} = indexer as Indexer;
const { onModalClose } = props;

View file

@ -1,15 +0,0 @@
import { createSelector } from 'reselect';
function createAppProfileSelector() {
return createSelector(
(state, { appProfileId }) => appProfileId,
(state) => state.settings.appProfiles.items,
(appProfileId, appProfiles) => {
return appProfiles.find((profile) => {
return profile.id === appProfileId;
});
}
);
}
export default createAppProfileSelector;

View file

@ -0,0 +1,14 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
function createAppProfileSelector() {
return createSelector(
(_: AppState, { appProfileId }: { appProfileId: number }) => appProfileId,
(state: AppState) => state.settings.appProfiles.items,
(appProfileId, appProfiles) => {
return appProfiles.find((profile) => profile.id === appProfileId);
}
);
}
export default createAppProfileSelector;

View file

@ -2,13 +2,10 @@ import { createSelector } from 'reselect';
import { isCommandExecuting } from 'Utilities/Command';
import createCommandSelector from './createCommandSelector';
function createCommandExecutingSelector(name, contraints = {}) {
return createSelector(
createCommandSelector(name, contraints),
(command) => {
return isCommandExecuting(command);
}
);
function createCommandExecutingSelector(name: string, contraints = {}) {
return createSelector(createCommandSelector(name, contraints), (command) => {
return isCommandExecuting(command);
});
}
export default createCommandExecutingSelector;

View file

@ -1,14 +0,0 @@
import { createSelector } from 'reselect';
import { findCommand } from 'Utilities/Command';
import createCommandsSelector from './createCommandsSelector';
function createCommandSelector(name, contraints = {}) {
return createSelector(
createCommandsSelector(),
(commands) => {
return findCommand(commands, { name, ...contraints });
}
);
}
export default createCommandSelector;

View file

@ -0,0 +1,11 @@
import { createSelector } from 'reselect';
import { findCommand } from 'Utilities/Command';
import createCommandsSelector from './createCommandsSelector';
function createCommandSelector(name: string, contraints = {}) {
return createSelector(createCommandsSelector(), (commands) => {
return findCommand(commands, { name, ...contraints });
});
}
export default createCommandSelector;

View file

@ -1,9 +0,0 @@
import _ from 'lodash';
import { createSelectorCreator, defaultMemoize } from 'reselect';
const createDeepEqualSelector = createSelectorCreator(
defaultMemoize,
_.isEqual
);
export default createDeepEqualSelector;

View file

@ -0,0 +1,6 @@
import { isEqual } from 'lodash';
import { createSelectorCreator, defaultMemoize } from 'reselect';
const createDeepEqualSelector = createSelectorCreator(defaultMemoize, isEqual);
export default createDeepEqualSelector;

View file

@ -1,13 +1,15 @@
import _ from 'lodash';
import { some } from 'lodash';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import createAllIndexersSelector from './createAllIndexersSelector';
function createExistingIndexerSelector() {
return createSelector(
(state, { definitionName }) => definitionName,
(_: AppState, { definitionName }: { definitionName: string }) =>
definitionName,
createAllIndexersSelector(),
(definitionName, indexers) => {
return _.some(indexers, { definitionName });
return some(indexers, { definitionName });
}
);
}

View file

@ -1,15 +1,14 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import Indexer from 'Indexer/Indexer';
import { createIndexerSelectorForHook } from './createIndexerSelector';
function createIndexerAppProfileSelector(indexerId: number) {
return createSelector(
(state: AppState) => state.settings.appProfiles.items,
createIndexerSelectorForHook(indexerId),
(appProfiles, indexer = {}) => {
return appProfiles.find((profile) => {
return profile.id === indexer.appProfileId;
});
(appProfiles, indexer = {} as Indexer) => {
return appProfiles.find((profile) => profile.id === indexer.appProfileId);
}
);
}

View file

@ -1,24 +0,0 @@
import { createSelector } from 'reselect';
export function createIndexerSelectorForHook(indexerId) {
return createSelector(
(state) => state.indexers.itemMap,
(state) => state.indexers.items,
(itemMap, allIndexers) => {
return indexerId ? allIndexers[itemMap[indexerId]]: undefined;
}
);
}
function createIndexerSelector() {
return createSelector(
(state, { indexerId }) => indexerId,
(state) => state.indexers.itemMap,
(state) => state.indexers.items,
(indexerId, itemMap, allIndexers) => {
return allIndexers[itemMap[indexerId]];
}
);
}
export default createIndexerSelector;

View file

@ -0,0 +1,25 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
export function createIndexerSelectorForHook(indexerId: number) {
return createSelector(
(state: AppState) => state.indexers.itemMap,
(state: AppState) => state.indexers.items,
(itemMap, allIndexers) => {
return indexerId ? allIndexers[itemMap[indexerId]] : undefined;
}
);
}
function createIndexerSelector() {
return createSelector(
(_: AppState, { indexerId }: { indexerId: number }) => indexerId,
(state: AppState) => state.indexers.itemMap,
(state: AppState) => state.indexers.items,
(indexerId, itemMap, allIndexers) => {
return allIndexers[itemMap[indexerId]];
}
);
}
export default createIndexerSelector;

View file

@ -1,24 +0,0 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import createAllIndexersSelector from './createAllIndexersSelector';
function createProfileInUseSelector(profileProp) {
return createSelector(
(state, { id }) => id,
(state) => state.settings.appProfiles.items,
createAllIndexersSelector(),
(id, profiles, indexers) => {
if (!id) {
return false;
}
if (_.some(indexers, { [profileProp]: id }) || profiles.length <= 1) {
return true;
}
return false;
}
);
}
export default createProfileInUseSelector;

View file

@ -0,0 +1,21 @@
import { some } from 'lodash';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import createAllIndexersSelector from './createAllIndexersSelector';
function createProfileInUseSelector(profileProp: string) {
return createSelector(
(_: AppState, { id }: { id: number }) => id,
(state: AppState) => state.settings.appProfiles.items,
createAllIndexersSelector(),
(id, profiles, indexers) => {
if (!id) {
return false;
}
return some(indexers, { [profileProp]: id }) || profiles.length <= 1;
}
);
}
export default createProfileInUseSelector;

View file

@ -1,8 +1,9 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
function createSystemStatusSelector() {
return createSelector(
(state) => state.system.status,
(state: AppState) => state.system.status,
(status) => {
return status.item;
}

View file

@ -1,9 +1,10 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
function createTagDetailsSelector() {
return createSelector(
(state, { id }) => id,
(state) => state.tags.details.items,
(_: AppState, { id }: { id: number }) => id,
(state: AppState) => state.tags.details.items,
(id, tagDetails) => {
return tagDetails.find((t) => t.id === id);
}

View file

@ -1,8 +1,9 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
function createUISettingsSelector() {
return createSelector(
(state) => state.settings.ui,
(state: AppState) => state.settings.ui,
(ui) => {
return ui.item;
}

View file

@ -1,27 +0,0 @@
import ModelBase from 'App/ModelBase';
export interface Field {
order: number;
name: string;
label: string;
value: boolean | number | string;
type: string;
advanced: boolean;
privacy: string;
}
interface Indexer extends ModelBase {
enable: boolean;
appProfileId: number;
protocol: string;
priority: number;
name: string;
fields: Field[];
implementationName: string;
implementation: string;
configContract: string;
infoLink: string;
tags: number[];
}
export default Indexer;

View file

@ -0,0 +1,28 @@
import ModelBase from 'App/ModelBase';
import { IndexerCategory } from 'Indexer/Indexer';
interface Release extends ModelBase {
guid: string;
categories: IndexerCategory[];
protocol: string;
title: string;
sortTitle: string;
fileName: string;
infoUrl: string;
downloadUrl?: string;
magnetUrl?: string;
indexerId: number;
indexer: string;
age: number;
ageHours: number;
ageMinutes: number;
publishDate: string;
size?: number;
files?: number;
grabs?: number;
seeders?: number;
leechers?: number;
indexerFlags: string[];
}
export default Release;

View file

@ -0,0 +1,31 @@
interface SystemStatus {
appData: string;
appName: string;
authentication: string;
branch: string;
buildTime: string;
instanceName: string;
isAdmin: boolean;
isDebug: boolean;
isDocker: boolean;
isLinux: boolean;
isNetCore: boolean;
isOsx: boolean;
isProduction: boolean;
isUserInteractive: boolean;
isWindows: boolean;
migrationVersion: number;
mode: string;
osName: string;
osVersion: string;
packageUpdateMechanism: string;
runtimeName: string;
runtimeVersion: string;
sqliteVersion: string;
startTime: string;
startupPath: string;
urlBase: string;
version: string;
}
export default SystemStatus;