1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-11-24 08:42:19 +02:00

New: Custom Format Language Condition

This commit is contained in:
Qstick 2022-08-14 14:07:26 -05:00
parent 52760e0908
commit 89b0b04e08
243 changed files with 1345 additions and 3956 deletions

View File

@ -1,4 +1,4 @@
.language,
.languages,
.quality {
composes: cell from '~Components/Table/Cells/TableRowCell.css';

View File

@ -6,7 +6,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import EpisodeFormats from 'Episode/EpisodeFormats';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import { icons, kinds } from 'Helpers/Props';
import SeriesTitleLink from 'Series/SeriesTitleLink';
@ -45,7 +45,7 @@ class BlocklistRow extends Component {
id,
series,
sourceTitle,
language,
languages,
quality,
customFormats,
date,
@ -96,14 +96,14 @@ class BlocklistRow extends Component {
);
}
if (name === 'language') {
if (name === 'languages') {
return (
<TableRowCell
key={name}
className={styles.language}
className={styles.languages}
>
<EpisodeLanguage
language={language}
<EpisodeLanguages
languages={languages}
/>
</TableRowCell>
);
@ -195,7 +195,7 @@ BlocklistRow.propTypes = {
id: PropTypes.number.isRequired,
series: PropTypes.object.isRequired,
sourceTitle: PropTypes.string.isRequired,
language: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
date: PropTypes.string.isRequired,

View File

@ -6,7 +6,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import episodeEntities from 'Episode/episodeEntities';
import EpisodeFormats from 'Episode/EpisodeFormats';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
@ -59,7 +59,7 @@ class HistoryRow extends Component {
episodeId,
series,
episode,
language,
languages,
languageCutoffNotMet,
quality,
customFormats,
@ -144,11 +144,11 @@ class HistoryRow extends Component {
);
}
if (name === 'language') {
if (name === 'languages') {
return (
<TableRowCell key={name}>
<EpisodeLanguage
language={language}
<EpisodeLanguages
languages={languages}
isCutoffMet={languageCutoffNotMet}
/>
</TableRowCell>
@ -278,7 +278,7 @@ HistoryRow.propTypes = {
episodeId: PropTypes.number,
series: PropTypes.object.isRequired,
episode: PropTypes.object,
language: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
languageCutoffNotMet: PropTypes.bool.isRequired,
quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),

View File

@ -9,7 +9,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import EpisodeFormats from 'Episode/EpisodeFormats';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
@ -88,7 +88,7 @@ class QueueRow extends Component {
errorMessage,
series,
episode,
language,
languages,
quality,
customFormats,
protocol,
@ -225,11 +225,11 @@ class QueueRow extends Component {
);
}
if (name === 'language') {
if (name === 'languages') {
return (
<TableRowCell key={name}>
<EpisodeLanguage
language={language}
<EpisodeLanguages
languages={languages}
/>
</TableRowCell>
);
@ -410,7 +410,7 @@ QueueRow.propTypes = {
errorMessage: PropTypes.string,
series: PropTypes.object,
episode: PropTypes.object,
language: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
protocol: PropTypes.string.isRequired,

View File

@ -57,12 +57,6 @@
composes: button from '~Components/Link/SpinnerButton.css';
}
.hideLanguageProfile {
composes: group from '~Components/Form/FormGroup.css';
display: none;
}
@media only screen and (max-width: $breakpointSmall) {
.modalFooter {
display: block;

View File

@ -47,10 +47,6 @@ class AddNewSeriesModalContent extends Component {
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
};
onLanguageProfileIdChange = ({ value }) => {
this.props.onInputChange({ name: 'languageProfileId', value: parseInt(value) });
};
onAddSeriesPress = () => {
const {
seriesType
@ -74,14 +70,12 @@ class AddNewSeriesModalContent extends Component {
rootFolderPath,
monitor,
qualityProfileId,
languageProfileId,
seriesType,
seasonFolder,
searchForMissingEpisodes,
searchForCutoffUnmetEpisodes,
folder,
tags,
showLanguageProfile,
isSmallScreen,
isWindows,
onModalClose,
@ -180,17 +174,6 @@ class AddNewSeriesModalContent extends Component {
/>
</FormGroup>
<FormGroup className={showLanguageProfile ? undefined : styles.hideLanguageProfile}>
<FormLabel>Language Profile</FormLabel>
<FormInputGroup
type={inputTypes.LANGUAGE_PROFILE_SELECT}
name="languageProfileId"
onChange={this.onLanguageProfileIdChange}
{...languageProfileId}
/>
</FormGroup>
<FormGroup>
<FormLabel>
Series Type
@ -299,14 +282,12 @@ AddNewSeriesModalContent.propTypes = {
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileId: PropTypes.object,
languageProfileId: PropTypes.object,
seriesType: PropTypes.object.isRequired,
seasonFolder: PropTypes.object.isRequired,
searchForMissingEpisodes: PropTypes.object.isRequired,
searchForCutoffUnmetEpisodes: PropTypes.object.isRequired,
folder: PropTypes.string.isRequired,
tags: PropTypes.object.isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
isWindows: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired,

View File

@ -11,10 +11,9 @@ import AddNewSeriesModalContent from './AddNewSeriesModalContent';
function createMapStateToProps() {
return createSelector(
(state) => state.addSeries,
(state) => state.settings.languageProfiles,
createDimensionsSelector(),
createSystemStatusSelector(),
(addSeriesState, languageProfiles, dimensions, systemStatus) => {
(addSeriesState, dimensions, systemStatus) => {
const {
isAdding,
addError,
@ -30,7 +29,6 @@ function createMapStateToProps() {
return {
isAdding,
addError,
showLanguageProfile: languageProfiles.items.length > 1,
isSmallScreen: dimensions.isSmallScreen,
validationErrors,
validationWarnings,
@ -61,7 +59,6 @@ class AddNewSeriesModalContentConnector extends Component {
rootFolderPath,
monitor,
qualityProfileId,
languageProfileId,
seasonFolder,
searchForMissingEpisodes,
searchForCutoffUnmetEpisodes,
@ -73,7 +70,6 @@ class AddNewSeriesModalContentConnector extends Component {
rootFolderPath: rootFolderPath.value,
monitor: monitor.value,
qualityProfileId: qualityProfileId.value,
languageProfileId: languageProfileId.value,
seriesType,
seasonFolder: seasonFolder.value,
searchForMissingEpisodes: searchForMissingEpisodes.value,
@ -101,7 +97,6 @@ AddNewSeriesModalContentConnector.propTypes = {
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileId: PropTypes.object,
languageProfileId: PropTypes.object,
seriesType: PropTypes.object.isRequired,
seasonFolder: PropTypes.object.isRequired,
searchForMissingEpisodes: PropTypes.object.isRequired,

View File

@ -84,8 +84,7 @@ class ImportSeries extends Component {
rootFoldersFetching,
rootFoldersPopulated,
rootFoldersError,
unmappedFolders,
showLanguageProfile
unmappedFolders
} = this.props;
const {
@ -135,7 +134,6 @@ class ImportSeries extends Component {
allUnselected={allUnselected}
selectedState={selectedState}
scroller={scroller}
showLanguageProfile={showLanguageProfile}
onSelectAllChange={this.onSelectAllChange}
onSelectedChange={this.onSelectedChange}
onRemoveSelectedStateItem={this.onRemoveSelectedStateItem}
@ -150,7 +148,6 @@ class ImportSeries extends Component {
!!unmappedFolders.length ?
<ImportSeriesFooterConnector
selectedIds={this.getSelectedIds()}
showLanguageProfile={showLanguageProfile}
onInputChange={this.onInputChange}
onImportPress={this.onImportPress}
/> :
@ -169,7 +166,6 @@ ImportSeries.propTypes = {
rootFoldersError: PropTypes.object,
unmappedFolders: PropTypes.arrayOf(PropTypes.object),
items: PropTypes.arrayOf(PropTypes.object),
showLanguageProfile: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
onImportPress: PropTypes.func.isRequired
};

View File

@ -16,14 +16,12 @@ function createMapStateToProps() {
(state) => state.addSeries,
(state) => state.importSeries,
(state) => state.settings.qualityProfiles,
(state) => state.settings.languageProfiles,
(
match,
rootFolders,
addSeries,
importSeriesState,
qualityProfiles,
languageProfiles
qualityProfiles
) => {
const {
isFetching: rootFoldersFetching,
@ -40,10 +38,7 @@ function createMapStateToProps() {
rootFoldersPopulated,
rootFoldersError,
qualityProfiles: qualityProfiles.items,
languageProfiles: languageProfiles.items,
showLanguageProfile: languageProfiles.items.length > 1,
defaultQualityProfileId: addSeries.defaults.qualityProfileId,
defaultLanguageProfileId: addSeries.defaults.languageProfileId
defaultQualityProfileId: addSeries.defaults.qualityProfileId
};
if (items.length) {
@ -78,9 +73,7 @@ class ImportSeriesConnector extends Component {
const {
rootFolderId,
qualityProfiles,
languageProfiles,
defaultQualityProfileId,
defaultLanguageProfileId,
dispatchFetchRootFolders,
dispatchSetAddSeriesDefault
} = this.props;
@ -98,14 +91,6 @@ class ImportSeriesConnector extends Component {
setDefaultPayload.qualityProfileId = qualityProfiles[0].id;
}
if (
!defaultLanguageProfileId ||
!languageProfiles.some((p) => p.id === defaultLanguageProfileId)
) {
setDefaults = true;
setDefaultPayload.languageProfileId = languageProfiles[0].id;
}
if (setDefaults) {
dispatchSetAddSeriesDefault(setDefaultPayload);
}
@ -157,9 +142,7 @@ ImportSeriesConnector.propTypes = {
rootFoldersFetching: PropTypes.bool.isRequired,
rootFoldersPopulated: PropTypes.bool.isRequired,
qualityProfiles: PropTypes.arrayOf(PropTypes.object).isRequired,
languageProfiles: PropTypes.arrayOf(PropTypes.object).isRequired,
defaultQualityProfileId: PropTypes.number.isRequired,
defaultLanguageProfileId: PropTypes.number.isRequired,
dispatchSetImportSeriesValue: PropTypes.func.isRequired,
dispatchImportSeries: PropTypes.func.isRequired,
dispatchClearImportSeries: PropTypes.func.isRequired,

View File

@ -25,7 +25,6 @@ class ImportSeriesFooter extends Component {
const {
defaultMonitor,
defaultQualityProfileId,
defaultLanguageProfileId,
defaultSeasonFolder,
defaultSeriesType
} = props;
@ -33,7 +32,6 @@ class ImportSeriesFooter extends Component {
this.state = {
monitor: defaultMonitor,
qualityProfileId: defaultQualityProfileId,
languageProfileId: defaultLanguageProfileId,
seriesType: defaultSeriesType,
seasonFolder: defaultSeasonFolder
};
@ -43,12 +41,10 @@ class ImportSeriesFooter extends Component {
const {
defaultMonitor,
defaultQualityProfileId,
defaultLanguageProfileId,
defaultSeriesType,
defaultSeasonFolder,
isMonitorMixed,
isQualityProfileIdMixed,
isLanguageProfileIdMixed,
isSeriesTypeMixed,
isSeasonFolderMixed
} = this.props;
@ -56,7 +52,6 @@ class ImportSeriesFooter extends Component {
const {
monitor,
qualityProfileId,
languageProfileId,
seriesType,
seasonFolder
} = this.state;
@ -75,12 +70,6 @@ class ImportSeriesFooter extends Component {
newState.qualityProfileId = defaultQualityProfileId;
}
if (isLanguageProfileIdMixed && languageProfileId !== MIXED) {
newState.languageProfileId = MIXED;
} else if (!isLanguageProfileIdMixed && languageProfileId !== defaultLanguageProfileId) {
newState.languageProfileId = defaultLanguageProfileId;
}
if (isSeriesTypeMixed && seriesType !== MIXED) {
newState.seriesType = MIXED;
} else if (!isSeriesTypeMixed && seriesType !== defaultSeriesType) {
@ -116,10 +105,8 @@ class ImportSeriesFooter extends Component {
isLookingUpSeries,
isMonitorMixed,
isQualityProfileIdMixed,
isLanguageProfileIdMixed,
isSeriesTypeMixed,
hasUnsearchedItems,
showLanguageProfile,
importError,
onImportPress,
onLookupPress,
@ -129,7 +116,6 @@ class ImportSeriesFooter extends Component {
const {
monitor,
qualityProfileId,
languageProfileId,
seriesType,
seasonFolder
} = this.state;
@ -166,24 +152,6 @@ class ImportSeriesFooter extends Component {
/>
</div>
{
showLanguageProfile &&
<div className={styles.inputContainer}>
<div className={styles.label}>
Language Profile
</div>
<FormInputGroup
type={inputTypes.LANGUAGE_PROFILE_SELECT}
name="languageProfileId"
value={languageProfileId}
isDisabled={!selectedCount}
includeMixed={isLanguageProfileIdMixed}
onChange={this.onInputChange}
/>
</div>
}
<div className={styles.inputContainer}>
<div className={styles.label}>
Series Type
@ -308,16 +276,13 @@ ImportSeriesFooter.propTypes = {
isLookingUpSeries: PropTypes.bool.isRequired,
defaultMonitor: PropTypes.string.isRequired,
defaultQualityProfileId: PropTypes.number,
defaultLanguageProfileId: PropTypes.number,
defaultSeriesType: PropTypes.string.isRequired,
defaultSeasonFolder: PropTypes.bool.isRequired,
isMonitorMixed: PropTypes.bool.isRequired,
isQualityProfileIdMixed: PropTypes.bool.isRequired,
isLanguageProfileIdMixed: PropTypes.bool.isRequired,
isSeriesTypeMixed: PropTypes.bool.isRequired,
isSeasonFolderMixed: PropTypes.bool.isRequired,
hasUnsearchedItems: PropTypes.bool.isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
importError: PropTypes.object,
onInputChange: PropTypes.func.isRequired,
onImportPress: PropTypes.func.isRequired,

View File

@ -19,7 +19,6 @@ function createMapStateToProps() {
const {
monitor: defaultMonitor,
qualityProfileId: defaultQualityProfileId,
languageProfileId: defaultLanguageProfileId,
seriesType: defaultSeriesType,
seasonFolder: defaultSeasonFolder
} = addSeries.defaults;
@ -33,7 +32,6 @@ function createMapStateToProps() {
const isMonitorMixed = isMixed(items, selectedIds, defaultMonitor, 'monitor');
const isQualityProfileIdMixed = isMixed(items, selectedIds, defaultQualityProfileId, 'qualityProfileId');
const isLanguageProfileIdMixed = isMixed(items, selectedIds, defaultLanguageProfileId, 'languageProfileId');
const isSeriesTypeMixed = isMixed(items, selectedIds, defaultSeriesType, 'seriesType');
const isSeasonFolderMixed = isMixed(items, selectedIds, defaultSeasonFolder, 'seasonFolder');
const hasUnsearchedItems = !isLookingUpSeries && items.some((item) => !item.isPopulated);
@ -44,12 +42,10 @@ function createMapStateToProps() {
isImporting,
defaultMonitor,
defaultQualityProfileId,
defaultLanguageProfileId,
defaultSeriesType,
defaultSeasonFolder,
isMonitorMixed,
isQualityProfileIdMixed,
isLanguageProfileIdMixed,
isSeriesTypeMixed,
isSeasonFolderMixed,
importError,

View File

@ -11,8 +11,7 @@
min-width: 185px;
}
.qualityProfile,
.languageProfile {
.qualityProfile {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 1 250px;

View File

@ -12,7 +12,6 @@ import styles from './ImportSeriesHeader.css';
function ImportSeriesHeader(props) {
const {
showLanguageProfile,
allSelected,
allUnselected,
onSelectAllChange
@ -59,16 +58,6 @@ function ImportSeriesHeader(props) {
Quality Profile
</VirtualTableHeaderCell>
{
showLanguageProfile &&
<VirtualTableHeaderCell
className={styles.languageProfile}
name="languageProfileId"
>
Language Profile
</VirtualTableHeaderCell>
}
<VirtualTableHeaderCell
className={styles.seriesType}
name="seriesType"
@ -106,7 +95,6 @@ function ImportSeriesHeader(props) {
}
ImportSeriesHeader.propTypes = {
showLanguageProfile: PropTypes.bool.isRequired,
allSelected: PropTypes.bool.isRequired,
allUnselected: PropTypes.bool.isRequired,
onSelectAllChange: PropTypes.func.isRequired

View File

@ -16,8 +16,7 @@
min-width: 185px;
}
.qualityProfile,
.languageProfile {
.qualityProfile {
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
flex: 0 1 250px;
@ -44,9 +43,3 @@
flex: 0 1 400px;
min-width: 300px;
}
.hideLanguageProfile {
composes: cell from '~Components/Table/Cells/VirtualTableRowCell.css';
display: none;
}

View File

@ -12,12 +12,10 @@ function ImportSeriesRow(props) {
id,
monitor,
qualityProfileId,
languageProfileId,
seasonFolder,
seriesType,
selectedSeries,
isExistingSeries,
showLanguageProfile,
isSelected,
onSelectedChange,
onInputChange
@ -55,17 +53,6 @@ function ImportSeriesRow(props) {
/>
</VirtualTableRowCell>
<VirtualTableRowCell
className={showLanguageProfile ? styles.languageProfile : styles.hideLanguageProfile}
>
<FormInputGroup
type={inputTypes.LANGUAGE_PROFILE_SELECT}
name="languageProfileId"
value={languageProfileId}
onChange={onInputChange}
/>
</VirtualTableRowCell>
<VirtualTableRowCell className={styles.seriesType}>
<FormInputGroup
type={inputTypes.SERIES_TYPE_SELECT}
@ -99,13 +86,11 @@ ImportSeriesRow.propTypes = {
id: PropTypes.string.isRequired,
monitor: PropTypes.string.isRequired,
qualityProfileId: PropTypes.number.isRequired,
languageProfileId: PropTypes.number.isRequired,
seriesType: PropTypes.string.isRequired,
seasonFolder: PropTypes.bool.isRequired,
selectedSeries: PropTypes.object,
isExistingSeries: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
isSelected: PropTypes.bool,
onSelectedChange: PropTypes.func.isRequired,
onInputChange: PropTypes.func.isRequired

View File

@ -16,7 +16,6 @@ class ImportSeriesTable extends Component {
unmappedFolders,
defaultMonitor,
defaultQualityProfileId,
defaultLanguageProfileId,
defaultSeriesType,
defaultSeasonFolder,
onSeriesLookup,
@ -26,7 +25,6 @@ class ImportSeriesTable extends Component {
const values = {
monitor: defaultMonitor,
qualityProfileId: defaultQualityProfileId,
languageProfileId: defaultLanguageProfileId,
seriesType: defaultSeriesType,
seasonFolder: defaultSeasonFolder
};
@ -106,7 +104,6 @@ class ImportSeriesTable extends Component {
rootFolderId,
items,
selectedState,
showLanguageProfile,
onSelectedChange
} = this.props;
@ -120,7 +117,6 @@ class ImportSeriesTable extends Component {
<ImportSeriesRowConnector
key={item.id}
rootFolderId={rootFolderId}
showLanguageProfile={showLanguageProfile}
isSelected={selectedState[item.id]}
onSelectedChange={onSelectedChange}
id={item.id}
@ -139,7 +135,6 @@ class ImportSeriesTable extends Component {
allUnselected,
isSmallScreen,
scroller,
showLanguageProfile,
selectedState,
onSelectAllChange
} = this.props;
@ -158,7 +153,6 @@ class ImportSeriesTable extends Component {
rowRenderer={this.rowRenderer}
header={
<ImportSeriesHeader
showLanguageProfile={showLanguageProfile}
allSelected={allSelected}
allUnselected={allUnselected}
onSelectAllChange={onSelectAllChange}
@ -176,7 +170,6 @@ ImportSeriesTable.propTypes = {
unmappedFolders: PropTypes.arrayOf(PropTypes.object),
defaultMonitor: PropTypes.string.isRequired,
defaultQualityProfileId: PropTypes.number,
defaultLanguageProfileId: PropTypes.number,
defaultSeriesType: PropTypes.string.isRequired,
defaultSeasonFolder: PropTypes.bool.isRequired,
allSelected: PropTypes.bool.isRequired,
@ -185,7 +178,6 @@ ImportSeriesTable.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
allSeries: PropTypes.arrayOf(PropTypes.object),
scroller: PropTypes.instanceOf(Element).isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
onSelectAllChange: PropTypes.func.isRequired,
onSelectedChange: PropTypes.func.isRequired,
onRemoveSelectedStateItem: PropTypes.func.isRequired,

View File

@ -14,7 +14,6 @@ function createMapStateToProps() {
return {
defaultMonitor: addSeries.defaults.monitor,
defaultQualityProfileId: addSeries.defaults.qualityProfileId,
defaultLanguageProfileId: addSeries.defaults.languageProfileId,
defaultSeriesType: addSeries.defaults.seriesType,
defaultSeasonFolder: addSeries.defaults.seasonFolder,
items: importSeries.items,

View File

@ -175,19 +175,6 @@ class AgendaEvent extends Component {
/>
}
{
showCutoffUnmetIcon &&
!!episodeFile &&
episodeFile.languageCutoffNotMet &&
!episodeFile.qualityCutoffNotMet &&
<Icon
className={styles.statusIcon}
name={icons.EPISODE_FILE}
kind={kinds.WARNING}
title="Language cutoff has not been met"
/>
}
{
episodeNumber === 1 && seasonNumber > 0 &&
<Icon

View File

@ -152,20 +152,6 @@ class CalendarEvent extends Component {
null
}
{
showCutoffUnmetIcon &&
!!episodeFile &&
episodeFile.languageCutoffNotMet &&
!episodeFile.qualityCutoffNotMet ?
<Icon
className={styles.statusIcon}
name={icons.EPISODE_FILE}
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
title="Language cutoff has not been met"
/> :
null
}
{
episodeNumber === 1 && seasonNumber > 0 ?
<Icon

View File

@ -47,7 +47,7 @@ function Legend(props) {
name="Cutoff Not Met"
icon={icons.EPISODE_FILE}
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
tooltip="Quality or language cutoff has not been met"
tooltip="Quality cutoff has not been met"
/>
);
}

View File

@ -7,7 +7,6 @@ import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
import LanguageProfileFilterBuilderRowValueConnector from './LanguageProfileFilterBuilderRowValueConnector';
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
import QualityFilterBuilderRowValueConnector from './QualityFilterBuilderRowValueConnector';
import QualityProfileFilterBuilderRowValueConnector from './QualityProfileFilterBuilderRowValueConnector';
@ -61,9 +60,6 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.INDEXER:
return IndexerFilterBuilderRowValueConnector;
case filterBuilderValueTypes.LANGUAGE_PROFILE:
return LanguageProfileFilterBuilderRowValueConnector;
case filterBuilderValueTypes.PROTOCOL:
return ProtocolFilterBuilderRowValue;

View File

@ -1,28 +0,0 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import FilterBuilderRowValue from './FilterBuilderRowValue';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.languageProfiles,
(languageProfiles) => {
const tagList = languageProfiles.items.map((languageProfile) => {
const {
id,
name
} = languageProfile;
return {
id,
name
};
});
return {
tagList
};
}
);
}
export default connect(createMapStateToProps)(FilterBuilderRowValue);

View File

@ -12,7 +12,6 @@ import EnhancedSelectInputConnector from './EnhancedSelectInputConnector';
import FormInputHelpText from './FormInputHelpText';
import IndexerSelectInputConnector from './IndexerSelectInputConnector';
import KeyValueListInput from './KeyValueListInput';
import LanguageProfileSelectInputConnector from './LanguageProfileSelectInputConnector';
import MonitorEpisodesSelectInput from './MonitorEpisodesSelectInput';
import NumberInput from './NumberInput';
import OAuthInputConnector from './OAuthInputConnector';
@ -64,9 +63,6 @@ function getComponent(type) {
case inputTypes.QUALITY_PROFILE_SELECT:
return QualityProfileSelectInputConnector;
case inputTypes.LANGUAGE_PROFILE_SELECT:
return LanguageProfileSelectInputConnector;
case inputTypes.INDEXER_SELECT:
return IndexerSelectInputConnector;

View File

@ -1,99 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import sortByName from 'Utilities/Array/sortByName';
import SelectInput from './SelectInput';
function createMapStateToProps() {
return createSelector(
createSortedSectionSelector('settings.languageProfiles', sortByName),
(state, { includeNoChange }) => includeNoChange,
(state, { includeMixed }) => includeMixed,
(languageProfiles, includeNoChange, includeMixed) => {
const values = _.map(languageProfiles.items, (languageProfile) => {
return {
key: languageProfile.id,
value: languageProfile.name
};
});
if (includeNoChange) {
values.unshift({
key: 'noChange',
value: 'No Change',
disabled: true
});
}
if (includeMixed) {
values.unshift({
key: 'mixed',
value: '(Mixed)',
disabled: true
});
}
return {
values
};
}
);
}
class LanguageProfileSelectInputConnector extends Component {
//
// Lifecycle
componentDidMount() {
const {
name,
value,
values
} = this.props;
if (!value || !_.some(values, (option) => parseInt(option.key) === value)) {
const firstValue = _.find(values, (option) => !isNaN(parseInt(option.key)));
if (firstValue) {
this.onChange({ name, value: firstValue.key });
}
}
}
//
// Listeners
onChange = ({ name, value }) => {
this.props.onChange({ name, value: parseInt(value) });
};
//
// Render
render() {
return (
<SelectInput
{...this.props}
onChange={this.onChange}
/>
);
}
}
LanguageProfileSelectInputConnector.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
values: PropTypes.arrayOf(PropTypes.object).isRequired,
includeNoChange: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired
};
LanguageProfileSelectInputConnector.defaultProps = {
includeNoChange: false
};
export default connect(createMapStateToProps)(LanguageProfileSelectInputConnector);

View File

@ -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 (
<EnhancedSelectInput
{...this.props}
onChange={this.props.onChange}
/>
);
}
}
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);

View File

@ -11,7 +11,6 @@ function ErrorPage(props) {
customFiltersError,
tagsError,
qualityProfilesError,
languageProfilesError,
uiSettingsError,
systemStatusError
} = props;
@ -28,8 +27,6 @@ function ErrorPage(props) {
errorMessage = getErrorMessage(tagsError, 'Failed to load tags from API');
} else if (qualityProfilesError) {
errorMessage = getErrorMessage(qualityProfilesError, 'Failed to load quality profiles from API');
} else if (languageProfilesError) {
errorMessage = getErrorMessage(languageProfilesError, 'Failed to load language profiles from API');
} else if (uiSettingsError) {
errorMessage = getErrorMessage(uiSettingsError, 'Failed to load UI settings from API');
} else if (systemStatusError) {
@ -56,7 +53,6 @@ ErrorPage.propTypes = {
customFiltersError: PropTypes.object,
tagsError: PropTypes.object,
qualityProfilesError: PropTypes.object,
languageProfilesError: PropTypes.object,
uiSettingsError: PropTypes.object,
systemStatusError: PropTypes.object
};

View File

@ -6,7 +6,7 @@ import { createSelector } from 'reselect';
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchSeries } from 'Store/Actions/seriesActions';
import { fetchImportLists, fetchLanguageProfiles, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchImportLists, fetchLanguages, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
@ -49,7 +49,7 @@ const selectIsPopulated = createSelector(
(state) => state.tags.isPopulated,
(state) => state.settings.ui.isPopulated,
(state) => state.settings.qualityProfiles.isPopulated,
(state) => state.settings.languageProfiles.isPopulated,
(state) => state.settings.languages.isPopulated,
(state) => state.settings.importLists.isPopulated,
(state) => state.system.status.isPopulated,
(
@ -58,7 +58,7 @@ const selectIsPopulated = createSelector(
tagsIsPopulated,
uiSettingsIsPopulated,
qualityProfilesIsPopulated,
languageProfilesIsPopulated,
languagesIsPopulated,
importListsIsPopulated,
systemStatusIsPopulated
) => {
@ -68,7 +68,7 @@ const selectIsPopulated = createSelector(
tagsIsPopulated &&
uiSettingsIsPopulated &&
qualityProfilesIsPopulated &&
languageProfilesIsPopulated &&
languagesIsPopulated &&
importListsIsPopulated &&
systemStatusIsPopulated
);
@ -81,7 +81,7 @@ const selectErrors = createSelector(
(state) => state.tags.error,
(state) => state.settings.ui.error,
(state) => state.settings.qualityProfiles.error,
(state) => state.settings.languageProfiles.error,
(state) => state.settings.languages.error,
(state) => state.settings.importLists.error,
(state) => state.system.status.error,
(
@ -90,7 +90,7 @@ const selectErrors = createSelector(
tagsError,
uiSettingsError,
qualityProfilesError,
languageProfilesError,
languagesError,
importListsError,
systemStatusError
) => {
@ -100,7 +100,7 @@ const selectErrors = createSelector(
tagsError ||
uiSettingsError ||
qualityProfilesError ||
languageProfilesError ||
languagesError ||
importListsError ||
systemStatusError
);
@ -112,7 +112,7 @@ const selectErrors = createSelector(
tagsError,
uiSettingsError,
qualityProfilesError,
languageProfilesError,
languagesError,
importListsError,
systemStatusError
};
@ -161,8 +161,8 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchQualityProfiles() {
dispatch(fetchQualityProfiles());
},
dispatchFetchLanguageProfiles() {
dispatch(fetchLanguageProfiles());
dispatchFetchLanguages() {
dispatch(fetchLanguages());
},
dispatchFetchImportLists() {
dispatch(fetchImportLists());
@ -201,7 +201,7 @@ class PageConnector extends Component {
this.props.dispatchFetchCustomFilters();
this.props.dispatchFetchTags();
this.props.dispatchFetchQualityProfiles();
this.props.dispatchFetchLanguageProfiles();
this.props.dispatchFetchLanguages();
this.props.dispatchFetchImportLists();
this.props.dispatchFetchUISettings();
this.props.dispatchFetchStatus();
@ -225,7 +225,7 @@ class PageConnector extends Component {
dispatchFetchSeries,
dispatchFetchTags,
dispatchFetchQualityProfiles,
dispatchFetchLanguageProfiles,
dispatchFetchLanguages,
dispatchFetchImportLists,
dispatchFetchUISettings,
dispatchFetchStatus,
@ -264,7 +264,7 @@ PageConnector.propTypes = {
dispatchFetchCustomFilters: PropTypes.func.isRequired,
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
dispatchFetchLanguageProfiles: PropTypes.func.isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchImportLists: PropTypes.func.isRequired,
dispatchFetchUISettings: PropTypes.func.isRequired,
dispatchFetchStatus: PropTypes.func.isRequired,

View File

@ -1,37 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Label from 'Components/Label';
import { kinds } from 'Helpers/Props';
function EpisodeLanguage(props) {
const {
className,
language,
isCutoffNotMet
} = props;
if (!language) {
return null;
}
return (
<Label
className={className}
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
>
{language.name}
</Label>
);
}
EpisodeLanguage.propTypes = {
className: PropTypes.string,
language: PropTypes.object,
isCutoffNotMet: PropTypes.bool
};
EpisodeLanguage.defaultProps = {
isCutoffNotMet: true
};
export default EpisodeLanguage;

View File

@ -0,0 +1,69 @@
import PropTypes from 'prop-types';
import React from 'react';
import Label from 'Components/Label';
import Popover from 'Components/Tooltip/Popover';
import { kinds, tooltipPositions } from 'Helpers/Props';
function EpisodeLanguages(props) {
const {
className,
languages,
isCutoffNotMet
} = props;
if (!languages) {
return null;
}
if (languages.length === 1) {
return (
<Label
className={className}
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
>
{languages[0].name}
</Label>
);
}
return (
<Popover
className={className}
anchor={
<Label
className={className}
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
>
Multi-Languages
</Label>
}
title={'Languages'}
body={
<ul>
{
languages.map((language) => {
return (
<li key={language.id}>
{language.name}
</li>
);
})
}
</ul>
}
position={tooltipPositions.LEFT}
/>
);
}
EpisodeLanguages.propTypes = {
className: PropTypes.string,
languages: PropTypes.arrayOf(PropTypes.object),
isCutoffNotMet: PropTypes.bool
};
EpisodeLanguages.defaultProps = {
isCutoffNotMet: true
};
export default EpisodeLanguages;

View File

@ -18,8 +18,8 @@ const columns = [
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isVisible: true
},
{

View File

@ -11,7 +11,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
@ -65,7 +65,7 @@ class EpisodeHistoryRow extends Component {
const {
eventType,
sourceTitle,
language,
languages,
languageCutoffNotMet,
quality,
qualityCutoffNotMet,
@ -90,8 +90,8 @@ class EpisodeHistoryRow extends Component {
</TableRowCell>
<TableRowCell>
<EpisodeLanguage
language={language}
<EpisodeLanguages
languages={languages}
isCutoffNotMet={languageCutoffNotMet}
/>
</TableRowCell>
@ -177,7 +177,7 @@ EpisodeHistoryRow.propTypes = {
id: PropTypes.number.isRequired,
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
language: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
languageCutoffNotMet: PropTypes.bool.isRequired,
quality: PropTypes.object.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,

View File

@ -4,7 +4,7 @@
width: 100px;
}
.language,
.languages,
.quality {
composes: cell from '~Components/Table/Cells/TableRowCell.css';

View File

@ -6,7 +6,7 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import formatBytes from 'Utilities/Number/formatBytes';
@ -50,7 +50,7 @@ class EpisodeFileRow extends Component {
const {
path,
size,
language,
languages,
quality,
languageCutoffNotMet,
qualityCutoffNotMet,
@ -87,14 +87,14 @@ class EpisodeFileRow extends Component {
);
}
if (name === 'language') {
if (name === 'languages') {
return (
<TableRowCell
key={name}
className={styles.language}
className={styles.languages}
>
<EpisodeLanguage
language={language}
<EpisodeLanguages
languages={languages}
isCutoffNotMet={languageCutoffNotMet}
/>
</TableRowCell>
@ -167,7 +167,7 @@ class EpisodeFileRow extends Component {
EpisodeFileRow.propTypes = {
path: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
language: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
languageCutoffNotMet: PropTypes.bool.isRequired,
quality: PropTypes.object.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,

View File

@ -24,8 +24,8 @@ const columns = [
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isSortable: false,
isVisible: true
},
@ -84,7 +84,7 @@ class EpisodeSummary extends Component {
mediaInfo,
path,
size,
language,
languages,
quality,
languageCutoffNotMet,
qualityCutoffNotMet,
@ -132,7 +132,7 @@ class EpisodeSummary extends Component {
<EpisodeFileRow
path={path}
size={size}
language={language}
languages={languages}
languageCutoffNotMet={languageCutoffNotMet}
quality={quality}
qualityCutoffNotMet={qualityCutoffNotMet}
@ -168,7 +168,7 @@ EpisodeSummary.propTypes = {
mediaInfo: PropTypes.object,
path: PropTypes.string,
size: PropTypes.number,
language: PropTypes.object,
languages: PropTypes.arrayOf(PropTypes.object),
languageCutoffNotMet: PropTypes.bool,
quality: PropTypes.object,
qualityCutoffNotMet: PropTypes.bool,

View File

@ -28,7 +28,7 @@ function createMapStateToProps() {
mediaInfo,
path,
size,
language,
languages,
languageCutoffNotMet,
quality,
qualityCutoffNotMet
@ -42,7 +42,7 @@ function createMapStateToProps() {
mediaInfo,
path,
size,
language,
languages,
languageCutoffNotMet,
quality,
qualityCutoffNotMet

View File

@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector';
function createMapStateToProps() {
@ -8,10 +8,10 @@ function createMapStateToProps() {
createEpisodeFileSelector(),
(episodeFile) => {
return {
language: episodeFile ? episodeFile.language : undefined
languages: episodeFile ? episodeFile.languages : undefined
};
}
);
}
export default connect(createMapStateToProps)(EpisodeLanguage);
export default connect(createMapStateToProps)(EpisodeLanguages);

View File

@ -3,7 +3,6 @@ export const BYTES = 'bytes';
export const DATE = 'date';
export const DEFAULT = 'default';
export const INDEXER = 'indexer';
export const LANGUAGE_PROFILE = 'languageProfile';
export const PROTOCOL = 'protocol';
export const QUALITY = 'quality';
export const QUALITY_PROFILE = 'qualityProfile';

View File

@ -9,8 +9,8 @@ export const OAUTH = 'oauth';
export const PASSWORD = 'password';
export const PATH = 'path';
export const QUALITY_PROFILE_SELECT = 'qualityProfileSelect';
export const LANGUAGE_PROFILE_SELECT = 'languageProfileSelect';
export const INDEXER_SELECT = 'indexerSelect';
export const LANGUAGE_SELECT = 'languageSelect';
export const DOWNLOAD_CLIENT_SELECT = 'downloadClientSelect';
export const ROOT_FOLDER_SELECT = 'rootFolderSelect';
export const SELECT = 'select';
@ -35,10 +35,10 @@ export const all = [
PASSWORD,
PATH,
QUALITY_PROFILE_SELECT,
LANGUAGE_PROFILE_SELECT,
INDEXER_SELECT,
DOWNLOAD_CLIENT_SELECT,
ROOT_FOLDER_SELECT,
LANGUAGE_SELECT,
SELECT,
DYNAMIC_SELECT,
SERIES_TYPE_SELECT,

View File

@ -66,8 +66,8 @@ const columns = [
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isSortable: true,
isVisible: true
},

View File

@ -193,7 +193,7 @@ class InteractiveImportModalContentConnector extends Component {
episodes,
releaseGroup,
quality,
language,
languages,
episodeFileId
} = item;
@ -217,8 +217,8 @@ class InteractiveImportModalContentConnector extends Component {
return;
}
if (!language) {
this.setState({ interactiveImportErrorMessage: 'Language must be chosen for each selected file' });
if (!languages) {
this.setState({ interactiveImportErrorMessage: 'Language(s) must be chosen for each selected file' });
return;
}
@ -230,7 +230,7 @@ class InteractiveImportModalContentConnector extends Component {
id: episodeFileId,
releaseGroup,
quality,
language
languages
});
return;
@ -244,7 +244,7 @@ class InteractiveImportModalContentConnector extends Component {
episodeIds: episodes.map((e) => e.id),
releaseGroup,
quality,
language,
languages,
downloadId: this.props.downloadId,
episodeFileId
});

View File

@ -5,7 +5,7 @@
}
.quality,
.language {
.languages {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
text-align: center;

View File

@ -7,7 +7,7 @@ import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import SelectEpisodeModal from 'InteractiveImport/Episode/SelectEpisodeModal';
@ -47,7 +47,7 @@ class InteractiveImportRow extends Component {
seasonNumber,
episodes,
quality,
language,
languages,
episodeFileId,
columns
} = this.props;
@ -58,7 +58,7 @@ class InteractiveImportRow extends Component {
seasonNumber != null &&
episodes.length &&
quality &&
language
languages
) {
this.props.onSelectedChange({
id,
@ -79,7 +79,7 @@ class InteractiveImportRow extends Component {
seasonNumber,
episodes,
quality,
language,
languages,
isSelected,
onValidRowChange
} = this.props;
@ -89,7 +89,7 @@ class InteractiveImportRow extends Component {
prevProps.seasonNumber === seasonNumber &&
!hasDifferentItems(prevProps.episodes, episodes) &&
prevProps.quality === quality &&
prevProps.language === language &&
prevProps.languages === languages &&
prevProps.isSelected === isSelected
) {
return;
@ -100,7 +100,7 @@ class InteractiveImportRow extends Component {
seasonNumber != null &&
episodes.length &&
quality &&
language
languages
);
if (isSelected && !isValid) {
@ -210,7 +210,7 @@ class InteractiveImportRow extends Component {
seasonNumber,
episodes,
quality,
language,
languages,
releaseGroup,
size,
rejections,
@ -252,7 +252,7 @@ class InteractiveImportRow extends Component {
const showEpisodeNumbersPlaceholder = isSelected && Number.isInteger(seasonNumber) && !episodes.length;
const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
const showQualityPlaceholder = isSelected && !quality;
const showLanguagePlaceholder = isSelected && !language;
const showLanguagePlaceholder = isSelected && !languages;
return (
<TableRow>
@ -352,10 +352,10 @@ class InteractiveImportRow extends Component {
}
{
!showLanguagePlaceholder && !!language &&
<EpisodeLanguage
!showLanguagePlaceholder && !!languages &&
<EpisodeLanguages
className={styles.label}
language={language}
languages={languages}
/>
}
</TableRowCellButton>
@ -441,7 +441,7 @@ class InteractiveImportRow extends Component {
<SelectLanguageModal
isOpen={isSelectLanguageModalOpen}
ids={[id]}
languageId={language ? language.id : 0}
languageId={languages ? languages.id : 0}
modalTitle={modalTitle}
onModalClose={this.onSelectLanguageModalClose}
/>
@ -460,7 +460,7 @@ InteractiveImportRow.propTypes = {
episodes: PropTypes.arrayOf(PropTypes.object).isRequired,
releaseGroup: PropTypes.string,
quality: PropTypes.object,
language: PropTypes.object,
languages: PropTypes.arrayOf(PropTypes.object),
size: PropTypes.number.isRequired,
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@ -4,7 +4,6 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { reprocessInteractiveImportItems, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
import { fetchLanguageProfileSchema } from 'Store/Actions/settingsActions';
import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
import SelectLanguageModalContent from './SelectLanguageModalContent';
@ -18,22 +17,12 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
dispatchFetchLanguageProfileSchema: fetchLanguageProfileSchema,
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems,
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems
};
class SelectLanguageModalContentConnector extends Component {
//
// Lifecycle
componentDidMount = () => {
if (!this.props.isPopulated) {
this.props.dispatchFetchLanguageProfileSchema();
}
};
//
// Listeners
@ -44,13 +33,20 @@ class SelectLanguageModalContentConnector extends Component {
dispatchReprocessInteractiveImportItems
} = this.props;
const languageId = parseInt(value);
const language = _.find(this.props.items,
(item) => item.language.id === languageId).language;
const languages = [];
value.forEach((languageId) => {
const language = _.find(this.props.items,
(item) => item.id === parseInt(languageId));
if (language !== undefined) {
languages.push(language);
}
});
dispatchUpdateInteractiveImportItems({
ids,
language
languages
});
dispatchReprocessInteractiveImportItems({ ids });
@ -77,7 +73,6 @@ SelectLanguageModalContentConnector.propTypes = {
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchFetchLanguageProfileSchema: PropTypes.func.isRequired,
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired

View File

@ -51,7 +51,7 @@ const columns = [
},
{
name: 'languageWeight',
label: 'Language',
label: 'Languages',
isSortable: true,
isVisible: true
},

View File

@ -24,13 +24,13 @@
}
.quality,
.language {
.languages {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
text-align: center;
}
.language {
.languages {
width: 100px;
}

View File

@ -10,7 +10,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeQuality from 'Episode/EpisodeQuality';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import formatDateTime from 'Utilities/Date/formatDateTime';
@ -116,7 +116,7 @@ class InteractiveSearchRow extends Component {
seeders,
leechers,
quality,
language,
languages,
customFormatScore,
customFormats,
sceneMapping,
@ -188,8 +188,8 @@ class InteractiveSearchRow extends Component {
}
</TableRowCell>
<TableRowCell className={styles.language}>
<EpisodeLanguage language={language} />
<TableRowCell className={styles.languages}>
<EpisodeLanguages languages={languages} />
</TableRowCell>
<TableRowCell className={styles.quality}>
@ -286,7 +286,7 @@ InteractiveSearchRow.propTypes = {
seeders: PropTypes.number,
leechers: PropTypes.number,
quality: PropTypes.object.isRequired,
language: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
sceneMapping: PropTypes.object,

View File

@ -28,7 +28,7 @@
width: 100px;
}
.language,
.languages,
.audio,
.video,
.status {

View File

@ -180,11 +180,11 @@ class EpisodeRow extends Component {
);
}
if (name === 'language') {
if (name === 'languages') {
return (
<TableRowCell
key={name}
className={styles.language}
className={styles.languages}
>
<EpisodeFileLanguageConnector
episodeFileId={episodeFileId}

View File

@ -59,7 +59,6 @@ class EditSeriesModalContent extends Component {
title,
item,
isSaving,
showLanguageProfile,
originalPath,
onInputChange,
onModalClose,
@ -71,7 +70,6 @@ class EditSeriesModalContent extends Component {
monitored,
seasonFolder,
qualityProfileId,
languageProfileId,
seriesType,
path,
tags
@ -120,20 +118,6 @@ class EditSeriesModalContent extends Component {
/>
</FormGroup>
{
showLanguageProfile &&
<FormGroup>
<FormLabel>Language Profile</FormLabel>
<FormInputGroup
type={inputTypes.LANGUAGE_PROFILE_SELECT}
name="languageProfileId"
{...languageProfileId}
onChange={onInputChange}
/>
</FormGroup>
}
<FormGroup>
<FormLabel>Series Type</FormLabel>
@ -209,7 +193,6 @@ EditSeriesModalContent.propTypes = {
title: PropTypes.string.isRequired,
item: PropTypes.object.isRequired,
isSaving: PropTypes.bool.isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
isPathChanging: PropTypes.bool.isRequired,
originalPath: PropTypes.string.isRequired,
onInputChange: PropTypes.func.isRequired,

View File

@ -27,10 +27,9 @@ function createIsPathChangingSelector() {
function createMapStateToProps() {
return createSelector(
(state) => state.series,
(state) => state.settings.languageProfiles,
createSeriesSelector(),
createIsPathChangingSelector(),
(seriesState, languageProfiles, series, isPathChanging) => {
(seriesState, series, isPathChanging) => {
const {
isSaving,
saveError,
@ -41,7 +40,6 @@ function createMapStateToProps() {
'monitored',
'seasonFolder',
'qualityProfileId',
'languageProfileId',
'seriesType',
'path',
'tags'
@ -56,7 +54,6 @@ function createMapStateToProps() {
isPathChanging,
originalPath: series.path,
item: settings.settings,
showLanguageProfile: languageProfiles.items.length > 1,
...settings
};
}

View File

@ -212,7 +212,6 @@ class SeriesEditor extends Component {
deleteError={deleteError}
isOrganizingSeries={isOrganizingSeries}
columns={columns}
showLanguageProfile={columns.find((column) => column.name === 'languageProfileId').isVisible}
onSaveSelected={this.onSaveSelected}
onOrganizeSeriesPress={this.onOrganizeSeriesPress}
/>

View File

@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import LanguageProfileSelectInputConnector from 'Components/Form/LanguageProfileSelectInputConnector';
import QualityProfileSelectInputConnector from 'Components/Form/QualityProfileSelectInputConnector';
import RootFolderSelectInputConnector from 'Components/Form/RootFolderSelectInputConnector';
import SelectInput from 'Components/Form/SelectInput';
@ -27,7 +26,6 @@ class SeriesEditorFooter extends Component {
this.state = {
monitored: NO_CHANGE,
qualityProfileId: NO_CHANGE,
languageProfileId: NO_CHANGE,
seriesType: NO_CHANGE,
seasonFolder: NO_CHANGE,
rootFolderPath: NO_CHANGE,
@ -49,7 +47,6 @@ class SeriesEditorFooter extends Component {
this.setState({
monitored: NO_CHANGE,
qualityProfileId: NO_CHANGE,
languageProfileId: NO_CHANGE,
seriesType: NO_CHANGE,
seasonFolder: NO_CHANGE,
rootFolderPath: NO_CHANGE,
@ -152,7 +149,6 @@ class SeriesEditorFooter extends Component {
const {
monitored,
qualityProfileId,
languageProfileId,
seriesType,
seasonFolder,
rootFolderPath,
@ -225,28 +221,6 @@ class SeriesEditorFooter extends Component {
);
}
if (name === 'languageProfileId') {
return (
<div
key={name}
className={styles.inputContainer}
>
<SeriesEditorFooterLabel
label="Language Profile"
isSaving={isSaving && languageProfileId !== NO_CHANGE}
/>
<LanguageProfileSelectInputConnector
name="languageProfileId"
value={languageProfileId}
includeNoChange={true}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
);
}
if (name === 'seriesType') {
return (
<div
@ -392,7 +366,6 @@ SeriesEditorFooter.propTypes = {
isDeleting: PropTypes.bool.isRequired,
deleteError: PropTypes.object,
isOrganizingSeries: PropTypes.bool.isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onSaveSelected: PropTypes.func.isRequired,
onOrganizeSeriesPress: PropTypes.func.isRequired

View File

@ -33,7 +33,6 @@ class SeriesEditorRow extends Component {
titleSlug,
seriesType,
qualityProfile,
languageProfile,
path,
tags,
seasonFolder,
@ -95,14 +94,6 @@ class SeriesEditorRow extends Component {
);
}
if (name === 'languageProfileId') {
return (
<TableRowCell key={name}>
{languageProfile.name}
</TableRowCell>
);
}
if (name === 'seriesType') {
return (
<TableRowCell key={name}>
@ -167,7 +158,6 @@ SeriesEditorRow.propTypes = {
titleSlug: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
monitored: PropTypes.bool.isRequired,
languageProfile: PropTypes.object.isRequired,
qualityProfile: PropTypes.object.isRequired,
seriesType: PropTypes.string.isRequired,
seasonFolder: PropTypes.bool.isRequired,

View File

@ -2,17 +2,14 @@ import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createLanguageProfileSelector from 'Store/Selectors/createLanguageProfileSelector';
import createQualityProfileSelector from 'Store/Selectors/createQualityProfileSelector';
import SeriesEditorRow from './SeriesEditorRow';
function createMapStateToProps() {
return createSelector(
createLanguageProfileSelector(),
createQualityProfileSelector(),
(languageProfile, qualityProfile) => {
(qualityProfile) => {
return {
languageProfile,
qualityProfile
};
}

View File

@ -29,8 +29,8 @@ const columns = [
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isVisible: true
},
{

View File

@ -11,7 +11,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import EpisodeLanguage from 'Episode/EpisodeLanguage';
import EpisodeLanguages from 'Episode/EpisodeLanguages';
import EpisodeNumber from 'Episode/EpisodeNumber';
import EpisodeQuality from 'Episode/EpisodeQuality';
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
@ -67,7 +67,7 @@ class SeriesHistoryRow extends Component {
const {
eventType,
sourceTitle,
language,
languages,
languageCutoffNotMet,
quality,
qualityCutoffNotMet,
@ -110,8 +110,8 @@ class SeriesHistoryRow extends Component {
</TableRowCell>
<TableRowCell>
<EpisodeLanguage
language={language}
<EpisodeLanguages
languages={languages}
isCutoffNotMet={languageCutoffNotMet}
/>
</TableRowCell>
@ -197,7 +197,7 @@ SeriesHistoryRow.propTypes = {
id: PropTypes.number.isRequired,
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
language: PropTypes.object.isRequired,
languages: PropTypes.object.isRequired,
languageCutoffNotMet: PropTypes.bool.isRequired,
quality: PropTypes.object.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,

View File

@ -55,15 +55,6 @@ function SeriesIndexSortMenu(props) {
Quality Profile
</SortMenuItem>
<SortMenuItem
name="languageProfileId"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
Language Profile
</SortMenuItem>
<SortMenuItem
name="nextAiring"
sortKey={sortKey}

View File

@ -188,7 +188,6 @@ class SeriesIndexOverviews extends Component {
isSmallScreen={isSmallScreen}
style={style}
seriesId={series.id}
languageProfileId={series.languageProfileId}
qualityProfileId={series.qualityProfileId}
/>
</div>

View File

@ -243,7 +243,6 @@ class SeriesIndexPosters extends Component {
timeFormat={timeFormat}
style={style}
seriesId={series.id}
languageProfileId={series.languageProfileId}
qualityProfileId={series.qualityProfileId}
/>
</div>

View File

@ -6,7 +6,6 @@ import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { executeCommand } from 'Store/Actions/commandActions';
import createExecutingCommandsSelector from 'Store/Selectors/createExecutingCommandsSelector';
import createSeriesLanguageProfileSelector from 'Store/Selectors/createSeriesLanguageProfileSelector';
import createSeriesQualityProfileSelector from 'Store/Selectors/createSeriesQualityProfileSelector';
import createSeriesSelector from 'Store/Selectors/createSeriesSelector';
@ -32,13 +31,11 @@ function createMapStateToProps() {
return createSelector(
createSeriesSelector(),
createSeriesQualityProfileSelector(),
createSeriesLanguageProfileSelector(),
selectShowSearchAction(),
createExecutingCommandsSelector(),
(
series,
qualityProfile,
languageProfile,
showSearchAction,
executingCommands
) => {
@ -71,7 +68,6 @@ function createMapStateToProps() {
return {
...series,
qualityProfile,
languageProfile,
latestSeason,
showSearchAction,
isRefreshingSeries,

View File

@ -30,8 +30,7 @@
flex: 2 0 90px;
}
.qualityProfileId,
.languageProfileId {
.qualityProfileId {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 1 0 125px;

View File

@ -66,8 +66,7 @@
flex: 2 0 90px;
}
.qualityProfileId,
.languageProfileId {
.qualityProfileId {
composes: cell;
flex: 1 0 125px;

View File

@ -86,7 +86,6 @@ class SeriesIndexRow extends Component {
seriesType,
network,
qualityProfile,
languageProfile,
nextAiring,
previousAiring,
added,
@ -224,17 +223,6 @@ class SeriesIndexRow extends Component {
);
}
if (name === 'languageProfileId') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{languageProfile.name}
</VirtualTableRowCell>
);
}
if (name === 'nextAiring') {
return (
<RelativeDateCellConnector
@ -525,7 +513,6 @@ SeriesIndexRow.propTypes = {
seriesType: PropTypes.string.isRequired,
network: PropTypes.string,
qualityProfile: PropTypes.object.isRequired,
languageProfile: PropTypes.object.isRequired,
nextAiring: PropTypes.string,
previousAiring: PropTypes.string,
added: PropTypes.string,

View File

@ -62,7 +62,6 @@ class SeriesIndexTable extends Component {
component={SeriesIndexRow}
columns={columns}
seriesId={series.id}
languageProfileId={series.languageProfileId}
qualityProfileId={series.qualityProfileId}
showBanners={showBanners}
/>

View File

@ -1,7 +1,6 @@
const growableColumns = [
'network',
'qualityProfileId',
'languageProfileId',
'path',
'tags'
];

View File

@ -4,12 +4,6 @@
margin-right: auto;
}
.hideLanguageProfile {
composes: group from '~Components/Form/FormGroup.css';
display: none;
}
.labelIcon {
margin-left: 8px;
}

View File

@ -35,7 +35,6 @@ function EditImportListModalContent(props) {
onSavePress,
onTestPress,
onDeleteImportListPress,
showLanguageProfile,
...otherProps
} = props;
@ -46,7 +45,6 @@ function EditImportListModalContent(props) {
shouldMonitor,
rootFolderPath,
qualityProfileId,
languageProfileId,
seriesType,
seasonFolder,
tags,
@ -148,18 +146,6 @@ function EditImportListModalContent(props) {
/>
</FormGroup>
<FormGroup className={showLanguageProfile ? undefined : styles.hideLanguageProfile}>
<FormLabel>Language Profile</FormLabel>
<FormInputGroup
type={inputTypes.LANGUAGE_PROFILE_SELECT}
name="languageProfileId"
helpText={'Language Profile list items will be added with'}
{...languageProfileId}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>
Series Type
@ -279,7 +265,6 @@ EditImportListModalContent.propTypes = {
isTesting: PropTypes.bool.isRequired,
saveError: PropTypes.object,
item: PropTypes.object.isRequired,
showLanguageProfile: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
onFieldChange: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired,

View File

@ -9,12 +9,10 @@ import EditImportListModalContent from './EditImportListModalContent';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.advancedSettings,
(state) => state.settings.languageProfiles,
createProviderSettingsSelector('importLists'),
(advancedSettings, languageProfiles, importList) => {
(advancedSettings, importList) => {
return {
advancedSettings,
showLanguageProfile: languageProfiles.items.length > 1,
...importList
};
}

View File

@ -1,27 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props';
import EditLanguageProfileModalContentConnector from './EditLanguageProfileModalContentConnector';
function EditLanguageProfileModal({ isOpen, onModalClose, ...otherProps }) {
return (
<Modal
size={sizes.MEDIUM}
isOpen={isOpen}
onModalClose={onModalClose}
>
<EditLanguageProfileModalContentConnector
{...otherProps}
onModalClose={onModalClose}
/>
</Modal>
);
}
EditLanguageProfileModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default EditLanguageProfileModal;

View File

@ -1,43 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import EditLanguageProfileModal from './EditLanguageProfileModal';
function mapStateToProps() {
return {};
}
const mapDispatchToProps = {
clearPendingChanges
};
class EditLanguageProfileModalConnector extends Component {
//
// Listeners
onModalClose = () => {
this.props.clearPendingChanges({ section: 'settings.languageProfiles' });
this.props.onModalClose();
};
//
// Render
render() {
return (
<EditLanguageProfileModal
{...this.props}
onModalClose={this.onModalClose}
/>
);
}
}
EditLanguageProfileModalConnector.propTypes = {
onModalClose: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(EditLanguageProfileModalConnector);

View File

@ -1,3 +0,0 @@
.deleteButtonContainer {
margin-right: auto;
}

View File

@ -1,165 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
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 { inputTypes, kinds } from 'Helpers/Props';
import LanguageProfileItems from './LanguageProfileItems';
import styles from './EditLanguageProfileModalContent.css';
function EditLanguageProfileModalContent(props) {
const {
isFetching,
error,
isSaving,
saveError,
languages,
item,
isInUse,
onInputChange,
onCutoffChange,
onSavePress,
onModalClose,
onDeleteLanguageProfilePress,
...otherProps
} = props;
const {
id,
name,
upgradeAllowed,
cutoff,
languages: itemLanguages
} = item;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{id ? 'Edit Language Profile' : 'Add Language Profile'}
</ModalHeader>
<ModalBody>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error &&
<div>Unable to add a new language profile, please try again.</div>
}
{
!isFetching && !error &&
<Form {...otherProps}>
<FormGroup>
<FormLabel>Name</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="name"
{...name}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>
Upgrades Allowed
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="upgradeAllowed"
{...upgradeAllowed}
helpText="If disabled languages will not be upgraded"
onChange={onInputChange}
/>
</FormGroup>
{
upgradeAllowed.value &&
<FormGroup>
<FormLabel>Upgrade Until</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="cutoff"
{...cutoff}
value={cutoff ? cutoff.value.id : 0}
values={languages}
helpText="Once this language is reached Sonarr will no longer download episodes"
onChange={onCutoffChange}
/>
</FormGroup>
}
<LanguageProfileItems
languageProfileItems={itemLanguages.value}
errors={itemLanguages.errors}
warnings={itemLanguages.warnings}
{...otherProps}
/>
</Form>
}
</ModalBody>
<ModalFooter>
{
id &&
<div
className={styles.deleteButtonContainer}
title={isInUse && 'Can\'t delete a language profile that is attached to a series'}
>
<Button
kind={kinds.DANGER}
isDisabled={isInUse}
onPress={onDeleteLanguageProfilePress}
>
Delete
</Button>
</div>
}
<Button
onPress={onModalClose}
>
Cancel
</Button>
<SpinnerErrorButton
isSpinning={isSaving}
error={saveError}
onPress={onSavePress}
>
Save
</SpinnerErrorButton>
</ModalFooter>
</ModalContent>
);
}
EditLanguageProfileModalContent.propTypes = {
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
item: PropTypes.object.isRequired,
isInUse: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired,
onCutoffChange: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired,
onDeleteLanguageProfilePress: PropTypes.func
};
export default EditLanguageProfileModalContent;

View File

@ -1,189 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchLanguageProfileSchema, saveLanguageProfile, setLanguageProfileValue } from 'Store/Actions/settingsActions';
import createProfileInUseSelector from 'Store/Selectors/createProfileInUseSelector';
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
import EditLanguageProfileModalContent from './EditLanguageProfileModalContent';
function createLanguagesSelector() {
return createSelector(
createProviderSettingsSelector('languageProfiles'),
(languageProfile) => {
const languages = languageProfile.item.languages;
if (!languages || !languages.value) {
return [];
}
return _.reduceRight(languages.value, (result, { allowed, language }) => {
if (allowed) {
result.push({
key: language.id,
value: language.name
});
}
return result;
}, []);
}
);
}
function createMapStateToProps() {
return createSelector(
createProviderSettingsSelector('languageProfiles'),
createLanguagesSelector(),
createProfileInUseSelector('languageProfileId'),
(languageProfile, languages, isInUse) => {
return {
languages,
...languageProfile,
isInUse
};
}
);
}
const mapDispatchToProps = {
fetchLanguageProfileSchema,
setLanguageProfileValue,
saveLanguageProfile
};
class EditLanguageProfileModalContentConnector extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
dragIndex: null,
dropIndex: null
};
}
componentDidMount() {
if (!this.props.id && !this.props.isPopulated) {
this.props.fetchLanguageProfileSchema();
}
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
this.props.onModalClose();
}
}
//
// Listeners
onInputChange = ({ name, value }) => {
this.props.setLanguageProfileValue({ name, value });
};
onCutoffChange = ({ name, value }) => {
const id = parseInt(value);
const item = _.find(this.props.item.languages.value, (i) => i.language.id === id);
this.props.setLanguageProfileValue({ name, value: item.language });
};
onSavePress = () => {
this.props.saveLanguageProfile({ id: this.props.id });
};
onLanguageProfileItemAllowedChange = (id, allowed) => {
const languageProfile = _.cloneDeep(this.props.item);
const item = _.find(languageProfile.languages.value, (i) => i.language.id === id);
item.allowed = allowed;
this.props.setLanguageProfileValue({
name: 'languages',
value: languageProfile.languages.value
});
const cutoff = languageProfile.cutoff.value;
// If the cutoff isn't allowed anymore or there isn't a cutoff set one
if (!cutoff || !_.find(languageProfile.languages.value, (i) => i.language.id === cutoff.id).allowed) {
const firstAllowed = _.find(languageProfile.languages.value, { allowed: true });
this.props.setLanguageProfileValue({ name: 'cutoff', value: firstAllowed ? firstAllowed.language : null });
}
};
onLanguageProfileItemDragMove = (dragIndex, dropIndex) => {
if (this.state.dragIndex !== dragIndex || this.state.dropIndex !== dropIndex) {
this.setState({
dragIndex,
dropIndex
});
}
};
onLanguageProfileItemDragEnd = ({ id }, didDrop) => {
const {
dragIndex,
dropIndex
} = this.state;
if (didDrop && dropIndex !== null) {
const languageProfile = _.cloneDeep(this.props.item);
const languages = languageProfile.languages.value.splice(dragIndex, 1);
languageProfile.languages.value.splice(dropIndex, 0, languages[0]);
this.props.setLanguageProfileValue({
name: 'languages',
value: languageProfile.languages.value
});
}
this.setState({
dragIndex: null,
dropIndex: null
});
};
//
// Render
render() {
if (_.isEmpty(this.props.item.languages) && !this.props.isFetching) {
return null;
}
return (
<EditLanguageProfileModalContent
{...this.state}
{...this.props}
onSavePress={this.onSavePress}
onInputChange={this.onInputChange}
onCutoffChange={this.onCutoffChange}
onLanguageProfileItemAllowedChange={this.onLanguageProfileItemAllowedChange}
onLanguageProfileItemDragMove={this.onLanguageProfileItemDragMove}
onLanguageProfileItemDragEnd={this.onLanguageProfileItemDragEnd}
/>
);
}
}
EditLanguageProfileModalContentConnector.propTypes = {
id: PropTypes.number,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
item: PropTypes.object.isRequired,
setLanguageProfileValue: PropTypes.func.isRequired,
fetchLanguageProfileSchema: PropTypes.func.isRequired,
saveLanguageProfile: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(EditLanguageProfileModalContentConnector);

View File

@ -1,31 +0,0 @@
.languageProfile {
composes: card from '~Components/Card.css';
width: 300px;
}
.nameContainer {
display: flex;
justify-content: space-between;
}
.name {
@add-mixin truncate;
margin-bottom: 20px;
font-weight: 300;
font-size: 24px;
}
.cloneButton {
composes: button from '~Components/Link/IconButton.css';
height: 36px;
}
.languages {
display: flex;
flex-wrap: wrap;
margin-top: 5px;
pointer-events: all;
}

View File

@ -1,147 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Card from 'Components/Card';
import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import { icons, kinds } from 'Helpers/Props';
import EditLanguageProfileModalConnector from './EditLanguageProfileModalConnector';
import styles from './LanguageProfile.css';
class LanguageProfile extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isEditLanguageProfileModalOpen: false,
isDeleteLanguageProfileModalOpen: false
};
}
//
// Listeners
onEditLanguageProfilePress = () => {
this.setState({ isEditLanguageProfileModalOpen: true });
};
onEditLanguageProfileModalClose = () => {
this.setState({ isEditLanguageProfileModalOpen: false });
};
onDeleteLanguageProfilePress = () => {
this.setState({
isEditLanguageProfileModalOpen: false,
isDeleteLanguageProfileModalOpen: true
});
};
onDeleteLanguageProfileModalClose = () => {
this.setState({ isDeleteLanguageProfileModalOpen: false });
};
onConfirmDeleteLanguageProfile = () => {
this.props.onConfirmDeleteLanguageProfile(this.props.id);
};
onCloneLanguageProfilePress = () => {
const {
id,
onCloneLanguageProfilePress
} = this.props;
onCloneLanguageProfilePress(id);
};
//
// Render
render() {
const {
id,
name,
upgradeAllowed,
cutoff,
languages,
isDeleting
} = this.props;
return (
<Card
className={styles.languageProfile}
overlayContent={true}
onPress={this.onEditLanguageProfilePress}
>
<div className={styles.nameContainer}>
<div className={styles.name}>
{name}
</div>
<IconButton
className={styles.cloneButton}
title="Clone Profile"
name={icons.CLONE}
onPress={this.onCloneLanguageProfilePress}
/>
</div>
<div className={styles.languages}>
{
languages.map((item) => {
if (!item.allowed) {
return null;
}
const isCutoff = upgradeAllowed && item.language.id === cutoff.id;
return (
<Label
key={item.language.id}
kind={isCutoff ? kinds.INFO : kinds.DEFAULT}
title={isCutoff ? 'Upgrade until this language is met or exceeded' : null}
>
{item.language.name}
</Label>
);
})
}
</div>
<EditLanguageProfileModalConnector
id={id}
isOpen={this.state.isEditLanguageProfileModalOpen}
onModalClose={this.onEditLanguageProfileModalClose}
onDeleteLanguageProfilePress={this.onDeleteLanguageProfilePress}
/>
<ConfirmModal
isOpen={this.state.isDeleteLanguageProfileModalOpen}
kind={kinds.DANGER}
title="Delete Language Profile"
message={`Are you sure you want to delete the language profile '${name}'?`}
confirmLabel="Delete"
isSpinning={isDeleting}
onConfirm={this.onConfirmDeleteLanguageProfile}
onCancel={this.onDeleteLanguageProfileModalClose}
/>
</Card>
);
}
}
LanguageProfile.propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
upgradeAllowed: PropTypes.bool.isRequired,
cutoff: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
isDeleting: PropTypes.bool.isRequired,
onConfirmDeleteLanguageProfile: PropTypes.func.isRequired,
onCloneLanguageProfilePress: PropTypes.func.isRequired
};
export default LanguageProfile;

View File

@ -1,44 +0,0 @@
.languageProfileItem {
display: flex;
align-items: stretch;
width: 100%;
border: 1px solid #aaa;
border-radius: 4px;
background: var(--inputBackgroundColor);
}
.checkContainer {
position: relative;
margin-right: 4px;
margin-bottom: 7px;
margin-left: 8px;
}
.languageName {
display: flex;
flex-grow: 1;
margin-bottom: 0;
margin-left: 2px;
font-weight: normal;
line-height: 36px;
cursor: pointer;
}
.dragHandle {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-left: auto;
width: $dragHandleWidth;
text-align: center;
cursor: grab;
}
.dragIcon {
top: 0;
}
.isDragging {
opacity: 0.25;
}

View File

@ -1,83 +0,0 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import CheckInput from 'Components/Form/CheckInput';
import Icon from 'Components/Icon';
import { icons } from 'Helpers/Props';
import styles from './LanguageProfileItem.css';
class LanguageProfileItem extends Component {
//
// Listeners
onAllowedChange = ({ value }) => {
const {
languageId,
onLanguageProfileItemAllowedChange
} = this.props;
onLanguageProfileItemAllowedChange(languageId, value);
};
//
// Render
render() {
const {
name,
allowed,
isDragging,
connectDragSource
} = this.props;
return (
<div
className={classNames(
styles.languageProfileItem,
isDragging && styles.isDragging
)}
>
<label
className={styles.languageName}
>
<CheckInput
containerClassName={styles.checkContainer}
name={name}
value={allowed}
onChange={this.onAllowedChange}
/>
{name}
</label>
{
connectDragSource(
<div className={styles.dragHandle}>
<Icon
className={styles.dragIcon}
name={icons.REORDER}
/>
</div>
)
}
</div>
);
}
}
LanguageProfileItem.propTypes = {
languageId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
connectDragSource: PropTypes.func,
onLanguageProfileItemAllowedChange: PropTypes.func
};
LanguageProfileItem.defaultProps = {
// The drag preview will not connect the drag handle.
connectDragSource: (node) => node
};
export default LanguageProfileItem;

View File

@ -1,4 +0,0 @@
.dragPreview {
width: 380px;
opacity: 0.75;
}

View File

@ -1,88 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DragLayer } from 'react-dnd';
import DragPreviewLayer from 'Components/DragPreviewLayer';
import { QUALITY_PROFILE_ITEM } from 'Helpers/dragTypes';
import dimensions from 'Styles/Variables/dimensions.js';
import LanguageProfileItem from './LanguageProfileItem';
import styles from './LanguageProfileItemDragPreview.css';
const formGroupSmallWidth = parseInt(dimensions.formGroupSmallWidth);
const formLabelLargeWidth = parseInt(dimensions.formLabelLargeWidth);
const formLabelRightMarginWidth = parseInt(dimensions.formLabelRightMarginWidth);
const dragHandleWidth = parseInt(dimensions.dragHandleWidth);
function collectDragLayer(monitor) {
return {
item: monitor.getItem(),
itemType: monitor.getItemType(),
currentOffset: monitor.getSourceClientOffset()
};
}
class LanguageProfileItemDragPreview extends Component {
//
// Render
render() {
const {
item,
itemType,
currentOffset
} = this.props;
if (!currentOffset || itemType !== QUALITY_PROFILE_ITEM) {
return null;
}
// The offset is shifted because the drag handle is on the right edge of the
// list item and the preview is wider than the drag handle.
const { x, y } = currentOffset;
const handleOffset = formGroupSmallWidth - formLabelLargeWidth - formLabelRightMarginWidth - dragHandleWidth;
const transform = `translate3d(${x - handleOffset}px, ${y}px, 0)`;
const style = {
position: 'absolute',
WebkitTransform: transform,
msTransform: transform,
transform
};
const {
languageId,
name,
allowed,
sortIndex
} = item;
return (
<DragPreviewLayer>
<div
className={styles.dragPreview}
style={style}
>
<LanguageProfileItem
languageId={languageId}
name={name}
allowed={allowed}
sortIndex={sortIndex}
isDragging={false}
/>
</div>
</DragPreviewLayer>
);
}
}
LanguageProfileItemDragPreview.propTypes = {
item: PropTypes.object,
itemType: PropTypes.string,
currentOffset: PropTypes.shape({
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired
})
};
export default DragLayer(collectDragLayer)(LanguageProfileItemDragPreview);

View File

@ -1,18 +0,0 @@
.languageProfileItemDragSource {
padding: 4px 0;
}
.languageProfileItemPlaceholder {
width: 100%;
height: 36px;
border: 1px dotted #aaa;
border-radius: 4px;
}
.languageProfileItemPlaceholderBefore {
margin-bottom: 8px;
}
.languageProfileItemPlaceholderAfter {
margin-top: 8px;
}

View File

@ -1,157 +0,0 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { DragSource, DropTarget } from 'react-dnd';
import { findDOMNode } from 'react-dom';
import { QUALITY_PROFILE_ITEM } from 'Helpers/dragTypes';
import LanguageProfileItem from './LanguageProfileItem';
import styles from './LanguageProfileItemDragSource.css';
const languageProfileItemDragSource = {
beginDrag({ languageId, name, allowed, sortIndex }) {
return {
languageId,
name,
allowed,
sortIndex
};
},
endDrag(props, monitor, component) {
props.onLanguageProfileItemDragEnd(monitor.getItem(), monitor.didDrop());
}
};
const languageProfileItemDropTarget = {
hover(props, monitor, component) {
const dragIndex = monitor.getItem().sortIndex;
const hoverIndex = props.sortIndex;
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
// Moving up, only trigger if drag position is above 50%
if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
// Moving down, only trigger if drag position is below 50%
if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
props.onLanguageProfileItemDragMove(dragIndex, hoverIndex);
}
};
function collectDragSource(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
};
}
function collectDropTarget(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver()
};
}
class LanguageProfileItemDragSource extends Component {
//
// Render
render() {
const {
languageId,
name,
allowed,
sortIndex,
isDragging,
isDraggingUp,
isDraggingDown,
isOver,
connectDragSource,
connectDropTarget,
onLanguageProfileItemAllowedChange
} = this.props;
const isBefore = !isDragging && isDraggingUp && isOver;
const isAfter = !isDragging && isDraggingDown && isOver;
// if (isDragging && !isOver) {
// return null;
// }
return connectDropTarget(
<div
className={classNames(
styles.languageProfileItemDragSource,
isBefore && styles.isDraggingUp,
isAfter && styles.isDraggingDown
)}
>
{
isBefore &&
<div
className={classNames(
styles.languageProfileItemPlaceholder,
styles.languageProfileItemPlaceholderBefore
)}
/>
}
<LanguageProfileItem
languageId={languageId}
name={name}
allowed={allowed}
sortIndex={sortIndex}
isDragging={isDragging}
isOver={isOver}
connectDragSource={connectDragSource}
onLanguageProfileItemAllowedChange={onLanguageProfileItemAllowedChange}
/>
{
isAfter &&
<div
className={classNames(
styles.languageProfileItemPlaceholder,
styles.languageProfileItemPlaceholderAfter
)}
/>
}
</div>
);
}
}
LanguageProfileItemDragSource.propTypes = {
languageId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
allowed: PropTypes.bool.isRequired,
sortIndex: PropTypes.number.isRequired,
isDragging: PropTypes.bool,
isDraggingUp: PropTypes.bool,
isDraggingDown: PropTypes.bool,
isOver: PropTypes.bool,
connectDragSource: PropTypes.func,
connectDropTarget: PropTypes.func,
onLanguageProfileItemAllowedChange: PropTypes.func.isRequired,
onLanguageProfileItemDragMove: PropTypes.func.isRequired,
onLanguageProfileItemDragEnd: PropTypes.func.isRequired
};
export default DropTarget(
QUALITY_PROFILE_ITEM,
languageProfileItemDropTarget,
collectDropTarget
)(DragSource(
QUALITY_PROFILE_ITEM,
languageProfileItemDragSource,
collectDragSource
)(LanguageProfileItemDragSource));

View File

@ -1,6 +0,0 @@
.languages {
margin-top: 10px;
/* TODO: This should consider the number of languages in the list */
min-height: 550px;
user-select: none;
}

View File

@ -1,103 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import FormLabel from 'Components/Form/FormLabel';
import LanguageProfileItemDragPreview from './LanguageProfileItemDragPreview';
import LanguageProfileItemDragSource from './LanguageProfileItemDragSource';
import styles from './LanguageProfileItems.css';
class LanguageProfileItems extends Component {
//
// Render
render() {
const {
dragIndex,
dropIndex,
languageProfileItems,
errors,
warnings,
...otherProps
} = this.props;
const isDragging = dropIndex !== null;
const isDraggingUp = isDragging && dropIndex > dragIndex;
const isDraggingDown = isDragging && dropIndex < dragIndex;
return (
<FormGroup>
<FormLabel>Languages</FormLabel>
<div>
<FormInputHelpText
text="Languages higher in the list are more preferred. Only checked languages are wanted"
/>
{
errors.map((error, index) => {
return (
<FormInputHelpText
key={index}
text={error.message}
isError={true}
isCheckInput={false}
/>
);
})
}
{
warnings.map((warning, index) => {
return (
<FormInputHelpText
key={index}
text={warning.message}
isWarning={true}
isCheckInput={false}
/>
);
})
}
<div className={styles.languages}>
{
languageProfileItems.map(({ allowed, language }, index) => {
return (
<LanguageProfileItemDragSource
key={language.id}
languageId={language.id}
name={language.name}
allowed={allowed}
sortIndex={index}
isDragging={isDragging}
isDraggingUp={isDraggingUp}
isDraggingDown={isDraggingDown}
{...otherProps}
/>
);
}).reverse()
}
<LanguageProfileItemDragPreview />
</div>
</div>
</FormGroup>
);
}
}
LanguageProfileItems.propTypes = {
dragIndex: PropTypes.number,
dropIndex: PropTypes.number,
languageProfileItems: PropTypes.arrayOf(PropTypes.object).isRequired,
errors: PropTypes.arrayOf(PropTypes.object),
warnings: PropTypes.arrayOf(PropTypes.object)
};
LanguageProfileItems.defaultProps = {
errors: [],
warnings: []
};
export default LanguageProfileItems;

View File

@ -1,31 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createLanguageProfileSelector from 'Store/Selectors/createLanguageProfileSelector';
function createMapStateToProps() {
return createSelector(
createLanguageProfileSelector(),
(languageProfile) => {
return {
name: languageProfile.name
};
}
);
}
function LanguageProfileNameConnector({ name, ...otherProps }) {
return (
<span>
{name}
</span>
);
}
LanguageProfileNameConnector.propTypes = {
languageProfileId: PropTypes.number.isRequired,
name: PropTypes.string.isRequired
};
export default connect(createMapStateToProps)(LanguageProfileNameConnector);

View File

@ -1,21 +0,0 @@
.languageProfiles {
display: flex;
flex-wrap: wrap;
}
.addLanguageProfile {
composes: languageProfile from '~./LanguageProfile.css';
background-color: var(--cardAlternateBackgroundColor);
color: var(--gray);
text-align: center;
font-size: 45px;
}
.center {
display: inline-block;
padding: 5px 20px 0;
border: 1px solid var(--borderColor);
border-radius: 4px;
background-color: var(--cardCenterBackgroundColor);
}

View File

@ -1,107 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Card from 'Components/Card';
import FieldSet from 'Components/FieldSet';
import Icon from 'Components/Icon';
import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons } from 'Helpers/Props';
import EditLanguageProfileModalConnector from './EditLanguageProfileModalConnector';
import LanguageProfile from './LanguageProfile';
import styles from './LanguageProfiles.css';
class LanguageProfiles extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isLanguageProfileModalOpen: false
};
}
//
// Listeners
onCloneLanguageProfilePress = (id) => {
this.props.onCloneLanguageProfilePress(id);
this.setState({ isLanguageProfileModalOpen: true });
};
onEditLanguageProfilePress = () => {
this.setState({ isLanguageProfileModalOpen: true });
};
onModalClose = () => {
this.setState({ isLanguageProfileModalOpen: false });
};
//
// Render
render() {
const {
items,
isDeleting,
onConfirmDeleteLanguageProfile,
onCloneLanguageProfilePress,
...otherProps
} = this.props;
return (
<FieldSet legend="Language Profiles">
<PageSectionContent
errorMessage="Unable to load Language Profiles"
{...otherProps}
>
<div className={styles.languageProfiles}>
{
items.map((item) => {
return (
<LanguageProfile
key={item.id}
{...item}
isDeleting={isDeleting}
onConfirmDeleteLanguageProfile={onConfirmDeleteLanguageProfile}
onCloneLanguageProfilePress={this.onCloneLanguageProfilePress}
/>
);
})
}
<Card
className={styles.addLanguageProfile}
onPress={this.onEditLanguageProfilePress}
>
<div className={styles.center}>
<Icon
name={icons.ADD}
size={45}
/>
</div>
</Card>
</div>
<EditLanguageProfileModalConnector
isOpen={this.state.isLanguageProfileModalOpen}
onModalClose={this.onModalClose}
/>
</PageSectionContent>
</FieldSet>
);
}
}
LanguageProfiles.propTypes = {
advancedSettings: PropTypes.bool.isRequired,
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
isDeleting: PropTypes.bool.isRequired,
onConfirmDeleteLanguageProfile: PropTypes.func.isRequired,
onCloneLanguageProfilePress: PropTypes.func.isRequired
};
export default LanguageProfiles;

View File

@ -1,69 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { cloneLanguageProfile, deleteLanguageProfile, fetchLanguageProfiles } from 'Store/Actions/settingsActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import sortByName from 'Utilities/Array/sortByName';
import LanguageProfiles from './LanguageProfiles';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.advancedSettings,
createSortedSectionSelector('settings.languageProfiles', sortByName),
(advancedSettings, languageProfiles) => {
return {
advancedSettings,
...languageProfiles
};
}
);
}
const mapDispatchToProps = {
dispatchFetchLanguageProfiles: fetchLanguageProfiles,
dispatchDeleteLanguageProfile: deleteLanguageProfile,
dispatchCloneLanguageProfile: cloneLanguageProfile
};
class LanguageProfilesConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.dispatchFetchLanguageProfiles();
}
//
// Listeners
onConfirmDeleteLanguageProfile = (id) => {
this.props.dispatchDeleteLanguageProfile({ id });
};
onCloneLanguageProfilePress = (id) => {
this.props.dispatchCloneLanguageProfile({ id });
};
//
// Render
render() {
return (
<LanguageProfiles
onConfirmDeleteLanguageProfile={this.onConfirmDeleteLanguageProfile}
onCloneLanguageProfilePress={this.onCloneLanguageProfilePress}
{...this.props}
/>
);
}
}
LanguageProfilesConnector.propTypes = {
dispatchFetchLanguageProfiles: PropTypes.func.isRequired,
dispatchDeleteLanguageProfile: PropTypes.func.isRequired,
dispatchCloneLanguageProfile: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(LanguageProfilesConnector);

View File

@ -5,7 +5,6 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import DelayProfilesConnector from './Delay/DelayProfilesConnector';
import LanguageProfilesConnector from './Language/LanguageProfilesConnector';
import QualityProfilesConnector from './Quality/QualityProfilesConnector';
import ReleaseProfilesConnector from './Release/ReleaseProfilesConnector';
@ -25,7 +24,6 @@ class Profiles extends Component {
<PageContentBody>
<DndProvider options={HTML5toTouch}>
<QualityProfilesConnector />
<LanguageProfilesConnector />
<DelayProfilesConnector />
<ReleaseProfilesConnector />
</DndProvider>

View File

@ -3,7 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import { fetchLanguageProfileSchema, fetchUISettings, saveUISettings, setUISettingsValue } from 'Store/Actions/settingsActions';
import { fetchUISettings, saveUISettings, setUISettingsValue } from 'Store/Actions/settingsActions';
import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
import UISettings from './UISettings';
@ -20,11 +20,11 @@ function createFilteredLanguagesSelector() {
}
const newItems = languages.items
.filter((lang) => !FILTER_LANGUAGES.includes(lang.language.name))
.filter((lang) => !FILTER_LANGUAGES.includes(lang.name))
.map((item) => {
return {
key: item.language.id,
value: item.language.name
key: item.id,
value: item.name
};
});
@ -58,8 +58,7 @@ const mapDispatchToProps = {
dispatchSetUISettingsValue: setUISettingsValue,
dispatchSaveUISettings: saveUISettings,
dispatchFetchUISettings: fetchUISettings,
dispatchClearPendingChanges: clearPendingChanges,
dispatchFetchLanguageProfileSchema: fetchLanguageProfileSchema
dispatchClearPendingChanges: clearPendingChanges
};
class UISettingsConnector extends Component {
@ -69,16 +68,10 @@ class UISettingsConnector extends Component {
componentDidMount() {
const {
isLanguagesPopulated,
dispatchFetchUISettings,
dispatchFetchLanguageProfileSchema
dispatchFetchUISettings
} = this.props;
dispatchFetchUISettings();
if (!isLanguagesPopulated) {
dispatchFetchLanguageProfileSchema();
}
}
componentWillUnmount() {
@ -115,8 +108,7 @@ UISettingsConnector.propTypes = {
dispatchSetUISettingsValue: PropTypes.func.isRequired,
dispatchSaveUISettings: PropTypes.func.isRequired,
dispatchFetchUISettings: PropTypes.func.isRequired,
dispatchClearPendingChanges: PropTypes.func.isRequired,
dispatchFetchLanguageProfileSchema: PropTypes.func.isRequired
dispatchClearPendingChanges: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(UISettingsConnector);

View File

@ -1,97 +0,0 @@
import { createAction } from 'redux-actions';
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import createFetchSchemaHandler from 'Store/Actions/Creators/createFetchSchemaHandler';
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks';
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
//
// Variables
const section = 'settings.languageProfiles';
//
// Actions Types
export const FETCH_LANGUAGE_PROFILES = 'settings/languageProfiles/fetchLanguageProfiles';
export const FETCH_LANGUAGE_PROFILE_SCHEMA = 'settings/languageProfiles/fetchLanguageProfileSchema';
export const SAVE_LANGUAGE_PROFILE = 'settings/languageProfiles/saveLanguageProfile';
export const DELETE_LANGUAGE_PROFILE = 'settings/languageProfiles/deleteLanguageProfile';
export const SET_LANGUAGE_PROFILE_VALUE = 'settings/languageProfiles/setLanguageProfileValue';
export const CLONE_LANGUAGE_PROFILE = 'settings/languageProfiles/cloneLanguageProfile';
//
// Action Creators
export const fetchLanguageProfiles = createThunk(FETCH_LANGUAGE_PROFILES);
export const fetchLanguageProfileSchema = createThunk(FETCH_LANGUAGE_PROFILE_SCHEMA);
export const saveLanguageProfile = createThunk(SAVE_LANGUAGE_PROFILE);
export const deleteLanguageProfile = createThunk(DELETE_LANGUAGE_PROFILE);
export const setLanguageProfileValue = createAction(SET_LANGUAGE_PROFILE_VALUE, (payload) => {
return {
section,
...payload
};
});
export const cloneLanguageProfile = createAction(CLONE_LANGUAGE_PROFILE);
//
// Details
export default {
//
// State
defaultState: {
isFetching: false,
isPopulated: false,
error: null,
isDeleting: false,
deleteError: null,
isSchemaFetching: false,
isSchemaPopulated: false,
schemaError: null,
schema: {},
isSaving: false,
saveError: null,
items: [],
pendingChanges: {}
},
//
// Action Handlers
actionHandlers: {
[FETCH_LANGUAGE_PROFILES]: createFetchHandler(section, '/languageprofile'),
[FETCH_LANGUAGE_PROFILE_SCHEMA]: createFetchSchemaHandler(section, '/languageprofile/schema'),
[SAVE_LANGUAGE_PROFILE]: createSaveProviderHandler(section, '/languageprofile'),
[DELETE_LANGUAGE_PROFILE]: createRemoveItemHandler(section, '/languageprofile')
},
//
// Reducers
reducers: {
[SET_LANGUAGE_PROFILE_VALUE]: createSetSettingValueReducer(section),
[CLONE_LANGUAGE_PROFILE]: function(state, { payload }) {
const id = payload.id;
const newState = getSectionState(state, section);
const item = newState.items.find((i) => i.id === id);
const pendingChanges = { ...item, id: 0 };
delete pendingChanges.id;
pendingChanges.name = `${pendingChanges.name} - Copy`;
newState.pendingChanges = pendingChanges;
return updateSectionState(state, section, newState);
}
}
};

View File

@ -0,0 +1,48 @@
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import { createThunk } from 'Store/thunks';
//
// Variables
const section = 'settings.languages';
//
// Actions Types
export const FETCH_LANGUAGES = 'settings/languages/fetchLanguages';
//
// Action Creators
export const fetchLanguages = createThunk(FETCH_LANGUAGES);
//
// Details
export default {
//
// State
defaultState: {
isFetching: false,
isPopulated: false,
error: null,
items: []
},
//
// Action Handlers
actionHandlers: {
[FETCH_LANGUAGES]: createFetchHandler(section, '/language')
},
//
// Reducers
reducers: {
}
};

View File

@ -34,7 +34,6 @@ export const defaultState = {
rootFolderPath: '',
monitor: monitorOptions[0].key,
qualityProfileId: 0,
languageProfileId: 0,
seriesType: seriesTypes.STANDARD,
seasonFolder: true,
searchForMissingEpisodes: false,

View File

@ -43,8 +43,8 @@ export const defaultState = {
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isVisible: false
},
{

View File

@ -60,8 +60,8 @@ export const defaultState = {
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isVisible: false
},
{

View File

@ -162,7 +162,7 @@ export const actionHandlers = handleThunks({
props.qualityCutoffNotMet = episodeFile.qualityCutoffNotMet;
props.languageCutoffNotMet = episodeFile.languageCutoffNotMet;
props.language = file.language;
props.languages = file.languages;
props.quality = file.quality;
props.releaseGroup = file.releaseGroup;

View File

@ -52,8 +52,8 @@ export const defaultState = {
isVisible: true
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isVisible: false
},
{

View File

@ -177,7 +177,7 @@ export const actionHandlers = handleThunks({
seasonNumber: item.seasonNumber,
episodeIds: (item.episodes || []).map((e) => e.id),
quality: item.quality,
language: item.language,
languages: item.languages,
releaseGroup: item.releaseGroup,
downloadId: item.downloadId
};

View File

@ -87,8 +87,8 @@ export const defaultState = {
isVisible: false
},
{
name: 'language',
label: 'Language',
name: 'languages',
label: 'Languages',
isSortable: true,
isVisible: false
},

View File

@ -204,12 +204,6 @@ export const filterBuilderProps = [
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.QUALITY_PROFILE
},
{
name: 'languageProfileId',
label: 'Language Profile',
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.LANGUAGE_PROFILE
},
{
name: 'nextAiring',
label: 'Next Airing',

View File

@ -53,12 +53,6 @@ export const defaultState = {
isSortable: true,
isVisible: true
},
{
name: 'languageProfileId',
label: 'Language Profile',
isSortable: true,
isVisible: true
},
{
name: 'seriesType',
label: 'Type',

Some files were not shown because too many files have changed in this diff Show More