1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-19 21:46:50 -04:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Bogdan 02e2e09ad3 Fixed: Use movie file from state for status label in add search results 2024-01-18 01:33:50 +02:00
445 changed files with 6815 additions and 9235 deletions
+2 -2
View File
@@ -9,14 +9,14 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '5.4.0'
majorVersion: '5.3.2'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.417'
nodeVersion: '20.X'
nodeVersion: '16.X'
innoVersion: '6.2.2'
windowsImage: 'windows-2022'
linuxImage: 'ubuntu-20.04'
-2
View File
@@ -2,8 +2,6 @@ const loose = true;
module.exports = {
plugins: [
'@babel/plugin-transform-logical-assignment-operators',
// Stage 1
'@babel/plugin-proposal-export-default-from',
['@babel/plugin-transform-optional-chaining', { loose }],
+3 -10
View File
@@ -25,7 +25,7 @@ import toggleSelected from 'Utilities/Table/toggleSelected';
import QueueFilterModal from './QueueFilterModal';
import QueueOptionsConnector from './QueueOptionsConnector';
import QueueRowConnector from './QueueRowConnector';
import RemoveQueueItemModal from './RemoveQueueItemModal';
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
class Queue extends Component {
@@ -307,16 +307,9 @@ class Queue extends Component {
}
</PageContentBody>
<RemoveQueueItemModal
<RemoveQueueItemsModal
isOpen={isConfirmRemoveModalOpen}
selectedCount={selectedCount}
canChangeCategory={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);
return !!(item && item.downloadClientHasPostImportCategory);
})
)}
canIgnore={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);
@@ -324,7 +317,7 @@ class Queue extends Component {
return !!(item && item.movieId);
})
)}
pending={isConfirmRemoveModalOpen && (
allPending={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);
-3
View File
@@ -96,7 +96,6 @@ class QueueRow extends Component {
indexer,
outputPath,
downloadClient,
downloadClientHasPostImportCategory,
estimatedCompletionTime,
added,
timeleft,
@@ -374,7 +373,6 @@ class QueueRow extends Component {
<RemoveQueueItemModal
isOpen={isRemoveQueueItemModalOpen}
sourceTitle={title}
canChangeCategory={!!downloadClientHasPostImportCategory}
canIgnore={!!movie}
isPending={isPending}
onRemovePress={this.onRemoveQueueItemModalConfirmed}
@@ -404,7 +402,6 @@ QueueRow.propTypes = {
indexer: PropTypes.string,
outputPath: PropTypes.string,
downloadClient: PropTypes.string,
downloadClientHasPostImportCategory: PropTypes.bool,
estimatedCompletionTime: PropTypes.string,
added: PropTypes.string,
timeleft: PropTypes.string,
@@ -0,0 +1,171 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
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 Modal from 'Components/Modal/Modal';
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, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class RemoveQueueItemModal extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
remove: true,
blocklist: false,
skipRedownload: false
};
}
//
// Control
resetState = function() {
this.setState({
remove: true,
blocklist: false,
skipRedownload: false
});
};
//
// Listeners
onRemoveChange = ({ value }) => {
this.setState({ remove: value });
};
onBlocklistChange = ({ value }) => {
this.setState({ blocklist: value });
};
onSkipRedownloadChange = ({ value }) => {
this.setState({ skipRedownload: value });
};
onRemoveConfirmed = () => {
const state = this.state;
this.resetState();
this.props.onRemovePress(state);
};
onModalClose = () => {
this.resetState();
this.props.onModalClose();
};
//
// Render
render() {
const {
isOpen,
sourceTitle,
canIgnore,
isPending
} = this.props;
const { remove, blocklist, skipRedownload } = this.state;
return (
<Modal
isOpen={isOpen}
size={sizes.MEDIUM}
onModalClose={this.onModalClose}
>
<ModalContent
onModalClose={this.onModalClose}
>
<ModalHeader>
{translate('RemoveQueueItem', { sourceTitle })}
</ModalHeader>
<ModalBody>
<div>
{translate('RemoveQueueItemConfirmation', { sourceTitle })}
</div>
{
isPending ?
null :
<FormGroup>
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="remove"
value={remove}
helpTextWarning={translate('RemoveFromDownloadClientHelpTextWarning')}
isDisabled={!canIgnore}
onChange={this.onRemoveChange}
/>
</FormGroup>
}
<FormGroup>
<FormLabel>{translate('BlocklistRelease')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blocklist"
value={blocklist}
helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange}
/>
</FormGroup>
{
blocklist ?
<FormGroup>
<FormLabel>{translate('SkipRedownload')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipRedownload"
value={skipRedownload}
helpText={translate('SkipRedownloadHelpText')}
onChange={this.onSkipRedownloadChange}
/>
</FormGroup> :
null
}
</ModalBody>
<ModalFooter>
<Button onPress={this.onModalClose}>
{translate('Close')}
</Button>
<Button
kind={kinds.DANGER}
onPress={this.onRemoveConfirmed}
>
{translate('Remove')}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
}
RemoveQueueItemModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired,
canIgnore: PropTypes.bool.isRequired,
isPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default RemoveQueueItemModal;
@@ -1,230 +0,0 @@
import React, { useCallback, useMemo, useState } from 'react';
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 Modal from 'Components/Modal/Modal';
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, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './RemoveQueueItemModal.css';
interface RemovePressProps {
remove: boolean;
changeCategory: boolean;
blocklist: boolean;
skipRedownload: boolean;
}
interface RemoveQueueItemModalProps {
isOpen: boolean;
sourceTitle: string;
canChangeCategory: boolean;
canIgnore: boolean;
isPending: boolean;
selectedCount?: number;
onRemovePress(props: RemovePressProps): void;
onModalClose: () => void;
}
type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore';
type BlocklistMethod =
| 'doNotBlocklist'
| 'blocklistAndSearch'
| 'blocklistOnly';
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
const {
isOpen,
sourceTitle,
canIgnore,
canChangeCategory,
isPending,
selectedCount,
onRemovePress,
onModalClose,
} = props;
const multipleSelected = selectedCount && selectedCount > 1;
const [removalMethod, setRemovalMethod] =
useState<RemovalMethod>('removeFromClient');
const [blocklistMethod, setBlocklistMethod] =
useState<BlocklistMethod>('doNotBlocklist');
const { title, message } = useMemo(() => {
if (!selectedCount) {
return {
title: translate('RemoveQueueItem', { sourceTitle }),
message: translate('RemoveQueueItemConfirmation', { sourceTitle }),
};
}
if (selectedCount === 1) {
return {
title: translate('RemoveSelectedItem'),
message: translate('RemoveSelectedItemQueueMessageText'),
};
}
return {
title: translate('RemoveSelectedItems'),
message: translate('RemoveSelectedItemsQueueMessageText', {
selectedCount,
}),
};
}, [sourceTitle, selectedCount]);
const removalMethodOptions = useMemo(() => {
return [
{
key: 'removeFromClient',
value: translate('RemoveFromDownloadClient'),
hint: multipleSelected
? translate('RemoveMultipleFromDownloadClientHint')
: translate('RemoveFromDownloadClientHint'),
},
{
key: 'changeCategory',
value: translate('ChangeCategory'),
isDisabled: !canChangeCategory,
hint: multipleSelected
? translate('ChangeCategoryMultipleHint')
: translate('ChangeCategoryHint'),
},
{
key: 'ignore',
value: multipleSelected
? translate('IgnoreDownloads')
: translate('IgnoreDownload'),
isDisabled: !canIgnore,
hint: multipleSelected
? translate('IgnoreDownloadsHint')
: translate('IgnoreDownloadHint'),
},
];
}, [canChangeCategory, canIgnore, multipleSelected]);
const blocklistMethodOptions = useMemo(() => {
return [
{
key: 'doNotBlocklist',
value: translate('DoNotBlocklist'),
hint: translate('DoNotBlocklistHint'),
},
{
key: 'blocklistAndSearch',
value: translate('BlocklistAndSearch'),
hint: multipleSelected
? translate('BlocklistAndSearchMultipleHint')
: translate('BlocklistAndSearchHint'),
},
{
key: 'blocklistOnly',
value: translate('BlocklistOnly'),
hint: multipleSelected
? translate('BlocklistMultipleOnlyHint')
: translate('BlocklistOnlyHint'),
},
];
}, [multipleSelected]);
const handleRemovalMethodChange = useCallback(
({ value }: { value: RemovalMethod }) => {
setRemovalMethod(value);
},
[setRemovalMethod]
);
const handleBlocklistMethodChange = useCallback(
({ value }: { value: BlocklistMethod }) => {
setBlocklistMethod(value);
},
[setBlocklistMethod]
);
const handleConfirmRemove = useCallback(() => {
onRemovePress({
remove: removalMethod === 'removeFromClient',
changeCategory: removalMethod === 'changeCategory',
blocklist: blocklistMethod !== 'doNotBlocklist',
skipRedownload: blocklistMethod === 'blocklistOnly',
});
setRemovalMethod('removeFromClient');
setBlocklistMethod('doNotBlocklist');
}, [
removalMethod,
blocklistMethod,
setRemovalMethod,
setBlocklistMethod,
onRemovePress,
]);
const handleModalClose = useCallback(() => {
setRemovalMethod('removeFromClient');
setBlocklistMethod('doNotBlocklist');
onModalClose();
}, [setRemovalMethod, setBlocklistMethod, onModalClose]);
return (
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={handleModalClose}>
<ModalContent onModalClose={handleModalClose}>
<ModalHeader>{title}</ModalHeader>
<ModalBody>
<div className={styles.message}>{message}</div>
{isPending ? null : (
<FormGroup>
<FormLabel>{translate('RemoveQueueItemRemovalMethod')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="removalMethod"
value={removalMethod}
values={removalMethodOptions}
isDisabled={!canChangeCategory && !canIgnore}
helpTextWarning={translate(
'RemoveQueueItemRemovalMethodHelpTextWarning'
)}
onChange={handleRemovalMethodChange}
/>
</FormGroup>
)}
<FormGroup>
<FormLabel>
{multipleSelected
? translate('BlocklistReleases')
: translate('BlocklistRelease')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="blocklistMethod"
value={blocklistMethod}
values={blocklistMethodOptions}
helpText={translate('BlocklistReleaseHelpText')}
onChange={handleBlocklistMethodChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button onPress={handleModalClose}>{translate('Close')}</Button>
<Button kind={kinds.DANGER} onPress={handleConfirmRemove}>
{translate('Remove')}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
export default RemoveQueueItemModal;
@@ -0,0 +1,174 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
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 Modal from 'Components/Modal/Modal';
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, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './RemoveQueueItemsModal.css';
class RemoveQueueItemsModal extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
remove: true,
blocklist: false,
skipRedownload: false
};
}
//
// Control
resetState = function() {
this.setState({
remove: true,
blocklist: false,
skipRedownload: false
});
};
//
// Listeners
onRemoveChange = ({ value }) => {
this.setState({ remove: value });
};
onBlocklistChange = ({ value }) => {
this.setState({ blocklist: value });
};
onSkipRedownloadChange = ({ value }) => {
this.setState({ skipRedownload: value });
};
onRemoveConfirmed = () => {
const state = this.state;
this.resetState();
this.props.onRemovePress(state);
};
onModalClose = () => {
this.resetState();
this.props.onModalClose();
};
//
// Render
render() {
const {
isOpen,
selectedCount,
canIgnore,
allPending
} = this.props;
const { remove, blocklist, skipRedownload } = this.state;
return (
<Modal
isOpen={isOpen}
size={sizes.MEDIUM}
onModalClose={this.onModalClose}
>
<ModalContent
onModalClose={this.onModalClose}
>
<ModalHeader>
{selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')}
</ModalHeader>
<ModalBody>
<div className={styles.message}>
{selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', { selectedCount }) : translate('RemoveSelectedItemQueueMessageText')}
</div>
{
allPending ?
null :
<FormGroup>
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="remove"
value={remove}
helpTextWarning={translate('RemoveHelpTextWarning')}
isDisabled={!canIgnore}
onChange={this.onRemoveChange}
/>
</FormGroup>
}
<FormGroup>
<FormLabel>
{selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blocklist"
value={blocklist}
helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange}
/>
</FormGroup>
{
blocklist ?
<FormGroup>
<FormLabel>{translate('SkipRedownload')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipRedownload"
value={skipRedownload}
helpText={translate('SkipRedownloadHelpText')}
onChange={this.onSkipRedownloadChange}
/>
</FormGroup> :
null
}
</ModalBody>
<ModalFooter>
<Button onPress={this.onModalClose}>
{translate('Close')}
</Button>
<Button
kind={kinds.DANGER}
onPress={this.onRemoveConfirmed}
>
{translate('Remove')}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
}
RemoveQueueItemsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
selectedCount: PropTypes.number.isRequired,
canIgnore: PropTypes.bool.isRequired,
allPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default RemoveQueueItemsModal;
@@ -69,7 +69,7 @@ class AddNewMovieConnector extends Component {
const movieIds = selectUniqueIds(items, 'internalId');
if (movieIds.length) {
this.props.fetchMovieFiles({ movieId: movieIds });
movieIds.map((movieId) => this.props.fetchMovieFiles({ movieId }));
}
}
}
@@ -75,6 +75,7 @@ class AddNewMovieSearchResult extends Component {
colorImpairedMode,
id,
monitored,
hasFile,
isAvailable,
movieFile,
queueItem,
@@ -87,8 +88,6 @@ class AddNewMovieSearchResult extends Component {
isNewAddMovieModalOpen
} = this.state;
const hasMovieFile = !!movieFile;
const linkProps = isExistingMovie ? { to: `/movie/${titleSlug}` } : { onPress: this.onPress };
const posterWidth = 167;
const posterHeight = 250;
@@ -127,7 +126,7 @@ class AddNewMovieSearchResult extends Component {
movieId={existingMovieId}
movieFile={movieFile}
monitored={monitored}
hasFile={hasMovieFile}
hasFile={hasFile}
status={status}
width={posterWidth}
detailedProgressBar={true}
@@ -271,7 +270,7 @@ class AddNewMovieSearchResult extends Component {
{
isExistingMovie && isSmallScreen &&
<MovieStatusLabel
hasMovieFiles={hasMovieFile}
hasMovieFiles={hasFile}
monitored={monitored}
isAvailable={isAvailable}
queueItem={queueItem}
@@ -323,6 +322,7 @@ AddNewMovieSearchResult.propTypes = {
isSmallScreen: PropTypes.bool.isRequired,
id: PropTypes.number,
monitored: PropTypes.bool.isRequired,
hasFile: PropTypes.bool.isRequired,
isAvailable: PropTypes.bool.isRequired,
movieFile: PropTypes.object,
queueItem: PropTypes.object,
@@ -25,6 +25,7 @@ function createMapStateToProps() {
isSmallScreen: dimensions.isSmallScreen,
queueItem,
movieFile,
hasFile: !!movieFile,
movieRuntimeFormat
};
}
@@ -10,7 +10,6 @@ import styles from './ImportMovieRow.css';
function ImportMovieRow(props) {
const {
id,
relativePath,
monitor,
qualityProfileId,
minimumAvailability,
@@ -32,7 +31,7 @@ function ImportMovieRow(props) {
/>
<VirtualTableRowCell className={styles.folder}>
{relativePath}
{id}
</VirtualTableRowCell>
<VirtualTableRowCell className={styles.movie}>
@@ -74,7 +73,6 @@ function ImportMovieRow(props) {
ImportMovieRow.propTypes = {
id: PropTypes.string.isRequired,
relativePath: PropTypes.string.isRequired,
monitor: PropTypes.string.isRequired,
qualityProfileId: PropTypes.number.isRequired,
minimumAvailability: PropTypes.string.isRequired,
@@ -30,7 +30,7 @@ class ImportMovieTable extends Component {
unmappedFolders.forEach((unmappedFolder) => {
const id = unmappedFolder.name;
onMovieLookup(id, unmappedFolder.path, unmappedFolder.relativePath);
onMovieLookup(id, unmappedFolder.path);
onSetImportMovieValue({
id,
@@ -25,11 +25,10 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) {
return {
onMovieLookup(name, path, relativePath) {
onMovieLookup(name, path) {
dispatch(queueLookupMovie({
name,
path,
relativePath,
term: name
}));
},
+1 -1
View File
@@ -147,7 +147,7 @@ class AgendaEvent extends Component {
className={styles.statusIcon}
name={icons.MOVIE_FILE}
kind={kinds.WARNING}
title={translate('QualityCutoffNotMet')}
title={translate('QualityCutoffHasNotBeenMet')}
/>
}
</div>
+3 -1
View File
@@ -33,7 +33,9 @@ class Calendar extends Component {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>{translate('CalendarLoadError')}</Alert>
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadTheCalendar')}
</Alert>
}
{
+3 -3
View File
@@ -104,7 +104,7 @@ class CalendarPage extends Component {
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label={translate('ICalLink')}
label={translate('iCalLink')}
iconName={icons.CALENDAR}
onPress={this.onGetCalendarLinkPress}
/>
@@ -112,7 +112,7 @@ class CalendarPage extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label={translate('RssSync')}
label={translate('RSSSync')}
iconName={icons.RSS}
isSpinning={isRssSyncExecuting}
onPress={onRssSyncPress}
@@ -180,7 +180,7 @@ class CalendarPage extends Component {
{
!movieError && movieIsPopulated && !hasMovie &&
<NoMovie totalItems={0} />
<NoMovie />
}
{
@@ -48,10 +48,6 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
.statusContainer {
display: flex;
align-items: center;
&:global(.fullColor) {
filter: var(--calendarFullColorFilter)
}
}
.statusIcon {
+3 -11
View File
@@ -76,18 +76,12 @@ class CalendarEvent extends Component {
{title}
</div>
<div
className={classNames(
styles.statusContainer,
fullColorEvents && 'fullColor'
)}
>
<div className={styles.statusContainer}>
{
queueItem ?
<span className={styles.statusIcon}>
<CalendarEventQueueDetails
{...queueItem}
fullColorEvents={fullColorEvents}
/>
</span> :
null
@@ -104,14 +98,12 @@ class CalendarEvent extends Component {
}
{
showCutoffUnmetIcon &&
!!movieFile &&
movieFile.qualityCutoffNotMet ?
showCutoffUnmetIcon && !!movieFile && movieFile.qualityCutoffNotMet ?
<Icon
className={styles.statusIcon}
name={icons.MOVIE_FILE}
kind={kinds.WARNING}
title={translate('QualityCutoffNotMet')}
title={translate('QualityCutoffHasNotBeenMet')}
/> :
null
}
@@ -126,7 +126,7 @@ class CalendarHeader extends Component {
isDisabled={view === calendarViews.AGENDA}
onPress={onTodayPress}
>
{translate('Today')}
Today
</Button>
</div>
+3 -4
View File
@@ -20,11 +20,10 @@ function Legend(props) {
if (showCutoffUnmetIcon) {
iconsToShow.push(
<LegendIconItem
name={translate('CutoffNotMet')}
name={translate('CutoffUnmet')}
icon={icons.MOVIE_FILE}
kind={kinds.WARNING}
fullColorEvents={fullColorEvents}
tooltip={translate('QualityCutoffNotMet')}
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
tooltip={translate('QualityOrLangCutoffHasNotBeenMet')}
/>
);
}
@@ -4,8 +4,4 @@
.icon {
margin-right: 5px;
&:global(.fullColorEvents) {
filter: var(--calendarFullColorFilter)
}
}
@@ -1,4 +1,3 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
@@ -7,9 +6,9 @@ import styles from './LegendIconItem.css';
function LegendIconItem(props) {
const {
name,
fullColorEvents,
icon,
kind,
darken,
tooltip
} = props;
@@ -19,11 +18,9 @@ function LegendIconItem(props) {
title={tooltip}
>
<Icon
className={classNames(
styles.icon,
fullColorEvents && 'fullColorEvents'
)}
className={styles.icon}
name={icon}
darken={darken}
kind={kind}
/>
@@ -34,10 +31,14 @@ function LegendIconItem(props) {
LegendIconItem.propTypes = {
name: PropTypes.string.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
icon: PropTypes.object.isRequired,
kind: PropTypes.string.isRequired,
darken: PropTypes.bool.isRequired,
tooltip: PropTypes.string.isRequired
};
LegendIconItem.defaultProps = {
darken: false
};
export default LegendIconItem;
@@ -135,7 +135,7 @@ class CalendarOptionsModalContent extends Component {
type={inputTypes.CHECK}
name="showCutoffUnmetIcon"
value={showCutoffUnmetIcon}
helpText={translate('IconForCutoffUnmetHelpText')}
helpText={translate('ShowCutoffUnmetIconHelpText')}
onChange={this.onOptionInputChange}
/>
</FormGroup>
@@ -177,7 +177,7 @@ class CalendarOptionsModalContent extends Component {
values={weekColumnOptions}
value={calendarWeekColumnHeader}
onChange={this.onGlobalInputChange}
helpText={translate('WeekColumnHeaderHelpText')}
helpText={translate('SettingsWeekColumnHeaderHelpText')}
/>
</FormGroup>
@@ -109,7 +109,7 @@ class CalendarLinkModalContent extends Component {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('CalendarFeed')}
{translate('RadarrCalendarFeed')}
</ModalHeader>
<ModalBody>
@@ -121,19 +121,19 @@ class CalendarLinkModalContent extends Component {
type={inputTypes.CHECK}
name="unmonitored"
value={unmonitored}
helpText={translate('ICalIncludeUnmonitoredMoviesHelpText')}
helpText={translate('UnmonitoredHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('ICalShowAsAllDayEvents')}</FormLabel>
<FormLabel>{translate('ShowAsAllDayEvents')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="asAllDay"
value={asAllDay}
helpText={translate('ICalShowAsAllDayEventsHelpText')}
helpText={translate('AsAllDayHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
@@ -145,7 +145,7 @@ class CalendarLinkModalContent extends Component {
type={inputTypes.TAG}
name="tags"
value={tags}
helpText={translate('ICalTagsMoviesHelpText')}
helpText={translate('TagsHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>
@@ -160,7 +160,7 @@ class CalendarLinkModalContent extends Component {
name="iCalHttpUrl"
value={iCalHttpUrl}
readOnly={true}
helpText={translate('ICalFeedHelpText')}
helpText={translate('ICalHttpUrlHelpText')}
buttons={[
<ClipboardButton
key="copy"
+15 -75
View File
@@ -14,50 +14,6 @@ import styles from './CollectionFooter.css';
const NO_CHANGE = 'noChange';
const monitoredOptions = [
{
key: NO_CHANGE,
get value() {
return translate('NoChange');
},
disabled: true
},
{
key: 'monitored',
get value() {
return translate('Monitored');
}
},
{
key: 'unmonitored',
get value() {
return translate('Unmonitored');
}
}
];
const searchOnAddOptions = [
{
key: NO_CHANGE,
get value() {
return translate('NoChange');
},
disabled: true
},
{
key: 'yes',
get value() {
return translate('Yes');
}
},
{
key: 'no',
get value() {
return translate('No');
}
}
];
class CollectionFooter extends Component {
//
@@ -67,12 +23,12 @@ class CollectionFooter extends Component {
super(props, context);
this.state = {
monitored: NO_CHANGE,
monitor: NO_CHANGE,
monitored: NO_CHANGE,
qualityProfileId: NO_CHANGE,
minimumAvailability: NO_CHANGE,
rootFolderPath: NO_CHANGE,
searchOnAdd: NO_CHANGE
destinationRootFolder: null
};
}
@@ -88,9 +44,8 @@ class CollectionFooter extends Component {
monitored: NO_CHANGE,
monitor: NO_CHANGE,
qualityProfileId: NO_CHANGE,
minimumAvailability: NO_CHANGE,
rootFolderPath: NO_CHANGE,
searchOnAdd: NO_CHANGE
minimumAvailability: NO_CHANGE
});
}
@@ -108,12 +63,11 @@ class CollectionFooter extends Component {
onUpdateSelectedPress = () => {
const {
monitored,
monitor,
monitored,
qualityProfileId,
minimumAvailability,
rootFolderPath,
searchOnAdd
rootFolderPath
} = this.state;
const changes = {};
@@ -138,10 +92,6 @@ class CollectionFooter extends Component {
changes.rootFolderPath = rootFolderPath;
}
if (searchOnAdd !== NO_CHANGE) {
changes.searchOnAdd = searchOnAdd === 'yes';
}
this.props.onUpdateSelectedPress(changes);
};
@@ -159,10 +109,15 @@ class CollectionFooter extends Component {
monitor,
qualityProfileId,
minimumAvailability,
rootFolderPath,
searchOnAdd
rootFolderPath
} = this.state;
const monitoredOptions = [
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
{ key: 'monitored', value: translate('Monitored') },
{ key: 'unmonitored', value: translate('Unmonitored') }
];
const selectedCount = selectedIds.length;
return (
@@ -170,7 +125,7 @@ class CollectionFooter extends Component {
<div className={styles.inputContainer}>
<CollectionFooterLabel
label={translate('MonitorCollection')}
isSaving={isSaving && monitored !== NO_CHANGE}
isSaving={isSaving}
/>
<SelectInput
@@ -185,7 +140,7 @@ class CollectionFooter extends Component {
<div className={styles.inputContainer}>
<CollectionFooterLabel
label={translate('MonitorMovies')}
isSaving={isSaving && monitor !== NO_CHANGE}
isSaving={isSaving}
/>
<SelectInput
@@ -243,25 +198,10 @@ class CollectionFooter extends Component {
/>
</div>
<div className={styles.inputContainer}>
<CollectionFooterLabel
label={translate('SearchMoviesOnAdd')}
isSaving={isSaving && searchOnAdd !== NO_CHANGE}
/>
<SelectInput
name="searchOnAdd"
value={searchOnAdd}
values={searchOnAddOptions}
isDisabled={!selectedCount}
onChange={this.onInputChange}
/>
</div>
<div className={styles.buttonContainer}>
<div className={styles.buttonContainerContent}>
<CollectionFooterLabel
label={translate('CountCollectionsSelected', { count: selectedCount })}
label={translate('CollectionsSelectedInterp', [selectedCount])}
isSaving={false}
/>
@@ -1,4 +1,3 @@
import { maxBy } from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormInputGroup from 'Components/Form/FormInputGroup';
@@ -51,7 +50,7 @@ class FilterBuilderModalContent extends Component {
if (id) {
dispatchSetFilter({ selectedFilterKey: id });
} else {
const last = maxBy(customFilters, 'id');
const last = customFilters[customFilters.length -1];
dispatchSetFilter({ selectedFilterKey: last.id });
}
@@ -109,7 +108,7 @@ class FilterBuilderModalContent extends Component {
this.setState({
labelErrors: [
{
message: translate('LabelIsRequired')
message: 'Label is required'
}
]
});
@@ -147,13 +146,13 @@ class FilterBuilderModalContent extends Component {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('CustomFilter')}
Custom Filter
</ModalHeader>
<ModalBody>
<div className={styles.labelContainer}>
<div className={styles.label}>
{translate('Label')}
Label
</div>
<div className={styles.labelInputContainer}>
@@ -167,7 +166,9 @@ class FilterBuilderModalContent extends Component {
</div>
</div>
<div className={styles.label}>{translate('Filters')}</div>
<div className={styles.label}>
{translate('Filters')}
</div>
<div className={styles.rows}>
{
@@ -37,8 +37,8 @@ class CustomFilter extends Component {
dispatchSetFilter
} = this.props;
// Assume that delete and then unmounting means the deletion was successful.
// Moving this check to an ancestor would be more accurate, but would have
// Assume that delete and then unmounting means the delete was successful.
// Moving this check to a ancestor would be more accurate, but would have
// more boilerplate.
if (this.state.isDeleting && id === selectedFilterKey) {
dispatchSetFilter({ selectedFilterKey: 'all' });
@@ -25,8 +25,7 @@ function createMapStateToProps() {
const values = _.map(filteredItems.sort(sortByName), (downloadClient) => {
return {
key: downloadClient.id,
value: downloadClient.name,
hint: `(${downloadClient.id})`
value: downloadClient.name
};
});
@@ -267,7 +267,6 @@ FormInputGroup.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.any,
values: PropTypes.arrayOf(PropTypes.any),
isDisabled: PropTypes.bool,
type: PropTypes.string.isRequired,
kind: PropTypes.oneOf(kinds.all),
min: PropTypes.number,
@@ -282,7 +281,6 @@ FormInputGroup.propTypes = {
includeNoChange: PropTypes.bool,
includeNoChangeDisabled: PropTypes.bool,
selectedValueOptions: PropTypes.object,
indexerFlags: PropTypes.number,
pending: PropTypes.bool,
errors: PropTypes.arrayOf(PropTypes.object),
warnings: PropTypes.arrayOf(PropTypes.object),
@@ -4,18 +4,22 @@ import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import EnhancedSelectInput from './EnhancedSelectInput';
interface IndexerFlagsSelectInputProps {
name: string;
indexerFlags: number;
onChange(payload: object): void;
}
const selectIndexerFlagsValues = (selectedFlags: number) =>
createSelector(
(state: AppState) => state.settings.indexerFlags,
(indexerFlags) => {
const value = indexerFlags.items.reduce((acc: number[], { id }) => {
// eslint-disable-next-line no-bitwise
if ((selectedFlags & id) === id) {
acc.push(id);
}
return acc;
}, []);
const value = indexerFlags.items
.filter(
// eslint-disable-next-line no-bitwise
(item) => (selectedFlags & item.id) === item.id
)
.map(({ id }) => id);
const values = indexerFlags.items.map(({ id, name }) => ({
key: id,
@@ -29,12 +33,6 @@ const selectIndexerFlagsValues = (selectedFlags: number) =>
}
);
interface IndexerFlagsSelectInputProps {
name: string;
indexerFlags: number;
onChange(payload: object): void;
}
function IndexerFlagsSelectInput(props: IndexerFlagsSelectInputProps) {
const { indexerFlags, onChange } = props;
@@ -91,7 +91,6 @@ class TextTagInputConnector extends Component {
render() {
return (
<TagInput
delimiters={['Tab', 'Enter', ',']}
tagList={[]}
onTagAdd={this.onTagAdd}
onTagDelete={this.onTagDelete}
+8
View File
@@ -12,10 +12,18 @@
.info {
color: var(--infoColor);
&:global(.darken) {
color: color(var(--infoColor) shade(30%));
}
}
.pink {
color: var(--pink);
&:global(.darken) {
color: color(var(--pink) shade(30%));
}
}
.success {
+5 -1
View File
@@ -18,6 +18,7 @@ class Icon extends PureComponent {
kind,
size,
title,
darken,
isSpinning,
...otherProps
} = this.props;
@@ -26,7 +27,8 @@ class Icon extends PureComponent {
<FontAwesomeIcon
className={classNames(
className,
styles[kind]
styles[kind],
darken && 'darken'
)}
icon={name}
spin={isSpinning}
@@ -59,6 +61,7 @@ Icon.propTypes = {
kind: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
darken: PropTypes.bool.isRequired,
isSpinning: PropTypes.bool.isRequired,
fixedWidth: PropTypes.bool.isRequired
};
@@ -66,6 +69,7 @@ Icon.propTypes = {
Icon.defaultProps = {
kind: kinds.DEFAULT,
size: 14,
darken: false,
isSpinning: false,
fixedWidth: false
};
+1 -1
View File
@@ -19,7 +19,7 @@ function ImportListList({ lists, importListList }) {
return (
<Label
key={list.id}
kind={kinds.SUCCESS}
kind={kinds.INFO}
size={sizes.MEDIUM}
>
{list.name}
@@ -101,7 +101,7 @@ const links = [
to: '/settings/downloadclients'
},
{
title: () => translate('ImportLists'),
title: () => translate('Lists'),
to: '/settings/importlists'
},
{
@@ -121,7 +121,7 @@ const links = [
to: '/settings/general'
},
{
title: () => translate('Ui'),
title: () => translate('UI'),
to: '/settings/ui'
}
]
+4 -1
View File
@@ -329,7 +329,10 @@ class DiscoverMovie extends Component {
null
}
<PageToolbarSeparator />
{
(view === 'posters' || view === 'overview') &&
<PageToolbarSeparator />
}
<DiscoverMovieViewMenu
view={view}
@@ -97,8 +97,6 @@ class DiscoverMovieOverview extends Component {
isExisting,
isExcluded,
isRecommendation,
isPopular,
isTrending,
isSelected,
overviewOptions,
...otherProps
@@ -216,26 +214,6 @@ class DiscoverMovieOverview extends Component {
null
}
{
isPopular ?
<Label
kind={kinds.INFO}
>
{translate('Popular')}
</Label> :
null
}
{
isTrending ?
<Label
kind={kinds.INFO}
>
{translate('Trending')}
</Label> :
null
}
<ImportListListConnector
lists={lists}
/>
@@ -305,8 +283,6 @@ DiscoverMovieOverview.propTypes = {
isExisting: PropTypes.bool.isRequired,
isExcluded: PropTypes.bool.isRequired,
isRecommendation: PropTypes.bool.isRequired,
isPopular: PropTypes.bool.isRequired,
isTrending: PropTypes.bool.isRequired,
isSelected: PropTypes.bool,
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
onSelectedChange: PropTypes.func.isRequired
@@ -57,12 +57,10 @@
flex: 0 0 115px;
}
.isTrending,
.isPopular,
.isRecommendation {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 30px;
flex: 0 0 50px;
}
.actions {
@@ -7,9 +7,7 @@ interface CssExports {
'digitalRelease': string;
'genres': string;
'inCinemas': string;
'isPopular': string;
'isRecommendation': string;
'isTrending': string;
'lists': string;
'originalLanguage': string;
'physicalRelease': string;
@@ -7,7 +7,6 @@ import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import DiscoverMovieTableOptionsConnector from './DiscoverMovieTableOptionsConnector';
import styles from './DiscoverMovieHeader.css';
@@ -99,43 +98,6 @@ class DiscoverMovieHeader extends Component {
<Icon
name={icons.RECOMMENDED}
size={12}
title={translate('Recommendation')}
/>
</VirtualTableHeaderCell>
);
}
if (name === 'isTrending') {
return (
<VirtualTableHeaderCell
key={name}
className={styles[name]}
name={name}
isSortable={true}
{...otherProps}
>
<Icon
name={icons.TRENDING}
size={12}
title={translate('Trending')}
/>
</VirtualTableHeaderCell>
);
}
if (name === 'isPopular') {
return (
<VirtualTableHeaderCell
key={name}
className={styles[name]}
name={name}
isSortable={true}
{...otherProps}
>
<Icon
name={icons.POPULAR}
size={12}
title={translate('Popular')}
/>
</VirtualTableHeaderCell>
);
@@ -76,12 +76,10 @@
flex: 1 0 110px;
}
.isTrending,
.isPopular,
.isRecommendation {
composes: cell;
flex: 0 0 30px;
flex: 0 0 50px;
}
.actions {
@@ -97,11 +95,6 @@
margin-top: 0;
}
.statusIcon {
width: 20px !important;
text-align: center;
}
.externalLinks {
margin-right: 0.5em;
}
@@ -12,9 +12,7 @@ interface CssExports {
'externalLinks': string;
'genres': string;
'inCinemas': string;
'isPopular': string;
'isRecommendation': string;
'isTrending': string;
'lists': string;
'originalLanguage': string;
'physicalRelease': string;
@@ -23,7 +21,6 @@ interface CssExports {
'runtime': string;
'sortTitle': string;
'status': string;
'statusIcon': string;
'studio': string;
}
export const cssExports: CssExports;
@@ -82,8 +82,6 @@ class DiscoverMovieRow extends Component {
isExisting,
isExcluded,
isRecommendation,
isTrending,
isPopular,
isSelected,
lists,
onSelectedChange
@@ -307,7 +305,6 @@ class DiscoverMovieRow extends Component {
{
isRecommendation ?
<Icon
className={styles.statusIcon}
name={icons.RECOMMENDED}
size={12}
title={translate('MovieIsRecommend')}
@@ -318,46 +315,6 @@ class DiscoverMovieRow extends Component {
);
}
if (name === 'isTrending') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{
isTrending ?
<Icon
className={styles.statusIcon}
name={icons.TRENDING}
size={12}
title={translate('MovieIsTrending')}
/> :
null
}
</VirtualTableRowCell>
);
}
if (name === 'isPopular') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{
isPopular ?
<Icon
className={styles.statusIcon}
name={icons.POPULAR}
size={12}
title={translate('MovieIsPopular')}
/> :
null
}
</VirtualTableRowCell>
);
}
if (name === 'actions') {
return (
<VirtualTableRowCell
@@ -447,8 +404,6 @@ DiscoverMovieRow.propTypes = {
isExcluded: PropTypes.bool.isRequired,
isSelected: PropTypes.bool,
isRecommendation: PropTypes.bool.isRequired,
isPopular: PropTypes.bool.isRequired,
isTrending: PropTypes.bool.isRequired,
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
onSelectedChange: PropTypes.func.isRequired
};
-2
View File
@@ -23,7 +23,6 @@ import {
import {
faArrowCircleLeft as fasArrowCircleLeft,
faArrowCircleRight as fasArrowCircleRight,
faArrowTrendUp as fasArrowTrendUp,
faAsterisk as fasAsterisk,
faBackward as fasBackward,
faBan as fasBan,
@@ -234,7 +233,6 @@ export const TAGS = fasTags;
export const TBA = fasQuestionCircle;
export const TEST = fasVial;
export const TRANSLATE = fasLanguage;
export const TRENDING = fasArrowTrendUp;
export const UNGROUP = farObjectUngroup;
export const UNKNOWN = fasQuestion;
export const UNMONITORED = farBookmark;
@@ -1,34 +0,0 @@
import React from 'react';
import Modal from 'Components/Modal/Modal';
import SelectIndexerFlagsModalContent from './SelectIndexerFlagsModalContent';
interface SelectIndexerFlagsModalProps {
isOpen: boolean;
indexerFlags: number;
modalTitle: string;
onIndexerFlagsSelect(indexerFlags: number): void;
onModalClose(): void;
}
function SelectIndexerFlagsModal(props: SelectIndexerFlagsModalProps) {
const {
isOpen,
indexerFlags,
modalTitle,
onIndexerFlagsSelect,
onModalClose,
} = props;
return (
<Modal isOpen={isOpen} onModalClose={onModalClose}>
<SelectIndexerFlagsModalContent
indexerFlags={indexerFlags}
modalTitle={modalTitle}
onIndexerFlagsSelect={onIndexerFlagsSelect}
onModalClose={onModalClose}
/>
</Modal>
);
}
export default SelectIndexerFlagsModal;
@@ -1,7 +0,0 @@
.modalBody {
composes: modalBody from '~Components/Modal/ModalBody.css';
display: flex;
flex: 1 1 auto;
flex-direction: column;
}
@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'modalBody': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -1,75 +0,0 @@
import React, { useCallback, useState } 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 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, scrollDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './SelectIndexerFlagsModalContent.css';
interface SelectIndexerFlagsModalContentProps {
indexerFlags: number;
modalTitle: string;
onIndexerFlagsSelect(indexerFlags: number): void;
onModalClose(): void;
}
function SelectIndexerFlagsModalContent(
props: SelectIndexerFlagsModalContentProps
) {
const { modalTitle, onIndexerFlagsSelect, onModalClose } = props;
const [indexerFlags, setIndexerFlags] = useState(props.indexerFlags);
const onIndexerFlagsChange = useCallback(
({ value }: { value: number }) => {
setIndexerFlags(value);
},
[setIndexerFlags]
);
const onIndexerFlagsSelectWrapper = useCallback(() => {
onIndexerFlagsSelect(indexerFlags);
}, [indexerFlags, onIndexerFlagsSelect]);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('SetIndexerFlagsModalTitle', { modalTitle })}
</ModalHeader>
<ModalBody
className={styles.modalBody}
scrollDirection={scrollDirections.NONE}
>
<Form>
<FormGroup>
<FormLabel>{translate('IndexerFlags')}</FormLabel>
<FormInputGroup
type={inputTypes.INDEXER_FLAGS_SELECT}
name="indexerFlags"
indexerFlags={indexerFlags}
autoFocus={true}
onChange={onIndexerFlagsChange}
/>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
<Button kind={kinds.SUCCESS} onPress={onIndexerFlagsSelectWrapper}>
{translate('SetIndexerFlags')}
</Button>
</ModalFooter>
</ModalContent>
);
}
export default SelectIndexerFlagsModalContent;
@@ -26,7 +26,6 @@ import usePrevious from 'Helpers/Hooks/usePrevious';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { align, icons, kinds, scrollDirections } from 'Helpers/Props';
import ImportMode from 'InteractiveImport/ImportMode';
import SelectIndexerFlagsModal from 'InteractiveImport/IndexerFlags/SelectIndexerFlagsModal';
import InteractiveImport, {
InteractiveImportCommandOptions,
} from 'InteractiveImport/InteractiveImport';
@@ -60,13 +59,7 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import InteractiveImportRow from './InteractiveImportRow';
import styles from './InteractiveImportModalContent.css';
type SelectType =
| 'select'
| 'movie'
| 'releaseGroup'
| 'quality'
| 'language'
| 'indexerFlags';
type SelectType = 'select' | 'movie' | 'releaseGroup' | 'quality' | 'language';
type FilterExistingFiles = 'all' | 'new';
@@ -120,15 +113,6 @@ const COLUMNS = [
isSortable: true,
isVisible: true,
},
{
name: 'indexerFlags',
label: React.createElement(Icon, {
name: icons.FLAG,
title: () => translate('IndexerFlags'),
}),
isSortable: true,
isVisible: true,
},
{
name: 'rejections',
label: React.createElement(Icon, {
@@ -258,6 +242,25 @@ function InteractiveImportModalContent(
const [interactiveImportErrorMessage, setInteractiveImportErrorMessage] =
useState<string | null>(null);
const [selectState, setSelectState] = useSelectState();
const [bulkSelectOptions, setBulkSelectOptions] = useState([
{
key: 'select',
value: translate('SelectDropdown'),
disabled: true,
},
{
key: 'quality',
value: translate('SelectQuality'),
},
{
key: 'releaseGroup',
value: translate('SelectReleaseGroup'),
},
{
key: 'language',
value: translate('SelectLanguage'),
},
]);
const { allSelected, allUnselected, selectedState } = selectState;
const previousIsDeleting = usePrevious(isDeleting);
const dispatch = useDispatch();
@@ -273,60 +276,26 @@ function InteractiveImportModalContent(
}
}
const showIndexerFlags = items.some((item) => item.indexerFlags);
if (!showIndexerFlags) {
const indexerFlagsColumn = result.find((c) => c.name === 'indexerFlags');
if (indexerFlagsColumn) {
indexerFlagsColumn.isVisible = false;
}
}
return result;
}, [showMovie, items]);
}, [showMovie]);
const selectedIds: number[] = useMemo(() => {
return getSelectedIds(selectedState);
}, [selectedState]);
const bulkSelectOptions = useMemo(() => {
const options = [
{
key: 'select',
value: translate('SelectDropdown'),
disabled: true,
},
{
key: 'quality',
value: translate('SelectQuality'),
},
{
key: 'releaseGroup',
value: translate('SelectReleaseGroup'),
},
{
key: 'language',
value: translate('SelectLanguage'),
},
{
key: 'indexerFlags',
value: translate('SelectIndexerFlags'),
},
];
if (allowMovieChange) {
options.splice(1, 0, {
key: 'movie',
value: translate('SelectMovie'),
});
}
return options;
}, [allowMovieChange]);
useEffect(
() => {
if (allowMovieChange) {
const newBulkSelectOptions = [...bulkSelectOptions];
newBulkSelectOptions.splice(1, 0, {
key: 'movie',
value: translate('SelectMovie'),
});
setBulkSelectOptions(newBulkSelectOptions);
}
if (initialSortKey) {
const sortProps: { sortKey: string; sortDirection?: string } = {
sortKey: initialSortKey,
@@ -446,14 +415,7 @@ function InteractiveImportModalContent(
const isSelected = selectedIds.indexOf(item.id) > -1;
if (isSelected) {
const {
movie,
releaseGroup,
quality,
languages,
indexerFlags,
movieFileId,
} = item;
const { movie, releaseGroup, quality, languages, movieFileId } = item;
if (!movie) {
setInteractiveImportErrorMessage(
@@ -487,7 +449,6 @@ function InteractiveImportModalContent(
releaseGroup,
quality,
languages,
indexerFlags,
});
return;
@@ -501,7 +462,6 @@ function InteractiveImportModalContent(
releaseGroup,
quality,
languages,
indexerFlags,
downloadId,
movieFileId,
});
@@ -659,22 +619,6 @@ function InteractiveImportModalContent(
[selectedIds, dispatch]
);
const onIndexerFlagsSelect = useCallback(
(indexerFlags: number) => {
dispatch(
updateInteractiveImportItems({
ids: selectedIds,
indexerFlags,
})
);
dispatch(reprocessInteractiveImportItems({ ids: selectedIds }));
setSelectModalOpen(null);
},
[selectedIds, dispatch]
);
const errorMessage = getErrorMessage(
error,
translate('InteractiveImportLoadError')
@@ -849,14 +793,6 @@ function InteractiveImportModalContent(
onModalClose={onSelectModalClose}
/>
<SelectIndexerFlagsModal
isOpen={selectModalOpen === 'indexerFlags'}
indexerFlags={0}
modalTitle={modalTitle}
onIndexerFlagsSelect={onIndexerFlagsSelect}
onModalClose={onSelectModalClose}
/>
<ConfirmModal
isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER}
@@ -8,13 +8,11 @@ import Column from 'Components/Table/Column';
import TableRow from 'Components/Table/TableRow';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import SelectIndexerFlagsModal from 'InteractiveImport/IndexerFlags/SelectIndexerFlagsModal';
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
import Language from 'Language/Language';
import IndexerFlags from 'Movie/IndexerFlags';
import Movie from 'Movie/Movie';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
@@ -32,12 +30,7 @@ import translate from 'Utilities/String/translate';
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
import styles from './InteractiveImportRow.css';
type SelectType =
| 'movie'
| 'releaseGroup'
| 'quality'
| 'language'
| 'indexerFlags';
type SelectType = 'movie' | 'releaseGroup' | 'quality' | 'language';
type SelectedChangeProps = SelectStateInputProps & {
hasMovieFileId: boolean;
@@ -54,7 +47,6 @@ interface InteractiveImportRowProps {
size: number;
customFormats?: object[];
customFormatScore?: number;
indexerFlags: number;
rejections: Rejection[];
columns: Column[];
movieFileId?: number;
@@ -77,7 +69,6 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
size,
customFormats,
customFormatScore,
indexerFlags,
rejections,
isSelected,
modalTitle,
@@ -93,10 +84,6 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
() => columns.find((c) => c.name === 'movie')?.isVisible ?? false,
[columns]
);
const isIndexerFlagsColumnVisible = useMemo(
() => columns.find((c) => c.name === 'indexerFlags')?.isVisible ?? false,
[columns]
);
const [selectModalOpen, setSelectModalOpen] = useState<SelectType | null>(
null
@@ -236,34 +223,12 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
[id, dispatch, setSelectModalOpen, selectRowAfterChange]
);
const onSelectIndexerFlagsPress = useCallback(() => {
setSelectModalOpen('indexerFlags');
}, [setSelectModalOpen]);
const onIndexerFlagsSelect = useCallback(
(indexerFlags: number) => {
dispatch(
updateInteractiveImportItem({
id,
indexerFlags,
})
);
dispatch(reprocessInteractiveImportItems({ ids: [id] }));
setSelectModalOpen(null);
selectRowAfterChange();
},
[id, dispatch, setSelectModalOpen, selectRowAfterChange]
);
const movieTitle = movie ? movie.title : '';
const showMoviePlaceholder = isSelected && !movie;
const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
const showQualityPlaceholder = isSelected && !quality;
const showLanguagePlaceholder = isSelected && !languages;
const showIndexerFlagsPlaceholder = isSelected && !indexerFlags;
return (
<TableRow>
@@ -346,28 +311,6 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
) : null}
</TableRowCell>
{isIndexerFlagsColumnVisible ? (
<TableRowCellButton
title={translate('ClickToChangeIndexerFlags')}
onPress={onSelectIndexerFlagsPress}
>
{showIndexerFlagsPlaceholder ? (
<InteractiveImportRowCellPlaceholder isOptional={true} />
) : (
<>
{indexerFlags ? (
<Popover
anchor={<Icon name={icons.FLAG} kind={kinds.PRIMARY} />}
title={translate('IndexerFlags')}
body={<IndexerFlags indexerFlags={indexerFlags} />}
position={tooltipPositions.LEFT}
/>
) : null}
</>
)}
</TableRowCellButton>
) : null}
<TableRowCell>
{rejections.length ? (
<Popover
@@ -418,14 +361,6 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
onLanguagesSelect={onLanguagesSelect}
onModalClose={onSelectModalClose}
/>
<SelectIndexerFlagsModal
isOpen={selectModalOpen === 'indexerFlags'}
indexerFlags={indexerFlags ?? 0}
modalTitle={modalTitle}
onIndexerFlagsSelect={onIndexerFlagsSelect}
onModalClose={onSelectModalClose}
/>
</TableRow>
);
}
@@ -11,7 +11,6 @@ export interface InteractiveImportCommandOptions {
releaseGroup?: string;
quality: QualityModel;
languages: Language[];
indexerFlags: number;
downloadId?: string;
movieFileId?: number;
}
@@ -28,7 +27,6 @@ interface InteractiveImport extends ModelBase {
movie?: Movie;
qualityWeight: number;
customFormats: object[];
indexerFlags: number;
rejections: Rejection[];
movieFileId?: number;
}
@@ -89,18 +89,18 @@ const columns = [
isVisible: true
},
{
name: 'rejections',
label: React.createElement(Icon, {
name: icons.DANGER,
title: () => translate('Rejections')
}),
name: 'releaseWeight',
label: React.createElement(Icon, { name: icons.DOWNLOAD }),
isSortable: true,
fixedSortDirection: sortDirections.ASCENDING,
isVisible: true
},
{
name: 'releaseWeight',
label: React.createElement(Icon, { name: icons.DOWNLOAD }),
name: 'rejections',
label: React.createElement(Icon, {
name: icons.DANGER,
title: () => translate('Rejections')
}),
isSortable: true,
fixedSortDirection: sortDirections.ASCENDING,
isVisible: true
@@ -39,7 +39,8 @@
}
.rejected,
.indexerFlags {
.indexerFlags,
.download {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 50px;
@@ -90,8 +90,8 @@ interface InteractiveSearchRowProps {
customFormats: CustomFormat[];
customFormatScore: number;
mappedMovieId?: number;
indexerFlags: string[];
rejections: string[];
indexerFlags: string[];
downloadAllowed: boolean;
isGrabbing: boolean;
isGrabbed: boolean;
@@ -125,8 +125,8 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
customFormatScore,
customFormats,
mappedMovieId,
indexerFlags = [],
rejections = [],
indexerFlags = [],
downloadAllowed,
isGrabbing = false,
isGrabbed = false,
@@ -276,7 +276,7 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
customFormats.length
)}
tooltip={<MovieFormats formats={customFormats} />}
position={tooltipPositions.LEFT}
position={tooltipPositions.TOP}
/>
</TableRowCell>
@@ -98,7 +98,7 @@ class DeleteMovieModalContent extends Component {
type={inputTypes.CHECK}
name="addImportExclusion"
value={addImportExclusion}
helpText={translate('AddListExclusionMovieHelpText')}
helpText={translate('AddImportExclusionHelpText')}
kind={kinds.DANGER}
onChange={onDeleteOptionChange}
/>
@@ -1,15 +1,8 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import Label from 'Components/Label';
import Link from 'Components/Link/Link';
import MonitorToggleButton from 'Components/MonitorToggleButton';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, sizes } from 'Helpers/Props';
import MovieHeadshot from 'Movie/MovieHeadshot';
import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
import translate from 'Utilities/String/translate';
import styles from '../MovieCreditPoster.css';
class MovieCastPoster extends Component {
@@ -59,7 +52,6 @@ class MovieCastPoster extends Component {
render() {
const {
tmdbId,
personName,
character,
images,
@@ -91,35 +83,15 @@ class MovieCastPoster extends Component {
style={contentStyle}
>
<div className={styles.posterContainer}>
<div className={styles.toggleMonitoredContainer}>
<div className={styles.controls}>
<MonitorToggleButton
className={styles.monitorToggleButton}
className={styles.action}
monitored={monitored}
size={20}
onPress={importListId > 0 ? this.onEditImportListPress : this.onAddImportListPress}
/>
</div>
<Label className={styles.controls}>
<span className={styles.externalLinks}>
<Popover
anchor={<Icon name={icons.EXTERNAL_LINK} size={12} />}
title={translate('Links')}
body={
<Link to={`https://www.themoviedb.org/person/${tmdbId}`}>
<Label
className={styles.externalLinkLabel}
kind={kinds.INFO}
size={sizes.LARGE}
>
{translate('TMDb')}
</Label>
</Link>
}
/>
</span>
</Label>
<div
style={elementStyle}
>
@@ -143,10 +115,10 @@ class MovieCastPoster extends Component {
</div>
</div>
<div className={classNames(styles.title, 'swiper-no-swiping')}>
<div className={styles.title}>
{personName}
</div>
<div className={classNames(styles.title, 'swiper-no-swiping')}>
<div className={styles.title}>
{character}
</div>
@@ -1,15 +1,8 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import Label from 'Components/Label';
import Link from 'Components/Link/Link';
import MonitorToggleButton from 'Components/MonitorToggleButton';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, sizes } from 'Helpers/Props';
import MovieHeadshot from 'Movie/MovieHeadshot';
import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
import translate from 'Utilities/String/translate';
import styles from '../MovieCreditPoster.css';
class MovieCrewPoster extends Component {
@@ -59,7 +52,6 @@ class MovieCrewPoster extends Component {
render() {
const {
tmdbId,
personName,
job,
images,
@@ -91,35 +83,15 @@ class MovieCrewPoster extends Component {
style={contentStyle}
>
<div className={styles.posterContainer}>
<div className={styles.toggleMonitoredContainer}>
<div className={styles.controls}>
<MonitorToggleButton
className={styles.monitorToggleButton}
className={styles.action}
monitored={monitored}
size={20}
onPress={importListId > 0 ? this.onEditImportListPress : this.onAddImportListPress}
/>
</div>
<Label className={styles.controls}>
<span className={styles.externalLinks}>
<Popover
anchor={<Icon name={icons.EXTERNAL_LINK} size={12} />}
title={translate('Links')}
body={
<Link to={`https://www.themoviedb.org/person/${tmdbId}`}>
<Label
className={styles.externalLinkLabel}
kind={kinds.INFO}
size={sizes.LARGE}
>
{translate('TMDb')}
</Label>
</Link>
}
/>
</span>
</Label>
<div
style={elementStyle}
>
@@ -143,10 +115,10 @@ class MovieCrewPoster extends Component {
</div>
</div>
<div className={classNames(styles.title, 'swiper-no-swiping')}>
<div className={styles.title}>
{personName}
</div>
<div className={classNames(styles.title, 'swiper-no-swiping')}>
<div className={styles.title}>
{job}
</div>
@@ -1,17 +1,13 @@
$hoverScale: 1.05;
.content {
border-radius: '5px';
transition: all 200ms ease-in;
&:hover {
z-index: 2;
box-shadow: 0 0 12px var(--black);
transition: all 200ms ease-in;
.controls {
opacity: 0.9;
transition: opacity 200ms linear 150ms;
}
}
}
@@ -48,13 +44,13 @@ $hoverScale: 1.05;
font-size: $smallFontSize;
}
.toggleMonitoredContainer {
.controls {
position: absolute;
top: 10px;
z-index: 3;
}
.monitorToggleButton {
.action {
composes: toggleButton from '~Components/MonitorToggleButton.css';
width: 25px;
@@ -65,39 +61,8 @@ $hoverScale: 1.05;
}
}
.controls {
position: absolute;
bottom: 10px;
left: 10px;
z-index: 3;
border-radius: 4px;
background-color: #707070;
color: var(--white);
font-size: $smallFontSize;
opacity: 0;
transition: opacity 0;
}
.action {
composes: button from '~Components/Link/IconButton.css';
&:hover {
color: var(--iconButtonHoverLightColor);
}
}
@media only screen and (max-width: $breakpointSmall) {
.container {
padding: 5px;
}
}
.externalLinks {
margin: 0 2px;
}
.externalLinkLabel {
composes: label from '~Components/Label.css';
cursor: pointer;
}
@@ -5,14 +5,10 @@ interface CssExports {
'container': string;
'content': string;
'controls': string;
'externalLinkLabel': string;
'externalLinks': string;
'monitorToggleButton': string;
'overlayTitle': string;
'poster': string;
'posterContainer': string;
'title': string;
'toggleMonitoredContainer': string;
}
export const cssExports: CssExports;
export default cssExports;
@@ -3,7 +3,6 @@
}
.link {
display: inline-block;
white-space: nowrap;
}
+1 -1
View File
@@ -235,7 +235,7 @@ const MovieIndex = withScrollPosition((props: MovieIndexProps) => {
/>
<PageToolbarButton
label={translate('RssSync')}
label={translate('RSSSync')}
iconName={icons.RSS}
isSpinning={isRssSyncExecuting}
isDisabled={hasNoMovie}
@@ -244,15 +244,11 @@ export default function MovieIndexPosters(props: MovieIndexPostersProps) {
if (isSmallScreen) {
const padding = bodyPaddingSmallScreen - 5;
const width = window.innerWidth - padding * 2;
const height = window.innerHeight;
if (width !== size.width || height !== size.height) {
setSize({
width,
height,
});
}
setSize({
width: window.innerWidth - padding * 2,
height: window.innerHeight,
});
return;
}
@@ -98,7 +98,7 @@ function DeleteMovieModalContent(props: DeleteMovieModalContentProps) {
type={inputTypes.CHECK}
name="addImportExclusion"
value={addImportExclusion}
helpText={translate('AddListExclusionMovieHelpText')}
helpText={translate('AddImportExclusionHelpText')}
onChange={onDeleteOptionChange}
/>
</FormGroup>
-26
View File
@@ -1,26 +0,0 @@
import React from 'react';
import { useSelector } from 'react-redux';
import createIndexerFlagsSelector from 'Store/Selectors/createIndexerFlagsSelector';
interface IndexerFlagsProps {
indexerFlags: number;
}
function IndexerFlags({ indexerFlags = 0 }: IndexerFlagsProps) {
const allIndexerFlags = useSelector(createIndexerFlagsSelector);
const flags = allIndexerFlags.items.filter(
// eslint-disable-next-line no-bitwise
(item) => (indexerFlags & item.id) === item.id
);
return flags.length ? (
<ul>
{flags.map((flag, index) => {
return <li key={index}>{flag.name}</li>;
})}
</ul>
) : null;
}
export default IndexerFlags;
@@ -45,8 +45,7 @@
width: 165px;
}
.releaseGroup,
.dateAdded {
.releaseGroup {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 120px;
@@ -57,9 +56,3 @@
width: 55px;
}
.indexerFlags {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 50px;
}
@@ -6,10 +6,8 @@ interface CssExports {
'audio': string;
'audioLanguages': string;
'customFormatScore': string;
'dateAdded': string;
'download': string;
'formats': string;
'indexerFlags': string;
'language': string;
'languages': string;
'quality': string;
@@ -1,15 +1,11 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
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 { icons, kinds, tooltipPositions } from 'Helpers/Props';
import IndexerFlags from 'Movie/IndexerFlags';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
@@ -85,9 +81,7 @@ class MovieFileEditorRow extends Component {
qualityCutoffNotMet,
customFormats,
customFormatScore,
indexerFlags,
languages,
dateAdded,
columns
} = this.props;
@@ -147,30 +141,12 @@ class MovieFileEditorRow extends Component {
customFormats.length
)}
tooltip={<MovieFormats formats={customFormats} />}
position={tooltipPositions.LEFT}
position={tooltipPositions.TOP}
/>
</TableRowCell>
);
}
if (name === 'indexerFlags') {
return (
<TableRowCell
key={name}
className={styles.indexerFlags}
>
{indexerFlags ? (
<Popover
anchor={<Icon name={icons.FLAG} kind={kinds.PRIMARY} />}
title={translate('IndexerFlags')}
body={<IndexerFlags indexerFlags={indexerFlags} />}
position={tooltipPositions.LEFT}
/>
) : null}
</TableRowCell>
);
}
if (name === 'languages') {
return (
<TableRowCell
@@ -311,16 +287,6 @@ class MovieFileEditorRow extends Component {
);
}
if (name === 'dateAdded') {
return (
<RelativeDateCellConnector
key={name}
className={styles.dateAdded}
date={dateAdded}
/>
);
}
if (name === 'actions') {
return (
<TableRowCell key={name} className={styles.actions}>
@@ -385,18 +351,15 @@ MovieFileEditorRow.propTypes = {
releaseGroup: PropTypes.string,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
customFormatScore: PropTypes.number.isRequired,
indexerFlags: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
mediaInfo: PropTypes.object,
dateAdded: PropTypes.string,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onDeletePress: PropTypes.func.isRequired
};
MovieFileEditorRow.defaultProps = {
customFormats: [],
indexerFlags: 0
customFormats: []
};
export default MovieFileEditorRow;
@@ -2,8 +2,6 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { sortDirections } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import MovieFileEditorRow from './MovieFileEditorRow';
import styles from './MovieFileEditorTableContent.css';
@@ -16,9 +14,6 @@ class MovieFileEditorTableContent extends Component {
const {
items,
columns,
sortKey,
sortDirection,
onSortPress,
onTableOptionChange
} = this.props;
@@ -27,7 +22,7 @@ class MovieFileEditorTableContent extends Component {
{
!items.length &&
<div className={styles.blankpad}>
{translate('NoMovieFilesToManage')}
No movie files to manage.
</div>
}
@@ -35,9 +30,6 @@ class MovieFileEditorTableContent extends Component {
!!items.length &&
<Table
columns={columns}
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
onTableOptionChange={onTableOptionChange}
>
<TableBody>
@@ -67,10 +59,7 @@ MovieFileEditorTableContent.propTypes = {
isDeleting: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string.isRequired,
sortDirection: PropTypes.oneOf(sortDirections.all),
onTableOptionChange: PropTypes.func.isRequired,
onSortPress: PropTypes.func.isRequired,
onDeletePress: PropTypes.func.isRequired
};
@@ -2,9 +2,8 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { deleteMovieFile, setMovieFilesSort, setMovieFilesTableOption } from 'Store/Actions/movieFileActions';
import { deleteMovieFile, setMovieFilesTableOption, updateMovieFiles } from 'Store/Actions/movieFileActions';
import { fetchLanguages, fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import getQualities from 'Utilities/Quality/getQualities';
import MovieFileEditorTableContent from './MovieFileEditorTableContent';
@@ -12,7 +11,7 @@ import MovieFileEditorTableContent from './MovieFileEditorTableContent';
function createMapStateToProps() {
return createSelector(
(state, { movieId }) => movieId,
createClientSideCollectionSelector('movieFiles'),
(state) => state.movieFiles,
(state) => state.settings.languages,
(state) => state.settings.qualityProfiles,
createMovieSelector(),
@@ -24,13 +23,13 @@ function createMapStateToProps() {
) => {
const languages = languageProfiles.items;
const qualities = getQualities(qualityProfiles.schema.items);
const filesForMovie = movieFiles.items.filter((file) => file.movieId === movieId);
const filesForMovie = movieFiles.items.filter((obj) => {
return obj.movieId === movieId;
});
return {
items: filesForMovie,
columns: movieFiles.columns,
sortKey: movieFiles.sortKey,
sortDirection: movieFiles.sortDirection,
isDeleting: movieFiles.isDeleting,
isSaving: movieFiles.isSaving,
error: null,
@@ -41,13 +40,31 @@ function createMapStateToProps() {
);
}
const mapDispatchToProps = {
fetchQualityProfileSchema,
fetchLanguages,
deleteMovieFile,
setMovieFilesTableOption,
setMovieFilesSort
};
function createMapDispatchToProps(dispatch, props) {
return {
dispatchFetchQualityProfileSchema(name, path) {
dispatch(fetchQualityProfileSchema());
},
dispatchFetchLanguages(name, path) {
dispatch(fetchLanguages());
},
dispatchUpdateMovieFiles(updateProps) {
dispatch(updateMovieFiles(updateProps));
},
onTableOptionChange(payload) {
dispatch(setMovieFilesTableOption(payload));
},
onDeletePress(movieFileId) {
dispatch(deleteMovieFile({
id: movieFileId
}));
}
};
}
class MovieFileEditorTableContentConnector extends Component {
@@ -55,40 +72,24 @@ class MovieFileEditorTableContentConnector extends Component {
// Lifecycle
componentDidMount() {
this.props.fetchLanguages();
this.props.fetchQualityProfileSchema();
this.props.dispatchFetchLanguages();
this.props.dispatchFetchQualityProfileSchema();
}
//
// Listeners
onDeletePress = (movieFileId) => {
this.props.deleteMovieFile({
id: movieFileId
});
};
onTableOptionChange = (payload) => {
this.props.setMovieFilesTableOption(payload);
};
onSortPress = (sortKey, sortDirection) => {
this.props.setMovieFilesSort({
sortKey,
sortDirection
});
};
//
// Render
render() {
const {
dispatchFetchLanguages,
dispatchFetchQualityProfileSchema,
dispatchUpdateMovieFiles,
...otherProps
} = this.props;
return (
<MovieFileEditorTableContent
{...this.props}
onDeletePress={this.onDeletePress}
onTableOptionChange={this.onTableOptionChange}
onSortPress={this.onSortPress}
{...otherProps}
/>
);
}
@@ -98,11 +99,9 @@ MovieFileEditorTableContentConnector.propTypes = {
movieId: PropTypes.number.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
qualities: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchLanguages: PropTypes.func.isRequired,
fetchQualityProfileSchema: PropTypes.func.isRequired,
deleteMovieFile: PropTypes.func.isRequired,
setMovieFilesTableOption: PropTypes.func.isRequired,
setMovieFilesSort: PropTypes.func.isRequired
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
dispatchUpdateMovieFiles: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(MovieFileEditorTableContentConnector);
export default connect(createMapStateToProps, createMapDispatchToProps)(MovieFileEditorTableContentConnector);
@@ -46,7 +46,7 @@ class ExtraFileTableContent extends Component {
{
!items.length &&
<div className={styles.blankpad}>
{translate('NoExtraFilesToManage')}
No extra files to manage.
</div>
}
@@ -14,7 +14,9 @@ function createMapStateToProps() {
movieId,
extraFiles
) => {
const filesForMovie = extraFiles.items.filter((file) => file.movieId === movieId);
const filesForMovie = extraFiles.items.filter((obj) => {
return obj.movieId === movieId;
});
return {
items: filesForMovie,
@@ -24,6 +26,11 @@ function createMapStateToProps() {
);
}
function createMapDispatchToProps(dispatch, props) {
return {
};
}
class ExtraFileTableContentConnector extends Component {
//
@@ -46,4 +53,4 @@ ExtraFileTableContentConnector.propTypes = {
movieId: PropTypes.number.isRequired
};
export default connect(createMapStateToProps, null)(ExtraFileTableContentConnector);
export default connect(createMapStateToProps, createMapDispatchToProps)(ExtraFileTableContentConnector);
-1
View File
@@ -15,7 +15,6 @@ export interface MovieFile extends ModelBase {
languages: Language[];
quality: QualityModel;
customFormats: CustomFormat[];
indexerFlags: number;
mediaInfo: MediaInfo;
qualityCutoffNotMet: boolean;
}
@@ -104,7 +104,7 @@ class OrganizePreviewModalContent extends Component {
{
!isFetching && error &&
<Alert kind={kinds.DANGER}>{translate('OrganizeLoadError')}</Alert>
<div>{translate('OrganizeLoadError')}</div>
}
{
@@ -6,12 +6,11 @@ import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import ParseToolbarButton from 'Parse/ParseToolbarButton';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import CustomFormatsConnector from './CustomFormats/CustomFormatsConnector';
function CustomFormatSettingsPage() {
return (
<PageContent title={translate('CustomFormatsSettings')}>
<PageContent title="Custom Format Settings">
<SettingsToolbarConnector
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -152,7 +152,7 @@ class CustomFormat extends Component {
isOpen={this.state.isDeleteCustomFormatModalOpen}
kind={kinds.DANGER}
title={translate('DeleteCustomFormat')}
message={translate('DeleteCustomFormatMessageText', { name })}
message={translate('DeleteCustomFormatMessageText', { customFormatName: name })}
confirmLabel={translate('Delete')}
isSpinning={isDeleting}
onConfirm={this.onConfirmDeleteCustomFormat}
@@ -61,8 +61,8 @@ class CustomFormats extends Component {
return (
<FieldSet legend={translate('CustomFormats')}>
<PageSectionContent
errorMessage={translate('CustomFormatsLoadError')}
{...otherProps}
errorMessage={translate('UnableToLoadCustomFormats')}
{...otherProps}c={true}
>
<div className={styles.customFormats}>
{
@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import Card from 'Components/Card';
import FieldSet from 'Components/FieldSet';
import Form from 'Components/Form/Form';
@@ -113,9 +112,9 @@ class EditCustomFormatModalContent extends Component {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('AddCustomFormatError')}
</Alert>
<div>
{translate('UnableToAddANewCustomFormatPleaseTryAgain')}
</div>
}
{
@@ -43,7 +43,7 @@ class ExportCustomFormatModalContent extends Component {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('CustomFormatsLoadError')}
{translate('UnableToLoadCustomFormats')}
</Alert>
}
@@ -97,7 +97,7 @@ class ImportCustomFormatModalContent extends Component {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('CustomFormatsLoadError')}
{translate('UnableToLoadCustomFormats')}
</Alert>
}
@@ -89,9 +89,7 @@ class ImportCustomFormatModalContentConnector extends Component {
const selectedImplementation = _.find(this.props.specificationSchema, { implementation: spec.implementation });
if (!selectedImplementation) {
throw new Error(translate('CustomFormatUnknownCondition', {
implementation: spec.implementation
}));
throw new Error(translate('CustomFormatUnknownCondition', [spec.implementation]));
}
this.props.selectCustomFormatSpecificationSchema({ implementation: spec.implementation });
@@ -111,10 +109,7 @@ class ImportCustomFormatModalContentConnector extends Component {
for (const [key, value] of Object.entries(fields)) {
const field = _.find(schema.fields, { name: key });
if (!field) {
throw new Error(translate('CustomFormatUnknownConditionOption', {
key,
implementation: schema.implementationName
}));
throw new Error(translate('CustomFormatUnknownConditionOption', [key, schema.implementationName]));
}
this.props.setCustomFormatSpecificationFieldValue({ name: key, value });
@@ -42,9 +42,9 @@ class AddSpecificationModalContent extends Component {
{
!isSchemaFetching && !!schemaError &&
<Alert kind={kinds.DANGER}>
{translate('AddConditionError')}
</Alert>
<div>
{translate('UnableToAddANewConditionPleaseTryAgain')}
</div>
}
{
@@ -53,10 +53,10 @@ class AddSpecificationModalContent extends Component {
<Alert kind={kinds.INFO}>
<div>
{translate('SupportedCustomConditions')}
{translate('RadarrSupportsCustomConditionsAgainstTheReleasePropertiesBelow')}
</div>
<div>
{translate('VisitTheWikiForMoreDetails')}
{translate('VisitGithubCustomFormatsAphrodite')}
<Link to="https://wiki.servarr.com/radarr/settings#custom-formats-2">{translate('Wiki')}</Link>
</div>
</Alert>
@@ -7,8 +7,8 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
import Button from 'Components/Link/Button';
import Link from 'Components/Link/Link';
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
@@ -49,16 +49,15 @@ function EditSpecificationModalContent(props) {
{...otherProps}
>
{
fields && fields.some((x) => x.label === translate('CustomFormatsSpecificationRegularExpression')) &&
fields && fields.some((x) => x.label === 'Regular Expression') &&
<Alert kind={kinds.INFO}>
<div>
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
<div dangerouslySetInnerHTML={{ __html: translate('ThisConditionMatchesUsingRegularExpressions', ['<code>\\^$.|?*+()[{</code>', '<code>\\</code>']) }} />
{translate('MoreDetails')} <Link to="https://www.regular-expressions.info/tutorial.html">{translate('LinkHere')}</Link>
</div>
<div>
<InlineMarkdown data={translate('RegularExpressionsTutorialLink')} />
</div>
<div>
<InlineMarkdown data={translate('RegularExpressionsCanBeTested')} />
{translate('RegularExpressionsCanBeTested')}
<Link to="http://regexstorm.net/tester">{translate('LinkHere')}</Link>
</div>
</Alert>
}
@@ -100,7 +99,7 @@ function EditSpecificationModalContent(props) {
type={inputTypes.CHECK}
name="negate"
{...negate}
helpText={translate('NegateHelpText', { implementationName })}
helpText={translate('NegateHelpText', [implementationName])}
onChange={onInputChange}
/>
</FormGroup>
@@ -114,7 +113,7 @@ function EditSpecificationModalContent(props) {
type={inputTypes.CHECK}
name="required"
{...required}
helpText={translate('RequiredHelpText', { implementationName })}
helpText={translate('RequiredHelpText', [implementationName, implementationName])}
onChange={onInputChange}
/>
</FormGroup>
@@ -43,9 +43,9 @@ class AddDownloadClientModalContent extends Component {
{
!isSchemaFetching && !!schemaError &&
<Alert kind={kinds.DANGER}>
{translate('AddDownloadClientError')}
</Alert>
<div>
{translate('UnableToAddANewDownloadClientPleaseTryAgain')}
</div>
}
{
@@ -54,10 +54,10 @@ class AddDownloadClientModalContent extends Component {
<Alert kind={kinds.INFO}>
<div>
{translate('SupportedDownloadClients')}
{translate('RadarrSupportsAnyDownloadClient')}
</div>
<div>
{translate('SupportedDownloadClientsMoreInfo')}
{translate('ForMoreInformationOnTheIndividualDownloadClients')}
</div>
</Alert>
@@ -41,7 +41,7 @@ class DownloadClient extends Component {
});
};
onDeleteDownloadClientModalClose = () => {
onDeleteDownloadClientModalClose= () => {
this.setState({ isDeleteDownloadClientModalOpen: false });
};
@@ -69,9 +69,9 @@ class EditDownloadClientModalContent extends Component {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('AddDownloadClientError')}
</Alert>
<div>
{translate('UnableToAddANewDownloadClientPleaseTryAgain')}
</div>
}
{
@@ -147,7 +147,7 @@ class EditDownloadClientModalContent extends Component {
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText={translate('DownloadClientMovieTagHelpText')}
helpText={translate('DownloadClientTagHelpText')}
{...tags}
onChange={onInputChange}
/>
@@ -184,6 +184,7 @@ class EditDownloadClientModalContent extends Component {
</FormGroup>
}
</FieldSet>
</Form>
}
</ModalBody>
@@ -30,7 +30,7 @@ function DownloadClientOptions(props) {
{
!isFetching && error &&
<Alert kind={kinds.DANGER}>
{translate('DownloadClientOptionsLoadError')}
{translate('UnableToLoadDownloadClientOptions')}
</Alert>
}
@@ -38,7 +38,6 @@ function DownloadClientOptions(props) {
hasSettings && !isFetching && !error && advancedSettings &&
<div>
<FieldSet legend={translate('CompletedDownloadHandling')}>
<Form>
<FormGroup
advancedSettings={advancedSettings}
@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
@@ -53,9 +52,9 @@ function EditRemotePathMappingModalContent(props) {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('AddRemotePathMappingError')}
</Alert>
<div>
{translate('UnableToAddANewRemotePathMappingPleaseTryAgain')}
</div>
}
{
@@ -67,7 +66,7 @@ function EditRemotePathMappingModalContent(props) {
<FormInputGroup
type={inputTypes.SELECT}
name="host"
helpText={translate('RemotePathMappingHostHelpText')}
helpText={translate('SettingsRemotePathMappingHostHelpText')}
{...host}
values={downloadClientHosts}
onChange={onInputChange}
@@ -75,24 +74,24 @@ function EditRemotePathMappingModalContent(props) {
</FormGroup>
<FormGroup>
<FormLabel>{translate('RemotePath')}</FormLabel>
<FormLabel>{translate('SettingsRemotePathMappingRemotePath')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="remotePath"
helpText={translate('RemotePathMappingRemotePathHelpText')}
helpText={translate('SettingsRemotePathMappingRemotePathHelpText')}
{...remotePath}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('LocalPath')}</FormLabel>
<FormLabel>{translate('SettingsRemotePathMappingLocalPath')}</FormLabel>
<FormInputGroup
type={inputTypes.PATH}
name="localPath"
helpText={translate('RemotePathMappingLocalPathHelpText')}
helpText={translate('SettingsRemotePathMappingLocalPathHelpText')}
{...localPath}
onChange={onInputChange}
/>
@@ -1,3 +1,4 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -54,7 +55,7 @@ function createRemotePathMappingSelector() {
items
} = remotePathMappings;
const mapping = id ? items.find((i) => i.id === id) : newRemotePathMapping;
const mapping = id ? _.find(items, { id }) : newRemotePathMapping;
const settings = selectSettings(mapping, pendingChanges, saveError);
return {
@@ -49,12 +49,12 @@ class RemotePathMappings extends Component {
return (
<FieldSet legend={translate('RemotePathMappings')}>
<PageSectionContent
errorMessage={translate('RemotePathMappingsLoadError')}
errorMessage={translate('UnableToLoadRemotePathMappings')}
{...otherProps}
>
<Alert kind={kinds.INFO}>
<InlineMarkdown data={translate('RemotePathMappingsInfo', { wikiLink: 'https://wiki.servarr.com/radarr/settings#remote-path-mappings' })} />
<InlineMarkdown data={translate('RemotePathMappingsInfo', { app: 'Radarr', wikiLink: 'https://wiki.servarr.com/radarr/settings#remote-path-mappings' })} />
</Alert>
<div className={styles.remotePathMappingsHeader}>
@@ -22,7 +22,6 @@ const requiresRestartKeys = [
'bindAddress',
'port',
'urlBase',
'instanceName',
'enableSsl',
'sslPort',
'sslCertPath',
@@ -126,7 +125,7 @@ class GeneralSettings extends Component {
{
!isFetching && error &&
<Alert kind={kinds.DANGER}>
{translate('GeneralSettingsLoadError')}
{translate('UnableToLoadGeneralSettings')}
</Alert>
}
@@ -187,8 +186,10 @@ class GeneralSettings extends Component {
isOpen={this.state.isRestartRequiredModalOpen}
kind={kinds.DANGER}
title={translate('RestartRadarr')}
message={`${translate('RestartRequiredToApplyChanges')} ${isWindowsService ? translate('RestartRequiredWindowsService') : ''}`}
cancelLabel={translate('RestartLater')}
message={
`Radarr requires a restart to apply changes, do you want to restart now? ${isWindowsService ? 'Depending which user is running the Radarr service you may need to restart Radarr as admin once before the service will start automatically.' : ''}`
}
cancelLabel={translate('IllRestartLater')}
confirmLabel={translate('RestartNow')}
onConfirm={this.onConfirmRestart}
onCancel={this.onCloseRestartRequiredModalOpen}
+10 -11
View File
@@ -63,7 +63,7 @@ function HostSettings(props) {
</FormGroup>
<FormGroup>
<FormLabel>{translate('UrlBase')}</FormLabel>
<FormLabel>{translate('URLBase')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
@@ -111,7 +111,7 @@ function HostSettings(props) {
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>{translate('EnableSsl')}</FormLabel>
<FormLabel>{translate('EnableSSL')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
@@ -128,7 +128,7 @@ function HostSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('SslPort')}</FormLabel>
<FormLabel>{translate('SSLPort')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
@@ -149,12 +149,12 @@ function HostSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('SslCertPath')}</FormLabel>
<FormLabel>{translate('SSLCertPath')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="sslCertPath"
helpText={translate('SslCertPathHelpText')}
helpText={translate('SSLCertPathHelpText')}
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
onChange={onInputChange}
{...sslCertPath}
@@ -169,12 +169,12 @@ function HostSettings(props) {
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('SslCertPassword')}</FormLabel>
<FormLabel>{translate('SSLCertPassword')}</FormLabel>
<FormInputGroup
type={inputTypes.PASSWORD}
name="sslCertPassword"
helpText={translate('SslCertPasswordHelpText')}
helpText={translate('SSLCertPasswordHelpText')}
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
onChange={onInputChange}
{...sslCertPassword}
@@ -184,19 +184,18 @@ function HostSettings(props) {
}
{
isWindows && mode !== 'service' ?
isWindows && mode !== 'service' &&
<FormGroup size={sizes.MEDIUM}>
<FormLabel>{translate('OpenBrowserOnStart')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="launchBrowser"
helpText={translate('OpenBrowserOnStartHelpText')}
helpText={translate('LaunchBrowserHelpText')}
onChange={onInputChange}
{...launchBrowser}
/>
</FormGroup> :
null
</FormGroup>
}
</FieldSet>
+3 -12
View File
@@ -25,18 +25,9 @@ function ProxySettings(props) {
} = settings;
const proxyTypeOptions = [
{
key: 'http',
value: translate('HttpHttps')
},
{
key: 'socks4',
value: translate('Socks4')
},
{
key: 'socks5',
value: translate('Socks5')
}
{ key: 'http', value: translate('HttpHttps') },
{ key: 'socks4', value: translate('Socks4') },
{ key: 'socks5', value: translate('Socks5') }
];
return (
@@ -70,8 +70,7 @@ function UpdateSettings(props) {
</FormGroup>
{
isWindows ?
null :
!isWindows &&
<div>
<FormGroup
advancedSettings={advancedSettings}
@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'Components/Alert';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
@@ -40,7 +39,7 @@ function EditImportListExclusionModalContent(props) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{id ? translate('EditImportListExclusion') : translate('AddImportListExclusion')}
{id ? translate('EditListExclusion') : translate('AddListExclusion')}
</ModalHeader>
<ModalBody className={styles.body}>
@@ -51,9 +50,9 @@ function EditImportListExclusionModalContent(props) {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('AddImportListExclusionError')}
</Alert>
<div>
{translate('UnableToAddANewListExclusionPleaseTryAgain')}
</div>
}
{
@@ -1,3 +1,4 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -6,7 +7,7 @@ import { saveImportExclusion, setImportExclusionValue } from 'Store/Actions/sett
import selectSettings from 'Store/Selectors/selectSettings';
import EditImportListExclusionModalContent from './EditImportListExclusionModalContent';
const newImportListExclusion = {
const newImportExclusion = {
movieTitle: '',
tmdbId: 0,
movieYear: 0
@@ -26,7 +27,7 @@ function createImportExclusionSelector() {
items
} = importExclusions;
const mapping = id ? items.find((i) => i.id === id) : newImportListExclusion;
const mapping = id ? _.find(items, { id }) : newImportExclusion;
const settings = selectSettings(mapping, pendingChanges, saveError);
return {
@@ -65,10 +66,10 @@ class EditImportExclusionModalContentConnector extends Component {
componentDidMount() {
if (!this.props.id) {
Object.keys(newImportListExclusion).forEach((name) => {
Object.keys(newImportExclusion).forEach((name) => {
this.props.setImportExclusionValue({
name,
value: newImportListExclusion[name]
value: newImportExclusion[name]
});
});
}
@@ -10,7 +10,7 @@
.movieTitle {
@add-mixin truncate;
flex: 0 1 600px;
flex: 0 0 600px;
}
.tmdbId,
@@ -67,7 +67,7 @@ class ImportListExclusion extends Component {
)}
>
<div className={styles.tmdbId}>{tmdbId}</div>
<div className={styles.movieTitle} title={movieTitle}>{movieTitle}</div>
<div className={styles.movieTitle}>{movieTitle}</div>
<div className={styles.movieYear}>{movieYear}</div>
<div className={styles.actions}>
@@ -5,7 +5,7 @@
}
.title {
flex: 0 1 600px;
flex: 0 0 600px;
}
.tmdbId,

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