diff --git a/frontend/src/Components/Form/EnhancedSelectInput.js b/frontend/src/Components/Form/EnhancedSelectInput.js index 8de415dba..7ffc22142 100644 --- a/frontend/src/Components/Form/EnhancedSelectInput.js +++ b/frontend/src/Components/Form/EnhancedSelectInput.js @@ -479,6 +479,7 @@ class EnhancedSelectInput extends Component { -
+ -
{value}
+
+
{value}
- { - hint != null && -
- {hint} -
- } -
-
+ { + hint != null && +
+ {hint} +
+ } +
+ + + { + dividerAfter ? +
: + null + } +
); } @@ -50,15 +59,18 @@ HintedSelectInputOption.propTypes = { id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, value: PropTypes.string.isRequired, hint: PropTypes.node, + name: PropTypes.string, depth: PropTypes.number, isSelected: PropTypes.bool.isRequired, isDisabled: PropTypes.bool.isRequired, + dividerAfter: PropTypes.bool.isRequired, isMultiSelect: PropTypes.bool.isRequired, isMobile: PropTypes.bool.isRequired }; HintedSelectInputOption.defaultProps = { isDisabled: false, + dividerAfter: false, isHidden: false, isMultiSelect: false }; diff --git a/frontend/src/Components/Form/LanguageSelectInputConnector.js b/frontend/src/Components/Form/LanguageSelectInputConnector.js new file mode 100644 index 000000000..dd3a52017 --- /dev/null +++ b/frontend/src/Components/Form/LanguageSelectInputConnector.js @@ -0,0 +1,52 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import EnhancedSelectInput from './EnhancedSelectInput'; + +function createMapStateToProps() { + return createSelector( + (state, { values }) => values, + ( languages ) => { + + const minId = languages.reduce((min, v) => (v.key < 1 ? v.key : min), languages[0].key); + + const values = languages.map(({ key, value }) => { + return { + key, + value, + dividerAfter: minId < 1 ? key === minId : false + }; + }); + + return { + values + }; + } + ); +} + +class LanguageSelectInputConnector extends Component { + + // + // Render + + render() { + + return ( + + ); + } +} + +LanguageSelectInputConnector.propTypes = { + name: PropTypes.string.isRequired, + value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]).isRequired, + values: PropTypes.arrayOf(PropTypes.object).isRequired, + onChange: PropTypes.func.isRequired +}; + +export default connect(createMapStateToProps)(LanguageSelectInputConnector); diff --git a/frontend/src/Components/Form/ProviderFieldFormGroup.js b/frontend/src/Components/Form/ProviderFieldFormGroup.js index f5130d18b..81b1c589b 100644 --- a/frontend/src/Components/Form/ProviderFieldFormGroup.js +++ b/frontend/src/Components/Form/ProviderFieldFormGroup.js @@ -49,6 +49,7 @@ function getSelectValues(selectOptions) { result.push({ key: option.value, value: option.name, + dividerAfter: option.dividerAfter, hint: option.hint }); diff --git a/frontend/src/Helpers/Props/inputTypes.js b/frontend/src/Helpers/Props/inputTypes.js index e3a225073..cd4ef89d9 100644 --- a/frontend/src/Helpers/Props/inputTypes.js +++ b/frontend/src/Helpers/Props/inputTypes.js @@ -12,6 +12,7 @@ export const PATH = 'path'; export const QUALITY_PROFILE_SELECT = 'qualityProfileSelect'; export const ROOT_FOLDER_SELECT = 'rootFolderSelect'; export const INDEXER_FLAGS_SELECT = 'indexerFlagsSelect'; +export const LANGUAGE_SELECT = 'languageSelect'; export const SELECT = 'select'; export const DYNAMIC_SELECT = 'dynamicSelect'; export const TAG = 'tag'; @@ -36,6 +37,7 @@ export const all = [ QUALITY_PROFILE_SELECT, ROOT_FOLDER_SELECT, INDEXER_FLAGS_SELECT, + LANGUAGE_SELECT, SELECT, DYNAMIC_SELECT, TAG, diff --git a/frontend/src/MovieFile/Edit/FileEditModalContent.js b/frontend/src/MovieFile/Edit/FileEditModalContent.js index 02bb6ba84..496d16057 100644 --- a/frontend/src/MovieFile/Edit/FileEditModalContent.js +++ b/frontend/src/MovieFile/Edit/FileEditModalContent.js @@ -155,7 +155,7 @@ class FileEditModalContent extends Component { {translate('Languages')} {translate('MovieInfoLanguage')} {translate('UILanguage')} MultiLanguages { get; set; } [FieldDefinition(3, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your API key will be sent to that host.")] diff --git a/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs b/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs index cbde09778..65e85dfc9 100644 --- a/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs +++ b/src/NzbDrone.Core/Indexers/HDBits/HDBitsSettings.cs @@ -37,7 +37,7 @@ namespace NzbDrone.Core.Indexers.HDBits [FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)] public string Username { get; set; } - [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)] diff --git a/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs b/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs index 4c9d4e70e..92af14a47 100644 --- a/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs +++ b/src/NzbDrone.Core/Indexers/IPTorrents/IPTorrentsSettings.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Core.Indexers.IPTorrents [FieldDefinition(0, Label = "Feed URL", HelpText = "The full RSS feed url generated by IPTorrents, using only the categories you selected (HD, SD, x264, etc ...)")] public string BaseUrl { get; set; } - [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(2, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] diff --git a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs index 3e2efa2aa..4f1e110b3 100644 --- a/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs +++ b/src/NzbDrone.Core/Indexers/Newznab/NewznabSettings.cs @@ -69,7 +69,7 @@ namespace NzbDrone.Core.Indexers.Newznab [FieldDefinition(1, Label = "API Path", HelpText = "Path to the api, usually /api", Advanced = true)] public string ApiPath { get; set; } - [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)] diff --git a/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs b/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs index 856bb557e..21a006db5 100644 --- a/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs +++ b/src/NzbDrone.Core/Indexers/Nyaa/NyaaSettings.cs @@ -35,7 +35,7 @@ namespace NzbDrone.Core.Indexers.Nyaa [FieldDefinition(0, Label = "Website URL")] public string BaseUrl { get; set; } - [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(2, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")] diff --git a/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs b/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs index 3e137cf37..22fb1f0b1 100644 --- a/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs +++ b/src/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsSettings.cs @@ -38,7 +38,7 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs [FieldDefinition(2, Label = "Delay", HelpText = "Time in minutes to delay new nzbs before they appear on the RSS feed", Advanced = true)] public int Delay { get; set; } - [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornSettings.cs b/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornSettings.cs index 66163519c..be6b88874 100644 --- a/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornSettings.cs +++ b/src/NzbDrone.Core/Indexers/PassThePopcorn/PassThePopcornSettings.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn [FieldDefinition(2, Label = "APIKey", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string APIKey { get; set; } - [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(4, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] diff --git a/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs b/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs index 8f344eb79..d1cf94ba9 100644 --- a/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs +++ b/src/NzbDrone.Core/Indexers/Rarbg/RarbgSettings.cs @@ -54,7 +54,7 @@ namespace NzbDrone.Core.Indexers.Rarbg [FieldDefinition(2, Type = FieldType.Captcha, Label = "CAPTCHA Token", HelpText = "CAPTCHA Clearance token used to handle CloudFlare Anti-DDOS measures on shared-ip VPNs.")] public string CaptchaToken { get; set; } - [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(4, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] diff --git a/src/NzbDrone.Core/Indexers/TorrentPotato/TorrentPotatoSettings.cs b/src/NzbDrone.Core/Indexers/TorrentPotato/TorrentPotatoSettings.cs index 688ca5c5f..787dbcc0c 100644 --- a/src/NzbDrone.Core/Indexers/TorrentPotato/TorrentPotatoSettings.cs +++ b/src/NzbDrone.Core/Indexers/TorrentPotato/TorrentPotatoSettings.cs @@ -38,7 +38,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato [FieldDefinition(2, Label = "Passkey", HelpText = "The password you use at your Indexer.", Privacy = PrivacyLevel.Password)] public string Passkey { get; set; } - [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(4, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] diff --git a/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs b/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs index 8f30a00a9..117af271a 100644 --- a/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs +++ b/src/NzbDrone.Core/Indexers/TorrentRss/TorrentRssIndexerSettings.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Indexers.TorrentRss [FieldDefinition(2, Type = FieldType.Checkbox, Label = "Allow Zero Size", HelpText = "Enabling this will allow you to use feeds that don't specify release size, but be careful, size related checks will not be performed.")] public bool AllowZeroSize { get; set; } - [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] + [FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(RealLanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)] public IEnumerable MultiLanguages { get; set; } [FieldDefinition(4, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)] diff --git a/src/NzbDrone.Core/Languages/LanguageFieldConverter.cs b/src/NzbDrone.Core/Languages/LanguageFieldConverter.cs index 801a251ac..dc439b434 100644 --- a/src/NzbDrone.Core/Languages/LanguageFieldConverter.cs +++ b/src/NzbDrone.Core/Languages/LanguageFieldConverter.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using NzbDrone.Core.Annotations; namespace NzbDrone.Core.Languages @@ -7,7 +8,10 @@ namespace NzbDrone.Core.Languages { public List GetSelectOptions() { - return Language.All.ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name }); + return Language.All + .OrderBy(l => l.Id > 0).ThenBy(l => l.Name) + .ToList() + .ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name, DividerAfter = v.Id == Language.Unknown.Id }); } } } diff --git a/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs b/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs index 12c5df289..44e0096a2 100644 --- a/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs +++ b/src/NzbDrone.Core/Languages/RealLanguageFieldConverter.cs @@ -10,8 +10,9 @@ namespace NzbDrone.Core.Languages { return Language.All .Where(l => l != Language.Unknown && l != Language.Any) + .OrderBy(l => l.Id > 0).ThenBy(l => l.Name) .ToList() - .ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name }); + .ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name, DividerAfter = v.Id == Language.Original.Id }); } } } diff --git a/src/Radarr.Api.V3/Profiles/Languages/LanguageModule.cs b/src/Radarr.Api.V3/Profiles/Languages/LanguageModule.cs index de5d0a87d..9e929b121 100644 --- a/src/Radarr.Api.V3/Profiles/Languages/LanguageModule.cs +++ b/src/Radarr.Api.V3/Profiles/Languages/LanguageModule.cs @@ -26,13 +26,15 @@ namespace Radarr.Api.V3.Profiles.Languages private List GetAll() { - return Language.All.Select(l => new LanguageResource + var languageResources = Language.All.Select(l => new LanguageResource { Id = (int)l, Name = l.ToString() }) - .OrderBy(l => l.Name) + .OrderBy(l => l.Id > 0).ThenBy(l => l.Name) .ToList(); + + return languageResources; } } }