-
+
\\^$.|?*+()[{ have special meanings and need escaping with a \\
' }} />
+ {'More details'} {'Here'}
-
-
-
-
+ {'Regular expressions can be tested '}
+ Here
}
diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClientsConnector.js b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClientsConnector.js
index 0dc410fcb..d9e543469 100644
--- a/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClientsConnector.js
+++ b/frontend/src/Settings/DownloadClients/DownloadClients/DownloadClientsConnector.js
@@ -5,12 +5,12 @@ import { createSelector } from 'reselect';
import { deleteDownloadClient, fetchDownloadClients } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import createTagsSelector from 'Store/Selectors/createTagsSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import DownloadClients from './DownloadClients';
function createMapStateToProps() {
return createSelector(
- createSortedSectionSelector('settings.downloadClients', sortByProp('name')),
+ createSortedSectionSelector('settings.downloadClients', sortByName),
createTagsSelector(),
(downloadClients, tagList) => {
return {
diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.css b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.css
index 6ea04a0c8..c106388ab 100644
--- a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.css
+++ b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.css
@@ -13,4 +13,4 @@
composes: button from '~Components/Link/Button.css';
margin-right: 10px;
-}
+}
\ No newline at end of file
diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx
index b2c1208cb..2722f02fa 100644
--- a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx
+++ b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx
@@ -10,11 +10,11 @@ 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 Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds } from 'Helpers/Props';
+import SortDirection from 'Helpers/Props/SortDirection';
import {
bulkDeleteDownloadClients,
bulkEditDownloadClients,
@@ -35,7 +35,7 @@ type OnSelectedChangeCallback = React.ComponentProps<
typeof ManageDownloadClientsModalRow
>['onSelectedChange'];
-const COLUMNS: Column[] = [
+const COLUMNS = [
{
name: 'name',
label: () => translate('Name'),
@@ -82,6 +82,8 @@ const COLUMNS: Column[] = [
interface ManageDownloadClientsModalContentProps {
onModalClose(): void;
+ sortKey?: string;
+ sortDirection?: SortDirection;
}
function ManageDownloadClientsModalContent(
@@ -218,9 +220,9 @@ function ManageDownloadClientsModalContent(
{error ?
{errorMessage}
: null}
- {isPopulated && !error && !items.length ? (
+ {isPopulated && !error && !items.length && (
{translate('NoDownloadClientsFound')}
- ) : null}
+ )}
{isPopulated && !!items.length && !isFetching && !isFetching ? (
diff --git a/frontend/src/Settings/General/LoggingSettings.js b/frontend/src/Settings/General/LoggingSettings.js
index 043867853..93918a3d2 100644
--- a/frontend/src/Settings/General/LoggingSettings.js
+++ b/frontend/src/Settings/General/LoggingSettings.js
@@ -15,14 +15,12 @@ const logLevelOptions = [
function LoggingSettings(props) {
const {
- advancedSettings,
settings,
onInputChange
} = props;
const {
- logLevel,
- logSizeLimit
+ logLevel
} = settings;
return (
@@ -41,30 +39,11 @@ function LoggingSettings(props) {
{...logLevel}
/>
-
-
- {translate('LogSizeLimit')}
-
-
-
);
}
LoggingSettings.propTypes = {
- advancedSettings: PropTypes.bool.isRequired,
settings: PropTypes.object.isRequired,
onInputChange: PropTypes.func.isRequired
};
diff --git a/frontend/src/Settings/General/UpdateSettings.js b/frontend/src/Settings/General/UpdateSettings.js
index a151423e5..081f5dda2 100644
--- a/frontend/src/Settings/General/UpdateSettings.js
+++ b/frontend/src/Settings/General/UpdateSettings.js
@@ -18,6 +18,7 @@ function UpdateSettings(props) {
const {
advancedSettings,
settings,
+ isWindows,
packageUpdateMechanism,
onInputChange
} = props;
@@ -43,10 +44,10 @@ function UpdateSettings(props) {
value: titleCase(packageUpdateMechanism)
});
} else {
- updateOptions.push({ key: 'builtIn', value: translate('BuiltIn') });
+ updateOptions.push({ key: 'builtIn', value: 'Built-In' });
}
- updateOptions.push({ key: 'script', value: translate('Script') });
+ updateOptions.push({ key: 'script', value: 'Script' });
return (
);
}
diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
index d50fb2385..2799af7d8 100644
--- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
+++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.js
@@ -292,7 +292,7 @@ function EditImportListModalContent(props) {
diff --git a/frontend/src/Settings/ImportLists/ImportLists/ImportListsConnector.js b/frontend/src/Settings/ImportLists/ImportLists/ImportListsConnector.js
index 5eb47068d..5c6bad8e7 100644
--- a/frontend/src/Settings/ImportLists/ImportLists/ImportListsConnector.js
+++ b/frontend/src/Settings/ImportLists/ImportLists/ImportListsConnector.js
@@ -4,12 +4,12 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { deleteImportList, fetchImportLists, fetchRootFolders } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import ImportLists from './ImportLists';
function createMapStateToProps() {
return createSelector(
- createSortedSectionSelector('settings.importLists', sortByProp('name')),
+ createSortedSectionSelector('settings.importLists', sortByName),
(importLists) => importLists
);
}
diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.css b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.css
index 6ea04a0c8..c106388ab 100644
--- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.css
+++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.css
@@ -13,4 +13,4 @@
composes: button from '~Components/Link/Button.css';
margin-right: 10px;
-}
+}
\ No newline at end of file
diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx
index 4fee485c9..60619c662 100644
--- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx
+++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx
@@ -198,9 +198,9 @@ function ManageImportListsModalContent(
{error ?
{errorMessage}
: null}
- {isPopulated && !error && !items.length ? (
+ {isPopulated && !error && !items.length && (
{translate('NoImportListsFound')}
- ) : null}
+ )}
{isPopulated && !!items.length && !isFetching && !isFetching ? (
{
return {
diff --git a/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.css b/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.css
index 6ea04a0c8..c106388ab 100644
--- a/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.css
+++ b/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.css
@@ -13,4 +13,4 @@
composes: button from '~Components/Link/Button.css';
margin-right: 10px;
-}
+}
\ No newline at end of file
diff --git a/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx b/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx
index 997d1b566..8137111f5 100644
--- a/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx
+++ b/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx
@@ -10,11 +10,11 @@ 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 Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds } from 'Helpers/Props';
+import SortDirection from 'Helpers/Props/SortDirection';
import {
bulkDeleteIndexers,
bulkEditIndexers,
@@ -35,7 +35,7 @@ type OnSelectedChangeCallback = React.ComponentProps<
typeof ManageIndexersModalRow
>['onSelectedChange'];
-const COLUMNS: Column[] = [
+const COLUMNS = [
{
name: 'name',
label: () => translate('Name'),
@@ -82,6 +82,8 @@ const COLUMNS: Column[] = [
interface ManageIndexersModalContentProps {
onModalClose(): void;
+ sortKey?: string;
+ sortDirection?: SortDirection;
}
function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
@@ -213,9 +215,9 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
{error ? {errorMessage}
: null}
- {isPopulated && !error && !items.length ? (
+ {isPopulated && !error && !items.length && (
{translate('NoIndexersFound')}
- ) : null}
+ )}
{isPopulated && !!items.length && !isFetching && !isFetching ? (
-
- {translate('SkipFreeSpaceCheck')}
+ {
+ !isWindows &&
+
+
+ {translate('SkipFreeSpaceCheck')}
+
-
-
+
+
+ }
state.settings.advancedSettings,
(state) => state.settings.namingExamples,
createSettingsSectionSelector(SECTION),
- (advancedSettings, namingExamples, sectionSettings) => {
+ (advancedSettings, examples, sectionSettings) => {
return {
advancedSettings,
- examples: namingExamples.item,
- examplesPopulated: namingExamples.isPopulated,
+ examples: examples.item,
+ examplesPopulated: !_.isEmpty(examples.item),
...sectionSettings
};
}
diff --git a/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js b/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js
index dc91e4622..47e5dfcf1 100644
--- a/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js
+++ b/frontend/src/Settings/MediaManagement/RootFolder/RootFolder.js
@@ -94,9 +94,9 @@ class RootFolder extends Component {
diff --git a/frontend/src/Settings/Metadata/Metadata/MetadatasConnector.js b/frontend/src/Settings/Metadata/Metadata/MetadatasConnector.js
index 8675f4742..fb52ac33b 100644
--- a/frontend/src/Settings/Metadata/Metadata/MetadatasConnector.js
+++ b/frontend/src/Settings/Metadata/Metadata/MetadatasConnector.js
@@ -4,12 +4,12 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchMetadata } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import Metadatas from './Metadatas';
function createMapStateToProps() {
return createSelector(
- createSortedSectionSelector('settings.metadata', sortByProp('name')),
+ createSortedSectionSelector('settings.metadata', sortByName),
(metadata) => metadata
);
}
diff --git a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js
index ee51799f2..383cf057b 100644
--- a/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js
+++ b/frontend/src/Settings/Notifications/Notifications/EditNotificationModalContent.js
@@ -105,7 +105,7 @@ function EditNotificationModalContent(props) {
diff --git a/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js b/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js
index 6351c6f8a..b306f742a 100644
--- a/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js
+++ b/frontend/src/Settings/Notifications/Notifications/NotificationsConnector.js
@@ -5,12 +5,12 @@ import { createSelector } from 'reselect';
import { deleteNotification, fetchNotifications } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import createTagsSelector from 'Store/Selectors/createTagsSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import Notifications from './Notifications';
function createMapStateToProps() {
return createSelector(
- createSortedSectionSelector('settings.notifications', sortByProp('name')),
+ createSortedSectionSelector('settings.notifications', sortByName),
createTagsSelector(),
(notifications, tagList) => {
return {
diff --git a/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js b/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js
index 0d1225a93..da1bf7f44 100644
--- a/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js
+++ b/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js
@@ -87,9 +87,9 @@ function EditDelayProfileModalContent(props) {
{
!isFetching && !!error ?
-
- {translate('AddDelayProfileError')}
- :
+
+ {translate('UnableToAddANewQualityProfilePleaseTryAgain')}
+
:
null
}
@@ -186,7 +186,7 @@ function EditDelayProfileModalContent(props) {
{
id === 1 ?
- {translate('DefaultDelayProfileArtist')}
+ {translate('DefaultDelayProfileHelpText')}
:
@@ -196,7 +196,7 @@ function EditDelayProfileModalContent(props) {
type={inputTypes.TAG}
name="tags"
{...tags}
- helpText={translate('DelayProfileArtistTagsHelpText')}
+ helpText={translate('TagsHelpText')}
onChange={onInputChange}
/>
diff --git a/frontend/src/Settings/Profiles/Metadata/MetadataProfiles.js b/frontend/src/Settings/Profiles/Metadata/MetadataProfiles.js
index 5e719517f..d39303ecc 100644
--- a/frontend/src/Settings/Profiles/Metadata/MetadataProfiles.js
+++ b/frontend/src/Settings/Profiles/Metadata/MetadataProfiles.js
@@ -5,7 +5,7 @@ import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons, metadataProfileNames } from 'Helpers/Props';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import translate from 'Utilities/String/translate';
import EditMetadataProfileModalConnector from './EditMetadataProfileModalConnector';
import MetadataProfile from './MetadataProfile';
@@ -59,20 +59,17 @@ class MetadataProfiles extends Component {
>
{
- items
- .filter((item) => item.name !== metadataProfileNames.NONE)
- .sort(sortByProp('name'))
- .map((item) => {
- return (
-
- );
- })
+ items.filter((item) => item.name !== metadataProfileNames.NONE).sort(sortByName).map((item) => {
+ return (
+
+ );
+ })
}
b.name ? 1 : -1;
}).map((x) => items[x.format]);
}
diff --git a/frontend/src/Settings/Profiles/Quality/QualityProfilesConnector.js b/frontend/src/Settings/Profiles/Quality/QualityProfilesConnector.js
index 4cb318463..581882ffd 100644
--- a/frontend/src/Settings/Profiles/Quality/QualityProfilesConnector.js
+++ b/frontend/src/Settings/Profiles/Quality/QualityProfilesConnector.js
@@ -4,12 +4,12 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { cloneQualityProfile, deleteQualityProfile, fetchQualityProfiles } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import QualityProfiles from './QualityProfiles';
function createMapStateToProps() {
return createSelector(
- createSortedSectionSelector('settings.qualityProfiles', sortByProp('name')),
+ createSortedSectionSelector('settings.qualityProfiles', sortByName),
(qualityProfiles) => qualityProfiles
);
}
diff --git a/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js b/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js
index e1c695c42..c6c297c81 100644
--- a/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js
+++ b/frontend/src/Settings/Profiles/Release/EditReleaseProfileModalContent.js
@@ -119,7 +119,7 @@ function EditReleaseProfileModalContent(props) {
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.css b/frontend/src/Settings/Quality/Definition/QualityDefinition.css
index 860333725..e090428a1 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.css
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.css
@@ -24,19 +24,19 @@
height: 20px;
}
-.track {
+.bar {
top: 9px;
margin: 0 5px;
height: 3px;
background-color: var(--sliderAccentColor);
box-shadow: 0 0 0 #000;
- &:nth-child(3n + 1) {
+ &:nth-child(3n+1) {
background-color: #ddd;
}
}
-.thumb {
+.handle {
top: 1px;
z-index: 0 !important;
width: 18px;
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts b/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts
index 9c9e8393a..2b92fb212 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.css.d.ts
@@ -1,6 +1,8 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
+ 'bar': string;
+ 'handle': string;
'kilobitsPerSecond': string;
'quality': string;
'qualityDefinition': string;
@@ -8,9 +10,7 @@ interface CssExports {
'sizeLimit': string;
'sizes': string;
'slider': string;
- 'thumb': string;
'title': string;
- 'track': string;
}
export const cssExports: CssExports;
export default cssExports;
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.js b/frontend/src/Settings/Quality/Definition/QualityDefinition.js
index 48251abfb..7d8a78737 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.js
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.js
@@ -55,27 +55,6 @@ class QualityDefinition extends Component {
};
}
- //
- // Control
-
- trackRenderer(props, state) {
- return (
-
- );
- }
-
- thumbRenderer(props, state) {
- return (
-
- );
- }
-
//
// Listeners
@@ -195,7 +174,6 @@ class QualityDefinition extends Component {
diff --git a/frontend/src/Settings/SettingsToolbarConnector.js b/frontend/src/Settings/SettingsToolbarConnector.js
index 65d937ab8..1e6f7a589 100644
--- a/frontend/src/Settings/SettingsToolbarConnector.js
+++ b/frontend/src/Settings/SettingsToolbarConnector.js
@@ -134,7 +134,6 @@ const historyShape = {
};
SettingsToolbarConnector.propTypes = {
- showSave: PropTypes.bool,
hasPendingChanges: PropTypes.bool.isRequired,
history: PropTypes.shape(historyShape).isRequired,
onSavePress: PropTypes.func,
diff --git a/frontend/src/Settings/Tags/AutoTagging/AutoTaggings.js b/frontend/src/Settings/Tags/AutoTagging/AutoTaggings.js
index 005547bb7..0d86721af 100644
--- a/frontend/src/Settings/Tags/AutoTagging/AutoTaggings.js
+++ b/frontend/src/Settings/Tags/AutoTagging/AutoTaggings.js
@@ -13,7 +13,7 @@ import {
} from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import createTagsSelector from 'Store/Selectors/createTagsSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import translate from 'Utilities/String/translate';
import AutoTagging from './AutoTagging';
import EditAutoTaggingModal from './EditAutoTaggingModal';
@@ -27,7 +27,7 @@ export default function AutoTaggings() {
isFetching,
isPopulated
} = useSelector(
- createSortedSectionSelector('settings.autoTaggings', sortByProp('name'))
+ createSortedSectionSelector('settings.autoTaggings', sortByName)
);
const tagList = useSelector(createTagsSelector());
diff --git a/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css b/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css
index d503b0af3..a197dbcd4 100644
--- a/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css
+++ b/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css
@@ -25,8 +25,3 @@
border-radius: 4px;
background-color: var(--cardCenterBackgroundColor);
}
-
-.autoTaggings {
- display: flex;
- flex-wrap: wrap;
-}
diff --git a/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css.d.ts b/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css.d.ts
index 2a7f6b41e..1339caf02 100644
--- a/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css.d.ts
+++ b/frontend/src/Settings/Tags/AutoTagging/EditAutoTaggingModalContent.css.d.ts
@@ -2,7 +2,6 @@
// Please do not change this file!
interface CssExports {
'addSpecification': string;
- 'autoTaggings': string;
'center': string;
'deleteButton': string;
'rightButtons': string;
diff --git a/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js b/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js
index 04302729b..2ab1e4a1c 100644
--- a/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js
+++ b/frontend/src/Settings/Tags/AutoTagging/Specifications/EditSpecificationModalContent.js
@@ -86,10 +86,10 @@ function EditSpecificationModalContent(props) {
-
+
-
+
}
diff --git a/frontend/src/Settings/Tags/TagsConnector.js b/frontend/src/Settings/Tags/TagsConnector.js
index 15f31d3c5..9f8bdee5b 100644
--- a/frontend/src/Settings/Tags/TagsConnector.js
+++ b/frontend/src/Settings/Tags/TagsConnector.js
@@ -4,13 +4,11 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchDelayProfiles, fetchDownloadClients, fetchImportLists, fetchIndexers, fetchNotifications, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
import { fetchTagDetails, fetchTags } from 'Store/Actions/tagActions';
-import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
import Tags from './Tags';
function createMapStateToProps() {
return createSelector(
- createSortedSectionSelector('tags', sortByProp('label')),
+ (state) => state.tags,
(tags) => {
const isFetching = tags.isFetching || tags.details.isFetching;
const error = tags.error || tags.details.error;
diff --git a/frontend/src/Store/Actions/Creators/createRemoveItemHandler.js b/frontend/src/Store/Actions/Creators/createRemoveItemHandler.js
index 3de794bdf..dfe29ace8 100644
--- a/frontend/src/Store/Actions/Creators/createRemoveItemHandler.js
+++ b/frontend/src/Store/Actions/Creators/createRemoveItemHandler.js
@@ -7,7 +7,7 @@ function createRemoveItemHandler(section, url) {
return function(getState, payload, dispatch) {
const {
id,
- queryParams
+ ...queryParams
} = payload;
dispatch(set({ section, isDeleting: true }));
diff --git a/frontend/src/Store/Actions/Settings/customFormats.js b/frontend/src/Store/Actions/Settings/customFormats.js
index 3b8a209f9..4a175abea 100644
--- a/frontend/src/Store/Actions/Settings/customFormats.js
+++ b/frontend/src/Store/Actions/Settings/customFormats.js
@@ -1,12 +1,7 @@
import { createAction } from 'redux-actions';
-import { sortDirections } from 'Helpers/Props';
-import createBulkEditItemHandler from 'Store/Actions/Creators/createBulkEditItemHandler';
-import createBulkRemoveItemHandler from 'Store/Actions/Creators/createBulkRemoveItemHandler';
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
-import createSetClientSideCollectionSortReducer
- from 'Store/Actions/Creators/Reducers/createSetClientSideCollectionSortReducer';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks';
import getSectionState from 'Utilities/State/getSectionState';
@@ -26,9 +21,6 @@ export const SAVE_CUSTOM_FORMAT = 'settings/customFormats/saveCustomFormat';
export const DELETE_CUSTOM_FORMAT = 'settings/customFormats/deleteCustomFormat';
export const SET_CUSTOM_FORMAT_VALUE = 'settings/customFormats/setCustomFormatValue';
export const CLONE_CUSTOM_FORMAT = 'settings/customFormats/cloneCustomFormat';
-export const BULK_EDIT_CUSTOM_FORMATS = 'settings/downloadClients/bulkEditCustomFormats';
-export const BULK_DELETE_CUSTOM_FORMATS = 'settings/downloadClients/bulkDeleteCustomFormats';
-export const SET_MANAGE_CUSTOM_FORMATS_SORT = 'settings/downloadClients/setManageCustomFormatsSort';
//
// Action Creators
@@ -36,9 +28,6 @@ export const SET_MANAGE_CUSTOM_FORMATS_SORT = 'settings/downloadClients/setManag
export const fetchCustomFormats = createThunk(FETCH_CUSTOM_FORMATS);
export const saveCustomFormat = createThunk(SAVE_CUSTOM_FORMAT);
export const deleteCustomFormat = createThunk(DELETE_CUSTOM_FORMAT);
-export const bulkEditCustomFormats = createThunk(BULK_EDIT_CUSTOM_FORMATS);
-export const bulkDeleteCustomFormats = createThunk(BULK_DELETE_CUSTOM_FORMATS);
-export const setManageCustomFormatsSort = createAction(SET_MANAGE_CUSTOM_FORMATS_SORT);
export const setCustomFormatValue = createAction(SET_CUSTOM_FORMAT_VALUE, (payload) => {
return {
@@ -58,30 +47,20 @@ export default {
// State
defaultState: {
- isFetching: false,
- isPopulated: false,
- error: null,
- isSaving: false,
- saveError: null,
- isDeleting: false,
- deleteError: null,
- items: [],
- pendingChanges: {},
-
isSchemaFetching: false,
isSchemaPopulated: false,
- schemaError: null,
+ isFetching: false,
+ isPopulated: false,
schema: {
includeCustomFormatWhenRenaming: false
},
-
- sortKey: 'name',
- sortDirection: sortDirections.ASCENDING,
- sortPredicates: {
- name: ({ name }) => {
- return name.toLocaleLowerCase();
- }
- }
+ error: null,
+ isDeleting: false,
+ deleteError: null,
+ isSaving: false,
+ saveError: null,
+ items: [],
+ pendingChanges: {}
},
//
@@ -103,10 +82,7 @@ export default {
}));
createSaveProviderHandler(section, '/customformat')(getState, payload, dispatch);
- },
-
- [BULK_EDIT_CUSTOM_FORMATS]: createBulkEditItemHandler(section, '/customformat/bulk'),
- [BULK_DELETE_CUSTOM_FORMATS]: createBulkRemoveItemHandler(section, '/customformat/bulk')
+ }
},
//
@@ -126,9 +102,7 @@ export default {
newState.pendingChanges = pendingChanges;
return updateSectionState(state, section, newState);
- },
-
- [SET_MANAGE_CUSTOM_FORMATS_SORT]: createSetClientSideCollectionSortReducer(section)
+ }
}
};
diff --git a/frontend/src/Store/Actions/Settings/downloadClients.js b/frontend/src/Store/Actions/Settings/downloadClients.js
index 1113e7daf..aee945ef5 100644
--- a/frontend/src/Store/Actions/Settings/downloadClients.js
+++ b/frontend/src/Store/Actions/Settings/downloadClients.js
@@ -96,8 +96,8 @@ export default {
sortKey: 'name',
sortDirection: sortDirections.ASCENDING,
sortPredicates: {
- name: ({ name }) => {
- return name.toLocaleLowerCase();
+ name: function(item) {
+ return item.name.toLowerCase();
}
}
},
diff --git a/frontend/src/Store/Actions/Settings/indexers.js b/frontend/src/Store/Actions/Settings/indexers.js
index 511a2e475..1e9aded2f 100644
--- a/frontend/src/Store/Actions/Settings/indexers.js
+++ b/frontend/src/Store/Actions/Settings/indexers.js
@@ -100,8 +100,8 @@ export default {
sortKey: 'name',
sortDirection: sortDirections.ASCENDING,
sortPredicates: {
- name: ({ name }) => {
- return name.toLocaleLowerCase();
+ name: function(item) {
+ return item.name.toLowerCase();
}
}
},
diff --git a/frontend/src/Store/Actions/albumSelectionActions.js b/frontend/src/Store/Actions/albumSelectionActions.js
deleted file mode 100644
index f19f5b691..000000000
--- a/frontend/src/Store/Actions/albumSelectionActions.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import moment from 'moment';
-import { createAction } from 'redux-actions';
-import { sortDirections } from 'Helpers/Props';
-import { createThunk, handleThunks } from 'Store/thunks';
-import updateSectionState from 'Utilities/State/updateSectionState';
-import createFetchHandler from './Creators/createFetchHandler';
-import createHandleActions from './Creators/createHandleActions';
-import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
-
-//
-// Variables
-
-export const section = 'albumSelection';
-
-//
-// State
-
-export const defaultState = {
- isFetching: false,
- isReprocessing: false,
- isPopulated: false,
- error: null,
- sortKey: 'title',
- sortDirection: sortDirections.ASCENDING,
- items: [],
- sortPredicates: {
- title: ({ title }) => {
- return title.toLocaleLowerCase();
- },
-
- releaseDate: function({ releaseDate }, direction) {
- if (releaseDate) {
- return moment(releaseDate).unix();
- }
-
- if (direction === sortDirections.DESCENDING) {
- return 0;
- }
-
- return Number.MAX_VALUE;
- }
- }
-};
-
-export const persistState = [
- 'albumSelection.sortKey',
- 'albumSelection.sortDirection'
-];
-
-//
-// Actions Types
-
-export const FETCH_ALBUMS = 'albumSelection/fetchAlbums';
-export const SET_ALBUMS_SORT = 'albumSelection/setAlbumsSort';
-export const CLEAR_ALBUMS = 'albumSelection/clearAlbums';
-
-//
-// Action Creators
-
-export const fetchAlbums = createThunk(FETCH_ALBUMS);
-export const setAlbumsSort = createAction(SET_ALBUMS_SORT);
-export const clearAlbums = createAction(CLEAR_ALBUMS);
-
-//
-// Action Handlers
-
-export const actionHandlers = handleThunks({
- [FETCH_ALBUMS]: createFetchHandler(section, '/album')
-});
-
-//
-// Reducers
-
-export const reducers = createHandleActions({
-
- [SET_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(section),
-
- [CLEAR_ALBUMS]: (state) => {
- return updateSectionState(state, section, {
- ...defaultState,
- sortKey: state.sortKey,
- sortDirection: state.sortDirection
- });
- }
-
-}, defaultState, section);
diff --git a/frontend/src/Store/Actions/artistIndexActions.js b/frontend/src/Store/Actions/artistIndexActions.js
index 736502460..72cb20142 100644
--- a/frontend/src/Store/Actions/artistIndexActions.js
+++ b/frontend/src/Store/Actions/artistIndexActions.js
@@ -1,6 +1,6 @@
import { createAction } from 'redux-actions';
import { filterBuilderTypes, filterBuilderValueTypes, filterTypePredicates, sortDirections } from 'Helpers/Props';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
import translate from 'Utilities/String/translate';
import { filterPredicates, filters, sortPredicates } from './artistActions';
import createHandleActions from './Creators/createHandleActions';
@@ -151,7 +151,7 @@ export const defaultState = {
{
name: 'genres',
label: () => translate('Genres'),
- isSortable: true,
+ isSortable: false,
isVisible: false
},
{
@@ -334,7 +334,7 @@ export const defaultState = {
return acc;
}, []);
- return tagList.sort(sortByProp('name'));
+ return tagList.sort(sortByName);
}
},
{
diff --git a/frontend/src/Store/Actions/historyActions.js b/frontend/src/Store/Actions/historyActions.js
index 9d16d29c4..225698229 100644
--- a/frontend/src/Store/Actions/historyActions.js
+++ b/frontend/src/Store/Actions/historyActions.js
@@ -150,7 +150,7 @@ export const defaultState = {
},
{
key: 'importFailed',
- label: () => translate('ImportCompleteFailed'),
+ label: () => translate('ImportFailed'),
filters: [
{
key: 'eventType',
diff --git a/frontend/src/Store/Actions/index.js b/frontend/src/Store/Actions/index.js
index 85fda482b..95b02d089 100644
--- a/frontend/src/Store/Actions/index.js
+++ b/frontend/src/Store/Actions/index.js
@@ -1,6 +1,5 @@
import * as albums from './albumActions';
import * as albumHistory from './albumHistoryActions';
-import * as albumSelection from './albumSelectionActions';
import * as app from './appActions';
import * as artist from './artistActions';
import * as artistHistory from './artistHistoryActions';
@@ -14,7 +13,6 @@ import * as history from './historyActions';
import * as interactiveImportActions from './interactiveImportActions';
import * as oAuth from './oAuthActions';
import * as organizePreview from './organizePreviewActions';
-import * as parse from './parseActions';
import * as paths from './pathActions';
import * as providerOptions from './providerOptionActions';
import * as queue from './queueActions';
@@ -30,28 +28,26 @@ import * as wanted from './wantedActions';
export default [
app,
- albums,
- albumHistory,
- albumSelection,
- artist,
- artistHistory,
- artistIndex,
blocklist,
captcha,
calendar,
commands,
customFilters,
+ albums,
trackFiles,
+ albumHistory,
history,
interactiveImportActions,
oAuth,
organizePreview,
retagPreview,
- parse,
paths,
providerOptions,
queue,
releases,
+ artist,
+ artistHistory,
+ artistIndex,
search,
settings,
system,
diff --git a/frontend/src/Store/Actions/interactiveImportActions.js b/frontend/src/Store/Actions/interactiveImportActions.js
index a250292c5..d174f443b 100644
--- a/frontend/src/Store/Actions/interactiveImportActions.js
+++ b/frontend/src/Store/Actions/interactiveImportActions.js
@@ -16,6 +16,7 @@ import createSetClientSideCollectionSortReducer from './Creators/Reducers/create
export const section = 'interactiveImport';
+const albumsSection = `${section}.albums`;
const trackFilesSection = `${section}.trackFiles`;
let abortCurrentFetchRequest = null;
let abortCurrentRequest = null;
@@ -57,6 +58,15 @@ export const defaultState = {
}
},
+ albums: {
+ isFetching: false,
+ isPopulated: false,
+ error: null,
+ sortKey: 'albumTitle',
+ sortDirection: sortDirections.ASCENDING,
+ items: []
+ },
+
trackFiles: {
isFetching: false,
isPopulated: false,
@@ -87,6 +97,10 @@ export const ADD_RECENT_FOLDER = 'interactiveImport/addRecentFolder';
export const REMOVE_RECENT_FOLDER = 'interactiveImport/removeRecentFolder';
export const SET_INTERACTIVE_IMPORT_MODE = 'interactiveImport/setInteractiveImportMode';
+export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/fetchInteractiveImportAlbums';
+export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'interactiveImport/clearInteractiveImportAlbumsSort';
+export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/clearInteractiveImportAlbums';
+
export const FETCH_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/fetchInteractiveImportTrackFiles';
export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/clearInteractiveImportTrackFiles';
@@ -103,6 +117,10 @@ export const addRecentFolder = createAction(ADD_RECENT_FOLDER);
export const removeRecentFolder = createAction(REMOVE_RECENT_FOLDER);
export const setInteractiveImportMode = createAction(SET_INTERACTIVE_IMPORT_MODE);
+export const fetchInteractiveImportAlbums = createThunk(FETCH_INTERACTIVE_IMPORT_ALBUMS);
+export const setInteractiveImportAlbumsSort = createAction(SET_INTERACTIVE_IMPORT_ALBUMS_SORT);
+export const clearInteractiveImportAlbums = createAction(CLEAR_INTERACTIVE_IMPORT_ALBUMS);
+
export const fetchInteractiveImportTrackFiles = createThunk(FETCH_INTERACTIVE_IMPORT_TRACKFILES);
export const clearInteractiveImportTrackFiles = createAction(CLEAR_INTERACTIVE_IMPORT_TRACKFILES);
@@ -235,6 +253,8 @@ export const actionHandlers = handleThunks({
});
},
+ [FETCH_INTERACTIVE_IMPORT_ALBUMS]: createFetchHandler(albumsSection, '/album'),
+
[FETCH_INTERACTIVE_IMPORT_TRACKFILES]: createFetchHandler(trackFilesSection, '/trackFile')
});
@@ -316,6 +336,14 @@ export const reducers = createHandleActions({
return Object.assign({}, state, { importMode: payload.importMode });
},
+ [SET_INTERACTIVE_IMPORT_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(albumsSection),
+
+ [CLEAR_INTERACTIVE_IMPORT_ALBUMS]: (state) => {
+ return updateSectionState(state, albumsSection, {
+ ...defaultState.albums
+ });
+ },
+
[CLEAR_INTERACTIVE_IMPORT_TRACKFILES]: (state) => {
return updateSectionState(state, trackFilesSection, {
...defaultState.trackFiles
diff --git a/frontend/src/Store/Actions/parseActions.ts b/frontend/src/Store/Actions/parseActions.ts
deleted file mode 100644
index d4b6e9bcb..000000000
--- a/frontend/src/Store/Actions/parseActions.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import { Dispatch } from 'redux';
-import { createAction } from 'redux-actions';
-import { batchActions } from 'redux-batched-actions';
-import AppState from 'App/State/AppState';
-import { createThunk, handleThunks } from 'Store/thunks';
-import createAjaxRequest from 'Utilities/createAjaxRequest';
-import { set, update } from './baseActions';
-import createHandleActions from './Creators/createHandleActions';
-import createClearReducer from './Creators/Reducers/createClearReducer';
-
-interface FetchPayload {
- title: string;
-}
-
-//
-// Variables
-
-export const section = 'parse';
-let parseTimeout: number | null = null;
-let abortCurrentRequest: (() => void) | null = null;
-
-//
-// State
-
-export const defaultState = {
- isFetching: false,
- isPopulated: false,
- error: null,
- item: {},
-};
-
-//
-// Actions Types
-
-export const FETCH = 'parse/fetch';
-export const CLEAR = 'parse/clear';
-
-//
-// Action Creators
-
-export const fetch = createThunk(FETCH);
-export const clear = createAction(CLEAR);
-
-//
-// Action Handlers
-
-export const actionHandlers = handleThunks({
- [FETCH]: function (
- _getState: () => AppState,
- payload: FetchPayload,
- dispatch: Dispatch
- ) {
- if (parseTimeout) {
- clearTimeout(parseTimeout);
- }
-
- parseTimeout = window.setTimeout(async () => {
- dispatch(set({ section, isFetching: true }));
-
- if (abortCurrentRequest) {
- abortCurrentRequest();
- }
-
- const { request, abortRequest } = createAjaxRequest({
- url: '/parse',
- data: {
- title: payload.title,
- },
- });
-
- try {
- const data = await request;
-
- dispatch(
- batchActions([
- update({ section, data }),
-
- set({
- section,
- isFetching: false,
- isPopulated: true,
- error: null,
- }),
- ])
- );
- } catch (error) {
- dispatch(
- set({
- section,
- isAdding: false,
- isAdded: false,
- addError: error,
- })
- );
- }
-
- abortCurrentRequest = abortRequest;
- }, 300);
- },
-});
-
-//
-// Reducers
-
-export const reducers = createHandleActions(
- {
- [CLEAR]: createClearReducer(section, defaultState),
- },
- defaultState,
- section
-);
diff --git a/frontend/src/Store/Actions/releaseActions.js b/frontend/src/Store/Actions/releaseActions.js
index c4955c915..1c9b6f5ef 100644
--- a/frontend/src/Store/Actions/releaseActions.js
+++ b/frontend/src/Store/Actions/releaseActions.js
@@ -219,9 +219,8 @@ export const defaultState = {
};
export const persistState = [
- 'releases.album.selectedFilterKey',
+ 'releases.selectedFilterKey',
'releases.album.customFilters',
- 'releases.artist.selectedFilterKey',
'releases.artist.customFilters'
];
diff --git a/frontend/src/Store/Actions/wantedActions.js b/frontend/src/Store/Actions/wantedActions.js
index 61d6f7752..35aa162d4 100644
--- a/frontend/src/Store/Actions/wantedActions.js
+++ b/frontend/src/Store/Actions/wantedActions.js
@@ -52,12 +52,6 @@ export const defaultState = {
isSortable: true,
isVisible: true
},
- {
- name: 'albums.lastSearchTime',
- label: () => translate('LastSearched'),
- isSortable: true,
- isVisible: false
- },
// {
// name: 'status',
// label: 'Status',
@@ -137,12 +131,6 @@ export const defaultState = {
// label: 'Status',
// isVisible: true
// },
- {
- name: 'albums.lastSearchTime',
- label: () => translate('LastSearched'),
- isSortable: true,
- isVisible: false
- },
{
name: 'actions',
columnLabel: () => translate('Actions'),
diff --git a/frontend/src/Store/Selectors/createArtistAlbumsSelector.ts b/frontend/src/Store/Selectors/createArtistAlbumsSelector.ts
index 414a451f5..2ae54a10c 100644
--- a/frontend/src/Store/Selectors/createArtistAlbumsSelector.ts
+++ b/frontend/src/Store/Selectors/createArtistAlbumsSelector.ts
@@ -1,5 +1,4 @@
import { createSelector } from 'reselect';
-import AlbumAppState from 'App/State/AlbumAppState';
import AppState from 'App/State/AppState';
import Artist from 'Artist/Artist';
import { createArtistSelectorForHook } from './createArtistSelector';
@@ -8,11 +7,11 @@ function createArtistAlbumsSelector(artistId: number) {
return createSelector(
(state: AppState) => state.albums,
createArtistSelectorForHook(artistId),
- (albums: AlbumAppState, artist = {} as Artist) => {
+ (albums, artist = {} as Artist) => {
const { isFetching, isPopulated, error, items } = albums;
const filteredAlbums = items.filter(
- (album) => album.artistId === artist.id
+ (album) => album.artist.artistMetadataId === artist.artistMetadataId
);
return {
diff --git a/frontend/src/Store/Selectors/createArtistMetadataProfileSelector.ts b/frontend/src/Store/Selectors/createArtistMetadataProfileSelector.ts
index fa60d936d..0acbd3997 100644
--- a/frontend/src/Store/Selectors/createArtistMetadataProfileSelector.ts
+++ b/frontend/src/Store/Selectors/createArtistMetadataProfileSelector.ts
@@ -1,14 +1,13 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import Artist from 'Artist/Artist';
-import MetadataProfile from 'typings/MetadataProfile';
import { createArtistSelectorForHook } from './createArtistSelector';
function createArtistMetadataProfileSelector(artistId: number) {
return createSelector(
(state: AppState) => state.settings.metadataProfiles.items,
createArtistSelectorForHook(artistId),
- (metadataProfiles: MetadataProfile[], artist = {} as Artist) => {
+ (metadataProfiles, artist = {} as Artist) => {
return metadataProfiles.find((profile) => {
return profile.id === artist.metadataProfileId;
});
diff --git a/frontend/src/Store/Selectors/createArtistQualityProfileSelector.ts b/frontend/src/Store/Selectors/createArtistQualityProfileSelector.ts
index 67639919b..99325276f 100644
--- a/frontend/src/Store/Selectors/createArtistQualityProfileSelector.ts
+++ b/frontend/src/Store/Selectors/createArtistQualityProfileSelector.ts
@@ -1,14 +1,13 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import Artist from 'Artist/Artist';
-import QualityProfile from 'typings/QualityProfile';
import { createArtistSelectorForHook } from './createArtistSelector';
function createArtistQualityProfileSelector(artistId: number) {
return createSelector(
(state: AppState) => state.settings.qualityProfiles.items,
createArtistSelectorForHook(artistId),
- (qualityProfiles: QualityProfile[], artist = {} as Artist) => {
+ (qualityProfiles, artist = {} as Artist) => {
return qualityProfiles.find(
(profile) => profile.id === artist.qualityProfileId
);
diff --git a/frontend/src/Store/Selectors/createRootFoldersSelector.ts b/frontend/src/Store/Selectors/createRootFoldersSelector.ts
index 432f9056d..a016d7665 100644
--- a/frontend/src/Store/Selectors/createRootFoldersSelector.ts
+++ b/frontend/src/Store/Selectors/createRootFoldersSelector.ts
@@ -1,15 +1,11 @@
import { createSelector } from 'reselect';
import { RootFolderAppState } from 'App/State/SettingsAppState';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
-import RootFolder from 'typings/RootFolder';
-import sortByProp from 'Utilities/Array/sortByProp';
+import sortByName from 'Utilities/Array/sortByName';
export default function createRootFoldersSelector() {
return createSelector(
- createSortedSectionSelector(
- 'settings.rootFolders',
- sortByProp('name')
- ),
+ createSortedSectionSelector('settings.rootFolders', sortByName),
(rootFolders: RootFolderAppState) => rootFolders
);
}
diff --git a/frontend/src/Store/Selectors/createSortedSectionSelector.ts b/frontend/src/Store/Selectors/createSortedSectionSelector.js
similarity index 68%
rename from frontend/src/Store/Selectors/createSortedSectionSelector.ts
rename to frontend/src/Store/Selectors/createSortedSectionSelector.js
index abee01f75..331d890c9 100644
--- a/frontend/src/Store/Selectors/createSortedSectionSelector.ts
+++ b/frontend/src/Store/Selectors/createSortedSectionSelector.js
@@ -1,18 +1,14 @@
import { createSelector } from 'reselect';
import getSectionState from 'Utilities/State/getSectionState';
-function createSortedSectionSelector(
- section: string,
- comparer: (a: T, b: T) => number
-) {
+function createSortedSectionSelector(section, comparer) {
return createSelector(
(state) => state,
(state) => {
const sectionState = getSectionState(state, section, true);
-
return {
...sectionState,
- items: [...sectionState.items].sort(comparer),
+ items: [...sectionState.items].sort(comparer)
};
}
);
diff --git a/frontend/src/System/Logs/Files/LogFiles.js b/frontend/src/System/Logs/Files/LogFiles.js
index 5339a8590..83736c617 100644
--- a/frontend/src/System/Logs/Files/LogFiles.js
+++ b/frontend/src/System/Logs/Files/LogFiles.js
@@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
+import Link from 'Components/Link/Link';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
-import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@@ -77,16 +77,15 @@ class LogFiles extends Component {
- {translate('LogFilesLocation', {
- location
- })}
+ Log files are located in: {location}
- {currentLogView === 'Log Files' ? (
-
-
-
- ) : null}
+ {
+ currentLogView === 'Log Files' &&
+
+ The log level defaults to 'Info' and can be changed in General Settings
+
+ }
{
diff --git a/frontend/src/System/Tasks/Queued/QueuedTaskRowNameCell.tsx b/frontend/src/System/Tasks/Queued/QueuedTaskRowNameCell.tsx
index 41a307d5f..77142c5a3 100644
--- a/frontend/src/System/Tasks/Queued/QueuedTaskRowNameCell.tsx
+++ b/frontend/src/System/Tasks/Queued/QueuedTaskRowNameCell.tsx
@@ -3,7 +3,6 @@ import { useSelector } from 'react-redux';
import { CommandBody } from 'Commands/Command';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import createMultiArtistsSelector from 'Store/Selectors/createMultiArtistsSelector';
-import sortByProp from 'Utilities/Array/sortByProp';
import translate from 'Utilities/String/translate';
import styles from './QueuedTaskRowNameCell.css';
@@ -40,7 +39,9 @@ export default function QueuedTaskRowNameCell(
}
const artists = useSelector(createMultiArtistsSelector(movieIds));
- const sortedArtists = artists.sort(sortByProp('sortName'));
+ const sortedArtists = artists.sort((a, b) =>
+ a.sortName.localeCompare(b.sortName)
+ );
return (
diff --git a/frontend/src/System/Updates/UpdateChanges.js b/frontend/src/System/Updates/UpdateChanges.js
new file mode 100644
index 000000000..3588069a0
--- /dev/null
+++ b/frontend/src/System/Updates/UpdateChanges.js
@@ -0,0 +1,46 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
+import styles from './UpdateChanges.css';
+
+class UpdateChanges extends Component {
+
+ //
+ // Render
+
+ render() {
+ const {
+ title,
+ changes
+ } = this.props;
+
+ if (changes.length === 0) {
+ return null;
+ }
+
+ return (
+
+
{title}
+
+ {
+ changes.map((change, index) => {
+ return (
+ -
+
+
+ );
+ })
+ }
+
+
+ );
+ }
+
+}
+
+UpdateChanges.propTypes = {
+ title: PropTypes.string.isRequired,
+ changes: PropTypes.arrayOf(PropTypes.string)
+};
+
+export default UpdateChanges;
diff --git a/frontend/src/System/Updates/UpdateChanges.tsx b/frontend/src/System/Updates/UpdateChanges.tsx
deleted file mode 100644
index 3e5ba1c9b..000000000
--- a/frontend/src/System/Updates/UpdateChanges.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
-import styles from './UpdateChanges.css';
-
-interface UpdateChangesProps {
- title: string;
- changes: string[];
-}
-
-function UpdateChanges(props: UpdateChangesProps) {
- const { title, changes } = props;
-
- if (changes.length === 0) {
- return null;
- }
-
- const uniqueChanges = [...new Set(changes)];
-
- return (
-
-
{title}
-
- {uniqueChanges.map((change, index) => {
- const checkChange = change.replace(
- /#\d{4,5}\b/g,
- (match) =>
- `[${match}](https://github.com/Lidarr/Lidarr/issues/${match.substring(
- 1
- )})`
- );
-
- return (
- -
-
-
- );
- })}
-
-
- );
-}
-
-export default UpdateChanges;
diff --git a/frontend/src/System/Updates/Updates.js b/frontend/src/System/Updates/Updates.js
new file mode 100644
index 000000000..528441cbe
--- /dev/null
+++ b/frontend/src/System/Updates/Updates.js
@@ -0,0 +1,249 @@
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+import React, { Component, Fragment } from 'react';
+import Alert from 'Components/Alert';
+import Icon from 'Components/Icon';
+import Label from 'Components/Label';
+import SpinnerButton from 'Components/Link/SpinnerButton';
+import LoadingIndicator from 'Components/Loading/LoadingIndicator';
+import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
+import PageContent from 'Components/Page/PageContent';
+import PageContentBody from 'Components/Page/PageContentBody';
+import { icons, kinds } from 'Helpers/Props';
+import formatDate from 'Utilities/Date/formatDate';
+import formatDateTime from 'Utilities/Date/formatDateTime';
+import translate from 'Utilities/String/translate';
+import UpdateChanges from './UpdateChanges';
+import styles from './Updates.css';
+
+class Updates extends Component {
+
+ //
+ // Render
+
+ render() {
+ const {
+ currentVersion,
+ isFetching,
+ isPopulated,
+ updatesError,
+ generalSettingsError,
+ items,
+ isInstallingUpdate,
+ updateMechanism,
+ updateMechanismMessage,
+ shortDateFormat,
+ longDateFormat,
+ timeFormat,
+ onInstallLatestPress
+ } = this.props;
+
+ const hasError = !!(updatesError || generalSettingsError);
+ const hasUpdates = isPopulated && !hasError && items.length > 0;
+ const noUpdates = isPopulated && !hasError && !items.length;
+ const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true });
+ const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
+
+ const externalUpdaterPrefix = 'Unable to update Lidarr directly,';
+ const externalUpdaterMessages = {
+ external: 'Lidarr is configured to use an external update mechanism',
+ apt: 'use apt to install the update',
+ docker: 'update the docker container to receive the update'
+ };
+
+ return (
+
+
+ {
+ !isPopulated && !hasError &&
+
+ }
+
+ {
+ noUpdates &&
+
+ {translate('NoUpdatesAreAvailable')}
+
+ }
+
+ {
+ hasUpdateToInstall &&
+
+ {
+ updateMechanism === 'builtIn' || updateMechanism === 'script' ?
+
+ Install Latest
+ :
+
+
+
+
+
+ {externalUpdaterPrefix}
+
+
+ }
+
+ {
+ isFetching &&
+
+ }
+
+ }
+
+ {
+ noUpdateToInstall &&
+
+
+
+ The latest version of Lidarr is already installed
+
+
+ {
+ isFetching &&
+
+ }
+
+ }
+
+ {
+ hasUpdates &&
+
+ {
+ items.map((update) => {
+ const hasChanges = !!update.changes;
+
+ return (
+
+
+
{update.version}
+
—
+
+ {formatDate(update.releaseDate, shortDateFormat)}
+
+
+ {
+ update.branch === 'master' ?
+ null :
+
+ }
+
+ {
+ update.version === currentVersion ?
+
:
+ null
+ }
+
+ {
+ update.version !== currentVersion && update.installedOn ?
+
:
+ null
+ }
+
+
+ {
+ !hasChanges &&
+
+ {translate('MaintenanceRelease')}
+
+ }
+
+ {
+ hasChanges &&
+
+
+
+
+
+ }
+
+ );
+ })
+ }
+
+ }
+
+ {
+ !!updatesError &&
+
+ Failed to fetch updates
+
+ }
+
+ {
+ !!generalSettingsError &&
+
+ Failed to update settings
+
+ }
+
+
+ );
+ }
+
+}
+
+Updates.propTypes = {
+ currentVersion: PropTypes.string.isRequired,
+ isFetching: PropTypes.bool.isRequired,
+ isPopulated: PropTypes.bool.isRequired,
+ updatesError: PropTypes.object,
+ generalSettingsError: PropTypes.object,
+ items: PropTypes.array.isRequired,
+ isInstallingUpdate: PropTypes.bool.isRequired,
+ updateMechanism: PropTypes.string,
+ updateMechanismMessage: PropTypes.string,
+ shortDateFormat: PropTypes.string.isRequired,
+ longDateFormat: PropTypes.string.isRequired,
+ timeFormat: PropTypes.string.isRequired,
+ onInstallLatestPress: PropTypes.func.isRequired
+};
+
+export default Updates;
diff --git a/frontend/src/System/Updates/Updates.tsx b/frontend/src/System/Updates/Updates.tsx
deleted file mode 100644
index 300ab1f99..000000000
--- a/frontend/src/System/Updates/Updates.tsx
+++ /dev/null
@@ -1,303 +0,0 @@
-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 * as commandNames from 'Commands/commandNames';
-import Alert from 'Components/Alert';
-import Icon from 'Components/Icon';
-import Label from 'Components/Label';
-import SpinnerButton from 'Components/Link/SpinnerButton';
-import LoadingIndicator from 'Components/Loading/LoadingIndicator';
-import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
-import ConfirmModal from 'Components/Modal/ConfirmModal';
-import PageContent from 'Components/Page/PageContent';
-import PageContentBody from 'Components/Page/PageContentBody';
-import { icons, kinds } from 'Helpers/Props';
-import { executeCommand } from 'Store/Actions/commandActions';
-import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
-import { fetchUpdates } from 'Store/Actions/systemActions';
-import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
-import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
-import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
-import { UpdateMechanism } from 'typings/Settings/General';
-import formatDate from 'Utilities/Date/formatDate';
-import formatDateTime from 'Utilities/Date/formatDateTime';
-import translate from 'Utilities/String/translate';
-import UpdateChanges from './UpdateChanges';
-import styles from './Updates.css';
-
-const VERSION_REGEX = /\d+\.\d+\.\d+\.\d+/i;
-
-function createUpdatesSelector() {
- return createSelector(
- (state: AppState) => state.system.updates,
- (state: AppState) => state.settings.general,
- (updates, generalSettings) => {
- const { error: updatesError, items } = updates;
-
- const isFetching = updates.isFetching || generalSettings.isFetching;
- const isPopulated = updates.isPopulated && generalSettings.isPopulated;
-
- return {
- isFetching,
- isPopulated,
- updatesError,
- generalSettingsError: generalSettings.error,
- items,
- updateMechanism: generalSettings.item.updateMechanism,
- };
- }
- );
-}
-
-function Updates() {
- const currentVersion = useSelector((state: AppState) => state.app.version);
- const { packageUpdateMechanismMessage } = useSelector(
- createSystemStatusSelector()
- );
- const { shortDateFormat, longDateFormat, timeFormat } = useSelector(
- createUISettingsSelector()
- );
- const isInstallingUpdate = useSelector(
- createCommandExecutingSelector(commandNames.APPLICATION_UPDATE)
- );
-
- const {
- isFetching,
- isPopulated,
- updatesError,
- generalSettingsError,
- items,
- updateMechanism,
- } = useSelector(createUpdatesSelector());
-
- const dispatch = useDispatch();
- const [isMajorUpdateModalOpen, setIsMajorUpdateModalOpen] = useState(false);
- const hasError = !!(updatesError || generalSettingsError);
- const hasUpdates = isPopulated && !hasError && items.length > 0;
- const noUpdates = isPopulated && !hasError && !items.length;
-
- const externalUpdaterPrefix = translate('UpdateAppDirectlyLoadError');
- const externalUpdaterMessages: Partial> = {
- external: translate('ExternalUpdater'),
- apt: translate('AptUpdater'),
- docker: translate('DockerUpdater'),
- };
-
- const { isMajorUpdate, hasUpdateToInstall } = useMemo(() => {
- const majorVersion = parseInt(
- currentVersion.match(VERSION_REGEX)?.[0] ?? '0'
- );
-
- const latestVersion = items[0]?.version;
- const latestMajorVersion = parseInt(
- latestVersion?.match(VERSION_REGEX)?.[0] ?? '0'
- );
-
- return {
- isMajorUpdate: latestMajorVersion > majorVersion,
- hasUpdateToInstall: items.some(
- (update) => update.installable && update.latest
- ),
- };
- }, [currentVersion, items]);
-
- const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
-
- const handleInstallLatestPress = useCallback(() => {
- if (isMajorUpdate) {
- setIsMajorUpdateModalOpen(true);
- } else {
- dispatch(executeCommand({ name: commandNames.APPLICATION_UPDATE }));
- }
- }, [isMajorUpdate, setIsMajorUpdateModalOpen, dispatch]);
-
- const handleInstallLatestMajorVersionPress = useCallback(() => {
- setIsMajorUpdateModalOpen(false);
-
- dispatch(
- executeCommand({
- name: commandNames.APPLICATION_UPDATE,
- installMajorUpdate: true,
- })
- );
- }, [setIsMajorUpdateModalOpen, dispatch]);
-
- const handleCancelMajorVersionPress = useCallback(() => {
- setIsMajorUpdateModalOpen(false);
- }, [setIsMajorUpdateModalOpen]);
-
- useEffect(() => {
- dispatch(fetchUpdates());
- dispatch(fetchGeneralSettings());
- }, [dispatch]);
-
- return (
-
-
- {isPopulated || hasError ? null : }
-
- {noUpdates ? (
- {translate('NoUpdatesAreAvailable')}
- ) : null}
-
- {hasUpdateToInstall ? (
-
- {updateMechanism === 'builtIn' || updateMechanism === 'script' ? (
-
- {translate('InstallLatest')}
-
- ) : (
- <>
-
-
-
- {externalUpdaterPrefix}{' '}
-
-
- >
- )}
-
- {isFetching ? (
-
- ) : null}
-
- ) : null}
-
- {noUpdateToInstall && (
-
-
-
{translate('OnLatestVersion')}
-
- {isFetching && (
-
- )}
-
- )}
-
- {hasUpdates && (
-
- {items.map((update) => {
- return (
-
-
-
{update.version}
-
—
-
- {formatDate(update.releaseDate, shortDateFormat)}
-
-
- {update.branch === 'master' ? null : (
-
- )}
-
- {update.version === currentVersion ? (
-
- ) : null}
-
- {update.version !== currentVersion && update.installedOn ? (
-
- ) : null}
-
-
- {update.changes ? (
-
-
-
-
-
- ) : (
-
{translate('MaintenanceRelease')}
- )}
-
- );
- })}
-
- )}
-
- {updatesError ? (
-
- {translate('FailedToFetchUpdates')}
-
- ) : null}
-
- {generalSettingsError ? (
-
- {translate('FailedToFetchSettings')}
-
- ) : null}
-
-
- {translate('InstallMajorVersionUpdateMessage')}
-
-
-
-
- }
- confirmLabel={translate('Install')}
- onConfirm={handleInstallLatestMajorVersionPress}
- onCancel={handleCancelMajorVersionPress}
- />
-
-
- );
-}
-
-export default Updates;
diff --git a/frontend/src/System/Updates/UpdatesConnector.js b/frontend/src/System/Updates/UpdatesConnector.js
new file mode 100644
index 000000000..77d75dbda
--- /dev/null
+++ b/frontend/src/System/Updates/UpdatesConnector.js
@@ -0,0 +1,98 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
+import * as commandNames from 'Commands/commandNames';
+import { executeCommand } from 'Store/Actions/commandActions';
+import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
+import { fetchUpdates } from 'Store/Actions/systemActions';
+import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
+import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
+import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
+import Updates from './Updates';
+
+function createMapStateToProps() {
+ return createSelector(
+ (state) => state.app.version,
+ createSystemStatusSelector(),
+ (state) => state.system.updates,
+ (state) => state.settings.general,
+ createUISettingsSelector(),
+ createCommandExecutingSelector(commandNames.APPLICATION_UPDATE),
+ (
+ currentVersion,
+ status,
+ updates,
+ generalSettings,
+ uiSettings,
+ isInstallingUpdate
+ ) => {
+ const {
+ error: updatesError,
+ items
+ } = updates;
+
+ const isFetching = updates.isFetching || generalSettings.isFetching;
+ const isPopulated = updates.isPopulated && generalSettings.isPopulated;
+
+ return {
+ currentVersion,
+ isFetching,
+ isPopulated,
+ updatesError,
+ generalSettingsError: generalSettings.error,
+ items,
+ isInstallingUpdate,
+ updateMechanism: generalSettings.item.updateMechanism,
+ updateMechanismMessage: status.packageUpdateMechanismMessage,
+ shortDateFormat: uiSettings.shortDateFormat,
+ longDateFormat: uiSettings.longDateFormat,
+ timeFormat: uiSettings.timeFormat
+ };
+ }
+ );
+}
+
+const mapDispatchToProps = {
+ dispatchFetchUpdates: fetchUpdates,
+ dispatchFetchGeneralSettings: fetchGeneralSettings,
+ dispatchExecuteCommand: executeCommand
+};
+
+class UpdatesConnector extends Component {
+
+ //
+ // Lifecycle
+
+ componentDidMount() {
+ this.props.dispatchFetchUpdates();
+ this.props.dispatchFetchGeneralSettings();
+ }
+
+ //
+ // Listeners
+
+ onInstallLatestPress = () => {
+ this.props.dispatchExecuteCommand({ name: commandNames.APPLICATION_UPDATE });
+ };
+
+ //
+ // Render
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+UpdatesConnector.propTypes = {
+ dispatchFetchUpdates: PropTypes.func.isRequired,
+ dispatchFetchGeneralSettings: PropTypes.func.isRequired,
+ dispatchExecuteCommand: PropTypes.func.isRequired
+};
+
+export default connect(createMapStateToProps, mapDispatchToProps)(UpdatesConnector);
diff --git a/frontend/src/Utilities/Array/sortByName.js b/frontend/src/Utilities/Array/sortByName.js
new file mode 100644
index 000000000..1956d3bac
--- /dev/null
+++ b/frontend/src/Utilities/Array/sortByName.js
@@ -0,0 +1,5 @@
+function sortByName(a, b) {
+ return a.name.localeCompare(b.name);
+}
+
+export default sortByName;
diff --git a/frontend/src/Utilities/Array/sortByProp.ts b/frontend/src/Utilities/Array/sortByProp.ts
deleted file mode 100644
index 8fbde08c9..000000000
--- a/frontend/src/Utilities/Array/sortByProp.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { StringKey } from 'typings/Helpers/KeysMatching';
-
-export function sortByProp<
- // eslint-disable-next-line no-use-before-define
- T extends Record,
- K extends StringKey
->(sortKey: K) {
- return (a: T, b: T) => {
- return a[sortKey].localeCompare(b[sortKey], undefined, { numeric: true });
- };
-}
-
-export default sortByProp;
diff --git a/frontend/src/Utilities/Date/getRelativeDate.ts b/frontend/src/Utilities/Date/getRelativeDate.js
similarity index 65%
rename from frontend/src/Utilities/Date/getRelativeDate.ts
rename to frontend/src/Utilities/Date/getRelativeDate.js
index 178d14fb7..812064272 100644
--- a/frontend/src/Utilities/Date/getRelativeDate.ts
+++ b/frontend/src/Utilities/Date/getRelativeDate.js
@@ -6,33 +6,15 @@ import isTomorrow from 'Utilities/Date/isTomorrow';
import isYesterday from 'Utilities/Date/isYesterday';
import translate from 'Utilities/String/translate';
-interface GetRelativeDateOptions {
- timeFormat?: string;
- includeSeconds?: boolean;
- timeForToday?: boolean;
-}
-
-function getRelativeDate(
- date: string | undefined,
- shortDateFormat: string,
- showRelativeDates: boolean,
- {
- timeFormat,
- includeSeconds = false,
- timeForToday = false,
- }: GetRelativeDateOptions = {}
-) {
+function getRelativeDate(date, shortDateFormat, showRelativeDates, { timeFormat, includeSeconds = false, timeForToday = false } = {}) {
if (!date) {
- return '';
+ return null;
}
const isTodayDate = isToday(date);
if (isTodayDate && timeForToday && timeFormat) {
- return formatTime(date, timeFormat, {
- includeMinuteZero: true,
- includeSeconds,
- });
+ return formatTime(date, timeFormat, { includeMinuteZero: true, includeSeconds });
}
if (!showRelativeDates) {
diff --git a/frontend/src/Utilities/String/translate.ts b/frontend/src/Utilities/String/translate.ts
index 5571ef6b0..98a0418ad 100644
--- a/frontend/src/Utilities/String/translate.ts
+++ b/frontend/src/Utilities/String/translate.ts
@@ -17,7 +17,7 @@ export async function fetchTranslations(): Promise {
translations = data.strings;
resolve(true);
- } catch {
+ } catch (error) {
resolve(false);
}
});
@@ -27,12 +27,6 @@ export default function translate(
key: string,
tokens: Record = {}
) {
- const { isProduction = true } = window.Lidarr;
-
- if (!isProduction && !(key in translations)) {
- console.warn(`Missing translation for key: ${key}`);
- }
-
const translation = translations[key] || key;
tokens.appName = 'Lidarr';
diff --git a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js
index 1dd9870d1..dbb4f2235 100644
--- a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js
+++ b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetConnector.js
@@ -131,15 +131,13 @@ class CutoffUnmetConnector extends Component {
onSearchSelectedPress = (selected) => {
this.props.executeCommand({
name: commandNames.ALBUM_SEARCH,
- albumIds: selected,
- commandFinished: this.repopulate
+ albumIds: selected
});
};
onSearchAllCutoffUnmetPress = () => {
this.props.executeCommand({
- name: commandNames.CUTOFF_UNMET_ALBUM_SEARCH,
- commandFinished: this.repopulate
+ name: commandNames.CUTOFF_UNMET_ALBUM_SEARCH
});
};
diff --git a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js
index 452e2947a..785b9b1c1 100644
--- a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js
+++ b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js
@@ -20,7 +20,6 @@ function CutoffUnmetRow(props) {
foreignAlbumId,
albumType,
title,
- lastSearchTime,
disambiguation,
isSelected,
columns,
@@ -90,15 +89,6 @@ function CutoffUnmetRow(props) {
);
}
- if (name === 'albums.lastSearchTime') {
- return (
-
- );
- }
-
if (name === 'status') {
return (
{
this.props.executeCommand({
name: commandNames.ALBUM_SEARCH,
- albumIds: selected,
- commandFinished: this.repopulate
+ albumIds: selected
});
};
onSearchAllMissingPress = () => {
this.props.executeCommand({
- name: commandNames.MISSING_ALBUM_SEARCH,
- commandFinished: this.repopulate
+ name: commandNames.MISSING_ALBUM_SEARCH
});
};
diff --git a/frontend/src/Wanted/Missing/MissingRow.js b/frontend/src/Wanted/Missing/MissingRow.js
index 6c0b5a0c6..0eb1a0452 100644
--- a/frontend/src/Wanted/Missing/MissingRow.js
+++ b/frontend/src/Wanted/Missing/MissingRow.js
@@ -17,7 +17,6 @@ function MissingRow(props) {
albumType,
foreignAlbumId,
title,
- lastSearchTime,
disambiguation,
isSelected,
columns,
@@ -87,15 +86,6 @@ function MissingRow(props) {
);
}
- if (name === 'albums.lastSearchTime') {
- return (
-
- );
- }
-
if (name === 'actions') {
return (
);
+ render(
+ ,
+ document.getElementById('root')
+ );
}
diff --git a/frontend/src/index.ejs b/frontend/src/index.ejs
index a893149d5..5968082b4 100644
--- a/frontend/src/index.ejs
+++ b/frontend/src/index.ejs
@@ -33,7 +33,7 @@
sizes="16x16"
href="/Content/Images/Icons/favicon-16x16.png"
/>
-
+
diff --git a/frontend/src/index.ts b/frontend/src/index.ts
index 37e780919..36aed4c4b 100644
--- a/frontend/src/index.ts
+++ b/frontend/src/index.ts
@@ -14,32 +14,6 @@ window.Lidarr = await response.json();
__webpack_public_path__ = `${window.Lidarr.urlBase}/`;
/* eslint-enable no-undef, @typescript-eslint/ban-ts-comment */
-const error = console.error;
-
-// Monkey patch console.error to filter out some warnings from React
-// TODO: Remove this after the great TypeScript migration
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function logError(...parameters: any[]) {
- const filter = parameters.find((parameter) => {
- return (
- typeof parameter === 'string' &&
- (parameter.includes(
- 'Support for defaultProps will be removed from function components in a future major release'
- ) ||
- parameter.includes(
- 'findDOMNode is deprecated and will be removed in the next major release'
- ))
- );
- });
-
- if (!filter) {
- error(...parameters);
- }
-}
-
-console.error = logError;
-
const { bootstrap } = await import('./bootstrap');
await bootstrap();
diff --git a/frontend/src/login.html b/frontend/src/login.html
index 24d086959..113dc547b 100644
--- a/frontend/src/login.html
+++ b/frontend/src/login.html
@@ -11,11 +11,8 @@
-
-
+
+
@@ -36,11 +33,7 @@
sizes="16x16"
href="/Content/Images/Icons/favicon-16x16.png"
/>
-
+
-
+
@@ -63,7 +59,7 @@
body {
background-color: var(--pageBackground);
color: var(--textColor);
- font-family: 'Roboto', 'open sans', 'Helvetica Neue', Helvetica, Arial,
+ font-family: "Roboto", "open sans", "Helvetica Neue", Helvetica, Arial,
sans-serif;
}
@@ -213,7 +209,9 @@
-
SIGN IN TO CONTINUE
+
+ SIGN IN TO CONTINUE
+
@@ -284,16 +282,16 @@