diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 603b20a48..cc26a2633 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -28,7 +28,8 @@ module.exports = { globals: { expect: false, chai: false, - sinon: false + sinon: false, + JSX: true }, parserOptions: { diff --git a/frontend/src/App/State/AppState.ts b/frontend/src/App/State/AppState.ts index 7dc6bc331..979785f3a 100644 --- a/frontend/src/App/State/AppState.ts +++ b/frontend/src/App/State/AppState.ts @@ -5,6 +5,7 @@ import CommandAppState from './CommandAppState'; import HistoryAppState from './HistoryAppState'; import QueueAppState from './QueueAppState'; import SettingsAppState from './SettingsAppState'; +import SystemAppState from './SystemAppState'; import TagsAppState from './TagsAppState'; import TrackFilesAppState from './TrackFilesAppState'; import TracksAppState from './TracksAppState'; @@ -62,6 +63,7 @@ interface AppState { tags: TagsAppState; trackFiles: TrackFilesAppState; tracksSelection: TracksAppState; + system: SystemAppState; } export default AppState; diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index a84f09b53..547f353cd 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -1,5 +1,6 @@ import AppSectionState, { AppSectionDeleteState, + AppSectionItemState, AppSectionSaveState, AppSectionSchemaState, } from 'App/State/AppSectionState'; @@ -46,7 +47,7 @@ export interface RootFolderAppState AppSectionSaveState {} export type IndexerFlagSettingsAppState = AppSectionState; -export type UiSettingsAppState = AppSectionState; +export type UiSettingsAppState = AppSectionItemState; interface SettingsAppState { downloadClients: DownloadClientAppState; @@ -57,7 +58,7 @@ interface SettingsAppState { notifications: NotificationAppState; qualityProfiles: QualityProfilesAppState; rootFolders: RootFolderAppState; - uiSettings: UiSettingsAppState; + ui: UiSettingsAppState; } export default SettingsAppState; diff --git a/frontend/src/App/State/SystemAppState.ts b/frontend/src/App/State/SystemAppState.ts new file mode 100644 index 000000000..d43c1d0ee --- /dev/null +++ b/frontend/src/App/State/SystemAppState.ts @@ -0,0 +1,10 @@ +import SystemStatus from 'typings/SystemStatus'; +import { AppSectionItemState } from './AppSectionState'; + +export type SystemStatusAppState = AppSectionItemState; + +interface SystemAppState { + status: SystemStatusAppState; +} + +export default SystemAppState; diff --git a/frontend/src/App/State/TagsAppState.ts b/frontend/src/App/State/TagsAppState.ts index d1f1d5a2f..edaf3a158 100644 --- a/frontend/src/App/State/TagsAppState.ts +++ b/frontend/src/App/State/TagsAppState.ts @@ -1,12 +1,32 @@ import ModelBase from 'App/ModelBase'; import AppSectionState, { AppSectionDeleteState, + AppSectionSaveState, } from 'App/State/AppSectionState'; export interface Tag extends ModelBase { label: string; } -interface TagsAppState extends AppSectionState, AppSectionDeleteState {} +export interface TagDetail extends ModelBase { + label: string; + autoTagIds: number[]; + delayProfileIds: number[]; + downloadClientIds: []; + importListIds: number[]; + indexerIds: number[]; + notificationIds: number[]; + restrictionIds: number[]; + artistIds: number[]; +} + +export interface TagDetailAppState + extends AppSectionState, + AppSectionDeleteState, + AppSectionSaveState {} + +interface TagsAppState extends AppSectionState, AppSectionDeleteState { + details: TagDetailAppState; +} export default TagsAppState; diff --git a/frontend/src/Store/Selectors/createAllArtistSelector.js b/frontend/src/Store/Selectors/createAllArtistSelector.ts similarity index 71% rename from frontend/src/Store/Selectors/createAllArtistSelector.js rename to frontend/src/Store/Selectors/createAllArtistSelector.ts index 38b1bcef1..6b6010429 100644 --- a/frontend/src/Store/Selectors/createAllArtistSelector.js +++ b/frontend/src/Store/Selectors/createAllArtistSelector.ts @@ -1,8 +1,9 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; function createAllArtistSelector() { return createSelector( - (state) => state.artist, + (state: AppState) => state.artist, (artist) => { return artist.items; } diff --git a/frontend/src/Store/Selectors/createArtistCountSelector.js b/frontend/src/Store/Selectors/createArtistCountSelector.ts similarity index 65% rename from frontend/src/Store/Selectors/createArtistCountSelector.js rename to frontend/src/Store/Selectors/createArtistCountSelector.ts index 31e0a39fc..b432d64a7 100644 --- a/frontend/src/Store/Selectors/createArtistCountSelector.js +++ b/frontend/src/Store/Selectors/createArtistCountSelector.ts @@ -1,18 +1,19 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; import createAllArtistSelector from './createAllArtistSelector'; function createArtistCountSelector() { return createSelector( createAllArtistSelector(), - (state) => state.artist.error, - (state) => state.artist.isFetching, - (state) => state.artist.isPopulated, + (state: AppState) => state.artist.error, + (state: AppState) => state.artist.isFetching, + (state: AppState) => state.artist.isPopulated, (artists, error, isFetching, isPopulated) => { return { count: artists.length, error, isFetching, - isPopulated + isPopulated, }; } ); diff --git a/frontend/src/Store/Selectors/createCommandExecutingSelector.js b/frontend/src/Store/Selectors/createCommandExecutingSelector.ts similarity index 50% rename from frontend/src/Store/Selectors/createCommandExecutingSelector.js rename to frontend/src/Store/Selectors/createCommandExecutingSelector.ts index 6037d5820..6a80e172b 100644 --- a/frontend/src/Store/Selectors/createCommandExecutingSelector.js +++ b/frontend/src/Store/Selectors/createCommandExecutingSelector.ts @@ -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; diff --git a/frontend/src/Store/Selectors/createCommandSelector.js b/frontend/src/Store/Selectors/createCommandSelector.js deleted file mode 100644 index 709dfebaf..000000000 --- a/frontend/src/Store/Selectors/createCommandSelector.js +++ /dev/null @@ -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; diff --git a/frontend/src/Store/Selectors/createCommandSelector.ts b/frontend/src/Store/Selectors/createCommandSelector.ts new file mode 100644 index 000000000..cced7b186 --- /dev/null +++ b/frontend/src/Store/Selectors/createCommandSelector.ts @@ -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; diff --git a/frontend/src/Store/Selectors/createCommandsSelector.js b/frontend/src/Store/Selectors/createCommandsSelector.ts similarity index 71% rename from frontend/src/Store/Selectors/createCommandsSelector.js rename to frontend/src/Store/Selectors/createCommandsSelector.ts index 7b9edffd9..2dd5d24a2 100644 --- a/frontend/src/Store/Selectors/createCommandsSelector.js +++ b/frontend/src/Store/Selectors/createCommandsSelector.ts @@ -1,8 +1,9 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; function createCommandsSelector() { return createSelector( - (state) => state.commands, + (state: AppState) => state.commands, (commands) => { return commands.items; } diff --git a/frontend/src/Store/Selectors/createDeepEqualSelector.js b/frontend/src/Store/Selectors/createDeepEqualSelector.js deleted file mode 100644 index 85562f28b..000000000 --- a/frontend/src/Store/Selectors/createDeepEqualSelector.js +++ /dev/null @@ -1,9 +0,0 @@ -import _ from 'lodash'; -import { createSelectorCreator, defaultMemoize } from 'reselect'; - -const createDeepEqualSelector = createSelectorCreator( - defaultMemoize, - _.isEqual -); - -export default createDeepEqualSelector; diff --git a/frontend/src/Store/Selectors/createDeepEqualSelector.ts b/frontend/src/Store/Selectors/createDeepEqualSelector.ts new file mode 100644 index 000000000..9d4a63d2e --- /dev/null +++ b/frontend/src/Store/Selectors/createDeepEqualSelector.ts @@ -0,0 +1,6 @@ +import { isEqual } from 'lodash'; +import { createSelectorCreator, defaultMemoize } from 'reselect'; + +const createDeepEqualSelector = createSelectorCreator(defaultMemoize, isEqual); + +export default createDeepEqualSelector; diff --git a/frontend/src/Store/Selectors/createExecutingCommandsSelector.js b/frontend/src/Store/Selectors/createExecutingCommandsSelector.ts similarity index 78% rename from frontend/src/Store/Selectors/createExecutingCommandsSelector.js rename to frontend/src/Store/Selectors/createExecutingCommandsSelector.ts index 266865a8a..dd16571fc 100644 --- a/frontend/src/Store/Selectors/createExecutingCommandsSelector.js +++ b/frontend/src/Store/Selectors/createExecutingCommandsSelector.ts @@ -1,9 +1,10 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; import { isCommandExecuting } from 'Utilities/Command'; function createExecutingCommandsSelector() { return createSelector( - (state) => state.commands.items, + (state: AppState) => state.commands.items, (commands) => { return commands.filter((command) => isCommandExecuting(command)); } diff --git a/frontend/src/Store/Selectors/createExistingArtistSelector.js b/frontend/src/Store/Selectors/createExistingArtistSelector.ts similarity index 58% rename from frontend/src/Store/Selectors/createExistingArtistSelector.js rename to frontend/src/Store/Selectors/createExistingArtistSelector.ts index 4811f2034..91b5bc4d6 100644 --- a/frontend/src/Store/Selectors/createExistingArtistSelector.js +++ b/frontend/src/Store/Selectors/createExistingArtistSelector.ts @@ -1,13 +1,15 @@ -import _ from 'lodash'; +import { some } from 'lodash'; import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; import createAllArtistSelector from './createAllArtistSelector'; function createExistingArtistSelector() { return createSelector( - (state, { foreignArtistId }) => foreignArtistId, + (_: AppState, { foreignArtistId }: { foreignArtistId: string }) => + foreignArtistId, createAllArtistSelector(), (foreignArtistId, artist) => { - return _.some(artist, { foreignArtistId }); + return some(artist, { foreignArtistId }); } ); } diff --git a/frontend/src/Store/Selectors/createMetadataProfileSelector.js b/frontend/src/Store/Selectors/createMetadataProfileSelector.js deleted file mode 100644 index bdd0d0636..000000000 --- a/frontend/src/Store/Selectors/createMetadataProfileSelector.js +++ /dev/null @@ -1,15 +0,0 @@ -import { createSelector } from 'reselect'; - -function createMetadataProfileSelector() { - return createSelector( - (state, { metadataProfileId }) => metadataProfileId, - (state) => state.settings.metadataProfiles.items, - (metadataProfileId, metadataProfiles) => { - return metadataProfiles.find((profile) => { - return profile.id === metadataProfileId; - }); - } - ); -} - -export default createMetadataProfileSelector; diff --git a/frontend/src/Store/Selectors/createMetadataProfileSelector.ts b/frontend/src/Store/Selectors/createMetadataProfileSelector.ts new file mode 100644 index 000000000..ae4c061db --- /dev/null +++ b/frontend/src/Store/Selectors/createMetadataProfileSelector.ts @@ -0,0 +1,17 @@ +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; + +function createMetadataProfileSelector() { + return createSelector( + (_: AppState, { metadataProfileId }: { metadataProfileId: number }) => + metadataProfileId, + (state: AppState) => state.settings.metadataProfiles.items, + (metadataProfileId, metadataProfiles) => { + return metadataProfiles.find( + (profile) => profile.id === metadataProfileId + ); + } + ); +} + +export default createMetadataProfileSelector; diff --git a/frontend/src/Store/Selectors/createProfileInUseSelector.js b/frontend/src/Store/Selectors/createProfileInUseSelector.js deleted file mode 100644 index 84fefb83e..000000000 --- a/frontend/src/Store/Selectors/createProfileInUseSelector.js +++ /dev/null @@ -1,24 +0,0 @@ -import _ from 'lodash'; -import { createSelector } from 'reselect'; -import createAllArtistSelector from './createAllArtistSelector'; - -function createProfileInUseSelector(profileProp) { - return createSelector( - (state, { id }) => id, - createAllArtistSelector(), - (state) => state.settings.importLists.items, - (id, artist, lists) => { - if (!id) { - return false; - } - - if (_.some(artist, { [profileProp]: id }) || _.some(lists, { [profileProp]: id })) { - return true; - } - - return false; - } - ); -} - -export default createProfileInUseSelector; diff --git a/frontend/src/Store/Selectors/createProfileInUseSelector.ts b/frontend/src/Store/Selectors/createProfileInUseSelector.ts new file mode 100644 index 000000000..85f0c3211 --- /dev/null +++ b/frontend/src/Store/Selectors/createProfileInUseSelector.ts @@ -0,0 +1,25 @@ +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; +import Artist from 'Artist/Artist'; +import ImportList from 'typings/ImportList'; +import createAllArtistSelector from './createAllArtistSelector'; + +function createProfileInUseSelector(profileProp: string) { + return createSelector( + (_: AppState, { id }: { id: number }) => id, + createAllArtistSelector(), + (state: AppState) => state.settings.importLists.items, + (id, artists, lists) => { + if (!id) { + return false; + } + + return ( + artists.some((a) => a[profileProp as keyof Artist] === id) || + lists.some((list) => list[profileProp as keyof ImportList] === id) + ); + } + ); +} + +export default createProfileInUseSelector; diff --git a/frontend/src/Store/Selectors/createQualityProfileSelector.js b/frontend/src/Store/Selectors/createQualityProfileSelector.js deleted file mode 100644 index 611dfc903..000000000 --- a/frontend/src/Store/Selectors/createQualityProfileSelector.js +++ /dev/null @@ -1,26 +0,0 @@ -import { createSelector } from 'reselect'; - -export function createQualityProfileSelectorForHook(qualityProfileId) { - return createSelector( - (state) => state.settings.qualityProfiles.items, - (qualityProfiles) => { - return qualityProfiles.find((profile) => { - return profile.id === qualityProfileId; - }); - } - ); -} - -function createQualityProfileSelector() { - return createSelector( - (state, { qualityProfileId }) => qualityProfileId, - (state) => state.settings.qualityProfiles.items, - (qualityProfileId, qualityProfiles) => { - return qualityProfiles.find((profile) => { - return profile.id === qualityProfileId; - }); - } - ); -} - -export default createQualityProfileSelector; diff --git a/frontend/src/Store/Selectors/createQualityProfileSelector.ts b/frontend/src/Store/Selectors/createQualityProfileSelector.ts new file mode 100644 index 000000000..b913e0c46 --- /dev/null +++ b/frontend/src/Store/Selectors/createQualityProfileSelector.ts @@ -0,0 +1,24 @@ +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; + +export function createQualityProfileSelectorForHook(qualityProfileId: number) { + return createSelector( + (state: AppState) => state.settings.qualityProfiles.items, + (qualityProfiles) => { + return qualityProfiles.find((profile) => profile.id === qualityProfileId); + } + ); +} + +function createQualityProfileSelector() { + return createSelector( + (_: AppState, { qualityProfileId }: { qualityProfileId: number }) => + qualityProfileId, + (state: AppState) => state.settings.qualityProfiles.items, + (qualityProfileId, qualityProfiles) => { + return qualityProfiles.find((profile) => profile.id === qualityProfileId); + } + ); +} + +export default createQualityProfileSelector; diff --git a/frontend/src/Store/Selectors/createQueueItemSelector.js b/frontend/src/Store/Selectors/createQueueItemSelector.ts similarity index 52% rename from frontend/src/Store/Selectors/createQueueItemSelector.js rename to frontend/src/Store/Selectors/createQueueItemSelector.ts index c85d7ed82..54951a724 100644 --- a/frontend/src/Store/Selectors/createQueueItemSelector.js +++ b/frontend/src/Store/Selectors/createQueueItemSelector.ts @@ -1,21 +1,16 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; function createQueueItemSelector() { return createSelector( - (state, { albumId }) => albumId, - (state) => state.queue.details.items, + (_: AppState, { albumId }: { albumId: number }) => albumId, + (state: AppState) => state.queue.details.items, (albumId, details) => { if (!albumId || !details) { return null; } - return details.find((item) => { - if (item.album) { - return item.album.id === albumId; - } - - return false; - }); + return details.find((item) => item.albumId === albumId); } ); } diff --git a/frontend/src/Store/Selectors/createSystemStatusSelector.js b/frontend/src/Store/Selectors/createSystemStatusSelector.ts similarity index 70% rename from frontend/src/Store/Selectors/createSystemStatusSelector.js rename to frontend/src/Store/Selectors/createSystemStatusSelector.ts index df586bbb9..f5e276069 100644 --- a/frontend/src/Store/Selectors/createSystemStatusSelector.js +++ b/frontend/src/Store/Selectors/createSystemStatusSelector.ts @@ -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; } diff --git a/frontend/src/Store/Selectors/createTagDetailsSelector.js b/frontend/src/Store/Selectors/createTagDetailsSelector.ts similarity index 62% rename from frontend/src/Store/Selectors/createTagDetailsSelector.js rename to frontend/src/Store/Selectors/createTagDetailsSelector.ts index dd178944c..2a271cafe 100644 --- a/frontend/src/Store/Selectors/createTagDetailsSelector.js +++ b/frontend/src/Store/Selectors/createTagDetailsSelector.ts @@ -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); } diff --git a/frontend/src/Store/Selectors/createTagsSelector.js b/frontend/src/Store/Selectors/createTagsSelector.ts similarity index 68% rename from frontend/src/Store/Selectors/createTagsSelector.js rename to frontend/src/Store/Selectors/createTagsSelector.ts index fbfd91cdb..f653ff6e3 100644 --- a/frontend/src/Store/Selectors/createTagsSelector.js +++ b/frontend/src/Store/Selectors/createTagsSelector.ts @@ -1,8 +1,9 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; function createTagsSelector() { return createSelector( - (state) => state.tags.items, + (state: AppState) => state.tags.items, (tags) => { return tags; } diff --git a/frontend/src/Store/Selectors/createTrackFileSelector.js b/frontend/src/Store/Selectors/createTrackFileSelector.ts similarity index 66% rename from frontend/src/Store/Selectors/createTrackFileSelector.js rename to frontend/src/Store/Selectors/createTrackFileSelector.ts index bcfc5cb0b..a162df1fa 100644 --- a/frontend/src/Store/Selectors/createTrackFileSelector.js +++ b/frontend/src/Store/Selectors/createTrackFileSelector.ts @@ -1,9 +1,10 @@ import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; function createTrackFileSelector() { return createSelector( - (state, { trackFileId }) => trackFileId, - (state) => state.trackFiles, + (_: AppState, { trackFileId }: { trackFileId: number }) => trackFileId, + (state: AppState) => state.trackFiles, (trackFileId, trackFiles) => { if (!trackFileId) { return; diff --git a/frontend/src/Store/Selectors/createUISettingsSelector.js b/frontend/src/Store/Selectors/createUISettingsSelector.ts similarity index 69% rename from frontend/src/Store/Selectors/createUISettingsSelector.js rename to frontend/src/Store/Selectors/createUISettingsSelector.ts index b256d0e98..ff539679b 100644 --- a/frontend/src/Store/Selectors/createUISettingsSelector.js +++ b/frontend/src/Store/Selectors/createUISettingsSelector.ts @@ -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; } diff --git a/frontend/src/typings/SystemStatus.ts b/frontend/src/typings/SystemStatus.ts new file mode 100644 index 000000000..e72be2c5c --- /dev/null +++ b/frontend/src/typings/SystemStatus.ts @@ -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;