New: Browse Lists from Discover Movies Page

This commit is contained in:
Qstick 2020-09-03 22:50:56 -04:00 committed by GitHub
parent 54bcf9eed3
commit 361ffe353d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
308 changed files with 4824 additions and 3169 deletions

View file

@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions'; import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions';
import { fetchRootFolders } from 'Store/Actions/rootFolderActions'; import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
import { fetchNetImportExclusions } from 'Store/Actions/Settings/netImportExclusions'; import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
import parseUrl from 'Utilities/String/parseUrl'; import parseUrl from 'Utilities/String/parseUrl';
import AddNewMovie from './AddNewMovie'; import AddNewMovie from './AddNewMovie';
@ -29,7 +29,7 @@ const mapDispatchToProps = {
lookupMovie, lookupMovie,
clearAddMovie, clearAddMovie,
fetchRootFolders, fetchRootFolders,
fetchNetImportExclusions fetchImportExclusions
}; };
class AddNewMovieConnector extends Component { class AddNewMovieConnector extends Component {
@ -45,7 +45,7 @@ class AddNewMovieConnector extends Component {
componentDidMount() { componentDidMount() {
this.props.fetchRootFolders(); this.props.fetchRootFolders();
this.props.fetchNetImportExclusions(); this.props.fetchImportExclusions();
} }
componentWillUnmount() { componentWillUnmount() {
@ -102,7 +102,7 @@ AddNewMovieConnector.propTypes = {
lookupMovie: PropTypes.func.isRequired, lookupMovie: PropTypes.func.isRequired,
clearAddMovie: PropTypes.func.isRequired, clearAddMovie: PropTypes.func.isRequired,
fetchRootFolders: PropTypes.func.isRequired, fetchRootFolders: PropTypes.func.isRequired,
fetchNetImportExclusions: PropTypes.func.isRequired fetchImportExclusions: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector); export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);

View file

@ -118,7 +118,7 @@ class AddNewMovieSearchResult extends Component {
className={styles.exclusionIcon} className={styles.exclusionIcon}
name={icons.DANGER} name={icons.DANGER}
size={36} size={36}
title={translate('MovieIsOnNetImportExclusionList')} title={translate('MovieIsOnImportExclusionList')}
/> />
} }
</div> </div>

View file

@ -15,10 +15,10 @@ import MovieIndexConnector from 'Movie/Index/MovieIndexConnector';
import CustomFormatSettingsConnector from 'Settings/CustomFormats/CustomFormatSettingsConnector'; import CustomFormatSettingsConnector from 'Settings/CustomFormats/CustomFormatSettingsConnector';
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector'; import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
import ImportListSettingsConnector from 'Settings/ImportLists/ImportListSettingsConnector';
import IndexerSettingsConnector from 'Settings/Indexers/IndexerSettingsConnector'; import IndexerSettingsConnector from 'Settings/Indexers/IndexerSettingsConnector';
import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementConnector'; import MediaManagementConnector from 'Settings/MediaManagement/MediaManagementConnector';
import MetadataSettings from 'Settings/Metadata/MetadataSettings'; import MetadataSettings from 'Settings/Metadata/MetadataSettings';
import NetImportSettingsConnector from 'Settings/NetImport/NetImportSettingsConnector';
import NotificationSettings from 'Settings/Notifications/NotificationSettings'; import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Profiles from 'Settings/Profiles/Profiles'; import Profiles from 'Settings/Profiles/Profiles';
import Quality from 'Settings/Quality/Quality'; import Quality from 'Settings/Quality/Quality';
@ -156,8 +156,8 @@ function AppRoutes(props) {
/> />
<Route <Route
path="/settings/netimports" path="/settings/importlists"
component={NetImportSettingsConnector} component={ImportListSettingsConnector}
/> />
<Route <Route

View file

@ -16,3 +16,4 @@ export const RENAME_MOVIE = 'RenameMovie';
export const RESET_API_KEY = 'ResetApiKey'; export const RESET_API_KEY = 'ResetApiKey';
export const RSS_SYNC = 'RssSync'; export const RSS_SYNC = 'RssSync';
export const MOVIE_SEARCH = 'MoviesSearch'; export const MOVIE_SEARCH = 'MoviesSearch';
export const IMPORT_LIST_SYNC = 'ImportListSync';

View file

@ -6,6 +6,7 @@ import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Prop
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue'; import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue'; import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector'; import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
import ImportListFilterBuilderRowValueConnector from './ImportListFilterBuilderRowValueConnector';
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector'; import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
import MovieStatusFilterBuilderRowValue from './MovieStatusFilterBuilderRowValue'; import MovieStatusFilterBuilderRowValue from './MovieStatusFilterBuilderRowValue';
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue'; import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
@ -74,6 +75,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.TAG: case filterBuilderValueTypes.TAG:
return TagFilterBuilderRowValueConnector; return TagFilterBuilderRowValueConnector;
case filterBuilderValueTypes.IMPORTLIST:
return ImportListFilterBuilderRowValueConnector;
default: default:
return FilterBuilderRowValueConnector; return FilterBuilderRowValueConnector;
} }

View file

@ -0,0 +1,27 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createImportListSelector from 'Store/Selectors/createImportListSelector';
import FilterBuilderRowValue from './FilterBuilderRowValue';
function createMapStateToProps() {
return createSelector(
createImportListSelector(),
(importLists) => {
return {
tagList: importLists.map((importList) => {
const {
id,
name
} = importList;
return {
id,
name
};
})
};
}
);
}
export default connect(createMapStateToProps)(FilterBuilderRowValue);

View file

@ -0,0 +1,3 @@
.lists {
flex: 1 0 auto;
}

View file

@ -0,0 +1,43 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import { kinds, sizes } from 'Helpers/Props';
import Label from './Label';
import styles from './ImportListList.css';
function ImportListList({ lists, importListList }) {
return (
<div className={styles.lists}>
{
lists.map((t) => {
const list = _.find(importListList, { id: t });
if (!list) {
return null;
}
return (
<Label
key={list.id}
kind={kinds.INFO}
size={sizes.MEDIUM}
>
{list.name}
</Label>
);
})
}
</div>
);
}
ImportListList.propTypes = {
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
importListList: PropTypes.arrayOf(PropTypes.object).isRequired
};
ImportListList.defaultProps = {
lists: []
};
export default ImportListList;

View file

@ -0,0 +1,17 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createImportListSelector from 'Store/Selectors/createImportListSelector';
import ImportListList from './ImportListList';
function createMapStateToProps() {
return createSelector(
createImportListSelector(),
(importListList) => {
return {
importListList
};
}
);
}
export default connect(createMapStateToProps)(ImportListList);

View file

@ -6,7 +6,7 @@ import { createSelector } from 'reselect';
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions'; import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions'; import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchMovies } from 'Store/Actions/movieActions'; import { fetchMovies } from 'Store/Actions/movieActions';
import { fetchLanguages, fetchNetImports, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions'; import { fetchImportLists, fetchLanguages, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions'; import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions'; import { fetchTags } from 'Store/Actions/tagActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
@ -48,7 +48,7 @@ const selectIsPopulated = createSelector(
(state) => state.settings.ui.isPopulated, (state) => state.settings.ui.isPopulated,
(state) => state.settings.qualityProfiles.isPopulated, (state) => state.settings.qualityProfiles.isPopulated,
(state) => state.settings.languages.isPopulated, (state) => state.settings.languages.isPopulated,
(state) => state.settings.netImports.isPopulated, (state) => state.settings.importLists.isPopulated,
(state) => state.system.status.isPopulated, (state) => state.system.status.isPopulated,
( (
customFiltersIsPopulated, customFiltersIsPopulated,
@ -56,7 +56,7 @@ const selectIsPopulated = createSelector(
uiSettingsIsPopulated, uiSettingsIsPopulated,
qualityProfilesIsPopulated, qualityProfilesIsPopulated,
languagesIsPopulated, languagesIsPopulated,
netImportsIsPopulated, importListsIsPopulated,
systemStatusIsPopulated systemStatusIsPopulated
) => { ) => {
return ( return (
@ -65,7 +65,7 @@ const selectIsPopulated = createSelector(
uiSettingsIsPopulated && uiSettingsIsPopulated &&
qualityProfilesIsPopulated && qualityProfilesIsPopulated &&
languagesIsPopulated && languagesIsPopulated &&
netImportsIsPopulated && importListsIsPopulated &&
systemStatusIsPopulated systemStatusIsPopulated
); );
} }
@ -77,7 +77,7 @@ const selectErrors = createSelector(
(state) => state.settings.ui.error, (state) => state.settings.ui.error,
(state) => state.settings.qualityProfiles.error, (state) => state.settings.qualityProfiles.error,
(state) => state.settings.languages.error, (state) => state.settings.languages.error,
(state) => state.settings.netImports.error, (state) => state.settings.importLists.error,
(state) => state.system.status.error, (state) => state.system.status.error,
( (
customFiltersError, customFiltersError,
@ -85,7 +85,7 @@ const selectErrors = createSelector(
uiSettingsError, uiSettingsError,
qualityProfilesError, qualityProfilesError,
languagesError, languagesError,
netImportsError, importListsError,
systemStatusError systemStatusError
) => { ) => {
const hasError = !!( const hasError = !!(
@ -94,7 +94,7 @@ const selectErrors = createSelector(
uiSettingsError || uiSettingsError ||
qualityProfilesError || qualityProfilesError ||
languagesError || languagesError ||
netImportsError || importListsError ||
systemStatusError systemStatusError
); );
@ -105,7 +105,7 @@ const selectErrors = createSelector(
uiSettingsError, uiSettingsError,
qualityProfilesError, qualityProfilesError,
languagesError, languagesError,
netImportsError, importListsError,
systemStatusError systemStatusError
}; };
} }
@ -153,8 +153,8 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchLanguages() { dispatchFetchLanguages() {
dispatch(fetchLanguages()); dispatch(fetchLanguages());
}, },
dispatchFetchNetImports() { dispatchFetchImportLists() {
dispatch(fetchNetImports()); dispatch(fetchImportLists());
}, },
dispatchFetchUISettings() { dispatchFetchUISettings() {
dispatch(fetchUISettings()); dispatch(fetchUISettings());
@ -191,7 +191,7 @@ class PageConnector extends Component {
this.props.dispatchFetchTags(); this.props.dispatchFetchTags();
this.props.dispatchFetchQualityProfiles(); this.props.dispatchFetchQualityProfiles();
this.props.dispatchFetchLanguages(); this.props.dispatchFetchLanguages();
this.props.dispatchFetchNetImports(); this.props.dispatchFetchImportLists();
this.props.dispatchFetchUISettings(); this.props.dispatchFetchUISettings();
this.props.dispatchFetchStatus(); this.props.dispatchFetchStatus();
} }
@ -215,7 +215,7 @@ class PageConnector extends Component {
dispatchFetchTags, dispatchFetchTags,
dispatchFetchQualityProfiles, dispatchFetchQualityProfiles,
dispatchFetchLanguages, dispatchFetchLanguages,
dispatchFetchNetImports, dispatchFetchImportLists,
dispatchFetchUISettings, dispatchFetchUISettings,
dispatchFetchStatus, dispatchFetchStatus,
...otherProps ...otherProps
@ -254,7 +254,7 @@ PageConnector.propTypes = {
dispatchFetchTags: PropTypes.func.isRequired, dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchQualityProfiles: PropTypes.func.isRequired, dispatchFetchQualityProfiles: PropTypes.func.isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired, dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchNetImports: PropTypes.func.isRequired, dispatchFetchImportLists: PropTypes.func.isRequired,
dispatchFetchUISettings: PropTypes.func.isRequired, dispatchFetchUISettings: PropTypes.func.isRequired,
dispatchFetchStatus: PropTypes.func.isRequired, dispatchFetchStatus: PropTypes.func.isRequired,
onSidebarVisibleChange: PropTypes.func.isRequired onSidebarVisibleChange: PropTypes.func.isRequired

View file

@ -98,7 +98,7 @@ const links = [
}, },
{ {
title: translate('Lists'), title: translate('Lists'),
to: '/settings/netimports' to: '/settings/importlists'
}, },
{ {
title: translate('Connect'), title: translate('Connect'),

View file

@ -18,26 +18,27 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll'; import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected'; import toggleSelected from 'Utilities/Table/toggleSelected';
import DiscoverMovieFooterConnector from './DiscoverMovieFooterConnector'; import DiscoverMovieFooterConnector from './DiscoverMovieFooterConnector';
import AddListMovieFilterMenu from './Menus/AddListMovieFilterMenu'; import DiscoverMovieFilterMenu from './Menus/DiscoverMovieFilterMenu';
import AddListMovieSortMenu from './Menus/AddListMovieSortMenu'; import DiscoverMovieSortMenu from './Menus/DiscoverMovieSortMenu';
import AddListMovieViewMenu from './Menus/AddListMovieViewMenu'; import DiscoverMovieViewMenu from './Menus/DiscoverMovieViewMenu';
import NoDiscoverMovie from './NoDiscoverMovie'; import NoDiscoverMovie from './NoDiscoverMovie';
import AddListMovieOverviewsConnector from './Overview/AddListMovieOverviewsConnector'; import DiscoverMovieOverviewsConnector from './Overview/DiscoverMovieOverviewsConnector';
import AddListMovieOverviewOptionsModal from './Overview/Options/AddListMovieOverviewOptionsModal'; import DiscoverMovieOverviewOptionsModal from './Overview/Options/DiscoverMovieOverviewOptionsModal';
import AddListMoviePostersConnector from './Posters/AddListMoviePostersConnector'; import DiscoverMoviePostersConnector from './Posters/DiscoverMoviePostersConnector';
import AddListMoviePosterOptionsModal from './Posters/Options/AddListMoviePosterOptionsModal'; import DiscoverMoviePosterOptionsModal from './Posters/Options/DiscoverMoviePosterOptionsModal';
import AddListMovieTableConnector from './Table/AddListMovieTableConnector'; import DiscoverMovieTableConnector from './Table/DiscoverMovieTableConnector';
import DiscoverMovieTableOptionsConnector from './Table/DiscoverMovieTableOptionsConnector';
function getViewComponent(view) { function getViewComponent(view) {
if (view === 'posters') { if (view === 'posters') {
return AddListMoviePostersConnector; return DiscoverMoviePostersConnector;
} }
if (view === 'overview') { if (view === 'overview') {
return AddListMovieOverviewsConnector; return DiscoverMovieOverviewsConnector;
} }
return AddListMovieTableConnector; return DiscoverMovieTableConnector;
} }
class DiscoverMovie extends Component { class DiscoverMovie extends Component {
@ -212,6 +213,10 @@ class DiscoverMovie extends Component {
this.onSelectAllChange({ value: !this.state.allSelected }); this.onSelectAllChange({ value: !this.state.allSelected });
} }
onImportListSyncPress = () => {
this.props.onImportListSyncPress();
}
onSelectedChange = ({ id, value, shiftKey = false }) => { onSelectedChange = ({ id, value, shiftKey = false }) => {
this.setState((state) => { this.setState((state) => {
return toggleSelected(state, this.props.items, id, value, shiftKey, 'tmdbId'); return toggleSelected(state, this.props.items, id, value, shiftKey, 'tmdbId');
@ -248,6 +253,7 @@ class DiscoverMovie extends Component {
onViewSelect, onViewSelect,
onScroll, onScroll,
onAddMoviesPress, onAddMoviesPress,
isSyncingLists,
...otherProps ...otherProps
} = this.props; } = this.props;
@ -272,6 +278,13 @@ class DiscoverMovie extends Component {
<PageContent> <PageContent>
<PageToolbar> <PageToolbar>
<PageToolbarSection> <PageToolbarSection>
<PageToolbarButton
label='Refresh Lists'
iconName={icons.REFRESH}
isSpinning={isSyncingLists}
isDisabled={hasNoMovie}
onPress={this.onImportListSyncPress}
/>
<PageToolbarButton <PageToolbarButton
label={allSelected ? 'Unselect All' : 'Select All'} label={allSelected ? 'Unselect All' : 'Select All'}
iconName={icons.CHECK_SQUARE} iconName={icons.CHECK_SQUARE}
@ -289,6 +302,7 @@ class DiscoverMovie extends Component {
<TableOptionsModalWrapper <TableOptionsModalWrapper
{...otherProps} {...otherProps}
columns={columns} columns={columns}
optionsComponent={DiscoverMovieTableOptionsConnector}
> >
<PageToolbarButton <PageToolbarButton
label={translate('Options')} label={translate('Options')}
@ -325,20 +339,20 @@ class DiscoverMovie extends Component {
<PageToolbarSeparator /> <PageToolbarSeparator />
} }
<AddListMovieViewMenu <DiscoverMovieViewMenu
view={view} view={view}
isDisabled={hasNoMovie} isDisabled={hasNoMovie}
onViewSelect={onViewSelect} onViewSelect={onViewSelect}
/> />
<AddListMovieSortMenu <DiscoverMovieSortMenu
sortKey={sortKey} sortKey={sortKey}
sortDirection={sortDirection} sortDirection={sortDirection}
isDisabled={hasNoMovie} isDisabled={hasNoMovie}
onSortSelect={onSortSelect} onSortSelect={onSortSelect}
/> />
<AddListMovieFilterMenu <DiscoverMovieFilterMenu
selectedFilterKey={selectedFilterKey} selectedFilterKey={selectedFilterKey}
filters={filters} filters={filters}
customFilters={customFilters} customFilters={customFilters}
@ -409,12 +423,12 @@ class DiscoverMovie extends Component {
/> />
} }
<AddListMoviePosterOptionsModal <DiscoverMoviePosterOptionsModal
isOpen={isPosterOptionsModalOpen} isOpen={isPosterOptionsModalOpen}
onModalClose={this.onPosterOptionsModalClose} onModalClose={this.onPosterOptionsModalClose}
/> />
<AddListMovieOverviewOptionsModal <DiscoverMovieOverviewOptionsModal
isOpen={isOverviewOptionsModalOpen} isOpen={isOverviewOptionsModalOpen}
onModalClose={this.onOverviewOptionsModalClose} onModalClose={this.onOverviewOptionsModalClose}
/> />
@ -436,13 +450,15 @@ DiscoverMovie.propTypes = {
sortKey: PropTypes.string, sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all), sortDirection: PropTypes.oneOf(sortDirections.all),
view: PropTypes.string.isRequired, view: PropTypes.string.isRequired,
isSyncingLists: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
onSortSelect: PropTypes.func.isRequired, onSortSelect: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired, onFilterSelect: PropTypes.func.isRequired,
onViewSelect: PropTypes.func.isRequired, onViewSelect: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired, onScroll: PropTypes.func.isRequired,
onAddMoviesPress: PropTypes.func.isRequired, onAddMoviesPress: PropTypes.func.isRequired,
onExcludeMoviesPress: PropTypes.func.isRequired onExcludeMoviesPress: PropTypes.func.isRequired,
onImportListSyncPress: PropTypes.func.isRequired
}; };
export default DiscoverMovie; export default DiscoverMovie;

View file

@ -2,11 +2,14 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import withScrollPosition from 'Components/withScrollPosition'; import withScrollPosition from 'Components/withScrollPosition';
import { addMovies, addNetImportExclusions, clearAddMovie, fetchDiscoverMovies, setListMovieFilter, setListMovieSort, setListMovieTableOption, setListMovieView } from 'Store/Actions/discoverMovieActions'; import { executeCommand } from 'Store/Actions/commandActions';
import { addImportExclusions, addMovies, clearAddMovie, fetchDiscoverMovies, setListMovieFilter, setListMovieSort, setListMovieTableOption, setListMovieView } from 'Store/Actions/discoverMovieActions';
import { fetchRootFolders } from 'Store/Actions/rootFolderActions'; import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
import { fetchNetImportExclusions } from 'Store/Actions/Settings/netImportExclusions'; import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions';
import scrollPositions from 'Store/scrollPositions'; import scrollPositions from 'Store/scrollPositions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createDiscoverMovieClientSideCollectionItemsSelector from 'Store/Selectors/createDiscoverMovieClientSideCollectionItemsSelector'; import createDiscoverMovieClientSideCollectionItemsSelector from 'Store/Selectors/createDiscoverMovieClientSideCollectionItemsSelector';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator'; import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
@ -15,13 +18,16 @@ import DiscoverMovie from './DiscoverMovie';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
createDiscoverMovieClientSideCollectionItemsSelector('discoverMovie'), createDiscoverMovieClientSideCollectionItemsSelector('discoverMovie'),
createCommandExecutingSelector(commandNames.IMPORT_LIST_SYNC),
createDimensionsSelector(), createDimensionsSelector(),
( (
movies, movies,
isSyncingLists,
dimensionsState dimensionsState
) => { ) => {
return { return {
...movies, ...movies,
isSyncingLists,
isSmallScreen: dimensionsState.isSmallScreen isSmallScreen: dimensionsState.isSmallScreen
}; };
} }
@ -34,8 +40,8 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(fetchRootFolders()); dispatch(fetchRootFolders());
}, },
dispatchFetchNetImportExclusions() { dispatchFetchImportExclusions() {
dispatch(fetchNetImportExclusions()); dispatch(fetchImportExclusions());
}, },
dispatchClearListMovie() { dispatchClearListMovie() {
@ -66,8 +72,14 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(addMovies({ ids, addOptions })); dispatch(addMovies({ ids, addOptions }));
}, },
dispatchAddNetImportExclusions(exclusions) { dispatchAddImportExclusions(exclusions) {
dispatch(addNetImportExclusions(exclusions)); dispatch(addImportExclusions(exclusions));
},
onImportListSyncPress() {
dispatch(executeCommand({
name: commandNames.IMPORT_LIST_SYNC
}));
} }
}; };
} }
@ -80,7 +92,7 @@ class DiscoverMovieConnector extends Component {
componentDidMount() { componentDidMount() {
registerPagePopulator(this.repopulate); registerPagePopulator(this.repopulate);
this.props.dispatchFetchRootFolders(); this.props.dispatchFetchRootFolders();
this.props.dispatchFetchNetImportExclusions(); this.props.dispatchFetchImportExclusions();
this.props.dispatchFetchListMovies(); this.props.dispatchFetchListMovies();
} }
@ -105,7 +117,7 @@ class DiscoverMovieConnector extends Component {
} }
onExcludeMoviesPress =({ ids }) => { onExcludeMoviesPress =({ ids }) => {
this.props.dispatchAddNetImportExclusions({ ids }); this.props.dispatchAddImportExclusions({ ids });
} }
// //
@ -119,6 +131,7 @@ class DiscoverMovieConnector extends Component {
onScroll={this.onScroll} onScroll={this.onScroll}
onAddMoviesPress={this.onAddMoviesPress} onAddMoviesPress={this.onAddMoviesPress}
onExcludeMoviesPress={this.onExcludeMoviesPress} onExcludeMoviesPress={this.onExcludeMoviesPress}
onSyncListsPress={this.onSyncListsPress}
/> />
); );
} }
@ -127,13 +140,13 @@ class DiscoverMovieConnector extends Component {
DiscoverMovieConnector.propTypes = { DiscoverMovieConnector.propTypes = {
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
view: PropTypes.string.isRequired, view: PropTypes.string.isRequired,
dispatchFetchNetImportExclusions: PropTypes.func.isRequired, dispatchFetchImportExclusions: PropTypes.func.isRequired,
dispatchFetchRootFolders: PropTypes.func.isRequired, dispatchFetchRootFolders: PropTypes.func.isRequired,
dispatchFetchListMovies: PropTypes.func.isRequired, dispatchFetchListMovies: PropTypes.func.isRequired,
dispatchClearListMovie: PropTypes.func.isRequired, dispatchClearListMovie: PropTypes.func.isRequired,
dispatchSetListMovieView: PropTypes.func.isRequired, dispatchSetListMovieView: PropTypes.func.isRequired,
dispatchAddMovies: PropTypes.func.isRequired, dispatchAddMovies: PropTypes.func.isRequired,
dispatchAddNetImportExclusions: PropTypes.func.isRequired dispatchAddImportExclusions: PropTypes.func.isRequired
}; };
export default withScrollPosition( export default withScrollPosition(

View file

@ -29,7 +29,7 @@
.excludeSelectedButton { .excludeSelectedButton {
composes: button from '~Components/Link/SpinnerButton.css'; composes: button from '~Components/Link/SpinnerButton.css';
margin-left: 50px; margin-left: 25px;
height: 35px; height: 35px;
} }

View file

@ -8,9 +8,9 @@ import DiscoverMovieFooter from './DiscoverMovieFooter';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.discoverMovie, (state) => state.discoverMovie,
(state) => state.settings.netImportExclusions, (state) => state.settings.importExclusions,
(state, { selectedIds }) => selectedIds, (state, { selectedIds }) => selectedIds,
(discoverMovie, netImportExclusions, selectedIds) => { (discoverMovie, importExclusions, selectedIds) => {
const { const {
monitor: defaultMonitor, monitor: defaultMonitor,
qualityProfileId: defaultQualityProfileId, qualityProfileId: defaultQualityProfileId,
@ -25,7 +25,7 @@ function createMapStateToProps() {
const { const {
isSaving isSaving
} = netImportExclusions; } = importExclusions;
return { return {
selectedCount: selectedIds.length, selectedCount: selectedIds.length,

View file

@ -27,7 +27,7 @@ function createMapStateToProps() {
); );
} }
class AddListMovieItemConnector extends Component { class DiscoverMovieItemConnector extends Component {
// //
// Render // Render
@ -52,9 +52,9 @@ class AddListMovieItemConnector extends Component {
} }
} }
AddListMovieItemConnector.propTypes = { DiscoverMovieItemConnector.propTypes = {
tmdbId: PropTypes.number, tmdbId: PropTypes.number,
component: PropTypes.elementType.isRequired component: PropTypes.elementType.isRequired
}; };
export default connect(createMapStateToProps)(AddListMovieItemConnector); export default connect(createMapStateToProps)(DiscoverMovieItemConnector);

View file

@ -1,11 +1,11 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { addNetImportExclusions } from 'Store/Actions/discoverMovieActions'; import { addImportExclusions } from 'Store/Actions/discoverMovieActions';
import ExcludeMovieModalContent from './ExcludeMovieModalContent'; import ExcludeMovieModalContent from './ExcludeMovieModalContent';
const mapDispatchToProps = { const mapDispatchToProps = {
addNetImportExclusions addImportExclusions
}; };
class ExcludeMovieModalContentConnector extends Component { class ExcludeMovieModalContentConnector extends Component {
@ -14,7 +14,7 @@ class ExcludeMovieModalContentConnector extends Component {
// Listeners // Listeners
onExcludePress = () => { onExcludePress = () => {
this.props.addNetImportExclusions({ ids: [this.props.tmdbId] }); this.props.addImportExclusions({ ids: [this.props.tmdbId] });
this.props.onModalClose(true); this.props.onModalClose(true);
} }
@ -37,7 +37,7 @@ ExcludeMovieModalContentConnector.propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
year: PropTypes.number.isRequired, year: PropTypes.number.isRequired,
onModalClose: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired,
addNetImportExclusions: PropTypes.func.isRequired addImportExclusions: PropTypes.func.isRequired
}; };
export default connect(undefined, mapDispatchToProps)(ExcludeMovieModalContentConnector); export default connect(undefined, mapDispatchToProps)(ExcludeMovieModalContentConnector);

View file

@ -1,10 +1,10 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import FilterMenu from 'Components/Menu/FilterMenu'; import FilterMenu from 'Components/Menu/FilterMenu';
import AddListMovieFilterModalConnector from 'DiscoverMovie/AddListMovieFilterModalConnector'; import DiscoverMovieFilterModalConnector from 'DiscoverMovie/DiscoverMovieFilterModalConnector';
import { align } from 'Helpers/Props'; import { align } from 'Helpers/Props';
function AddListMovieFilterMenu(props) { function DiscoverMovieFilterMenu(props) {
const { const {
selectedFilterKey, selectedFilterKey,
filters, filters,
@ -20,13 +20,13 @@ function AddListMovieFilterMenu(props) {
selectedFilterKey={selectedFilterKey} selectedFilterKey={selectedFilterKey}
filters={filters} filters={filters}
customFilters={customFilters} customFilters={customFilters}
filterModalConnectorComponent={AddListMovieFilterModalConnector} filterModalConnectorComponent={DiscoverMovieFilterModalConnector}
onFilterSelect={onFilterSelect} onFilterSelect={onFilterSelect}
/> />
); );
} }
AddListMovieFilterMenu.propTypes = { DiscoverMovieFilterMenu.propTypes = {
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired, filters: PropTypes.arrayOf(PropTypes.object).isRequired,
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired, customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
@ -34,8 +34,8 @@ AddListMovieFilterMenu.propTypes = {
onFilterSelect: PropTypes.func.isRequired onFilterSelect: PropTypes.func.isRequired
}; };
AddListMovieFilterMenu.defaultProps = { DiscoverMovieFilterMenu.defaultProps = {
showCustomFilters: false showCustomFilters: false
}; };
export default AddListMovieFilterMenu; export default DiscoverMovieFilterMenu;

View file

@ -5,7 +5,7 @@ import SortMenu from 'Components/Menu/SortMenu';
import SortMenuItem from 'Components/Menu/SortMenuItem'; import SortMenuItem from 'Components/Menu/SortMenuItem';
import { align, sortDirections } from 'Helpers/Props'; import { align, sortDirections } from 'Helpers/Props';
function AddListMovieSortMenu(props) { function DiscoverMovieSortMenu(props) {
const { const {
sortKey, sortKey,
sortDirection, sortDirection,
@ -64,6 +64,24 @@ function AddListMovieSortMenu(props) {
Physical Release Physical Release
</SortMenuItem> </SortMenuItem>
<SortMenuItem
name="digitalRelease"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
Digital Release
</SortMenuItem>
<SortMenuItem
name="runtime"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
Runtime
</SortMenuItem>
<SortMenuItem <SortMenuItem
name="ratings" name="ratings"
sortKey={sortKey} sortKey={sortKey}
@ -86,11 +104,11 @@ function AddListMovieSortMenu(props) {
); );
} }
AddListMovieSortMenu.propTypes = { DiscoverMovieSortMenu.propTypes = {
sortKey: PropTypes.string, sortKey: PropTypes.string,
sortDirection: PropTypes.oneOf(sortDirections.all), sortDirection: PropTypes.oneOf(sortDirections.all),
isDisabled: PropTypes.bool.isRequired, isDisabled: PropTypes.bool.isRequired,
onSortSelect: PropTypes.func.isRequired onSortSelect: PropTypes.func.isRequired
}; };
export default AddListMovieSortMenu; export default DiscoverMovieSortMenu;

View file

@ -5,7 +5,7 @@ import ViewMenu from 'Components/Menu/ViewMenu';
import ViewMenuItem from 'Components/Menu/ViewMenuItem'; import ViewMenuItem from 'Components/Menu/ViewMenuItem';
import { align } from 'Helpers/Props'; import { align } from 'Helpers/Props';
function AddListMovieViewMenu(props) { function DiscoverMovieViewMenu(props) {
const { const {
view, view,
isDisabled, isDisabled,
@ -46,10 +46,10 @@ function AddListMovieViewMenu(props) {
); );
} }
AddListMovieViewMenu.propTypes = { DiscoverMovieViewMenu.propTypes = {
view: PropTypes.string.isRequired, view: PropTypes.string.isRequired,
isDisabled: PropTypes.bool.isRequired, isDisabled: PropTypes.bool.isRequired,
onViewSelect: PropTypes.func.isRequired onViewSelect: PropTypes.func.isRequired
}; };
export default AddListMovieViewMenu; export default DiscoverMovieViewMenu;

View file

@ -44,7 +44,7 @@ function NoDiscoverMovie(props) {
<div className={styles.buttonContainer}> <div className={styles.buttonContainer}>
<Button <Button
to="/settings/netimports" to="/settings/importlists"
kind={kinds.PRIMARY} kind={kinds.PRIMARY}
> >
{translate('AddList')} {translate('AddList')}

View file

@ -32,6 +32,12 @@ $hoverScale: 1.05;
} }
} }
.alreadyExistsIcon {
margin-left: 10px;
color: #37bc9b;
pointer-events: all;
}
.exclusionIcon { .exclusionIcon {
margin-left: 10px; margin-left: 10px;
color: $dangerColor; color: $dangerColor;
@ -50,10 +56,15 @@ $hoverScale: 1.05;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex: 0 0 auto; flex: 0 0 auto;
margin-bottom: 10px; margin-bottom: 5px;
line-height: 32px; line-height: 32px;
} }
.lists {
display: flex;
margin-bottom: 5px;
}
.title { .title {
@add-mixin truncate; @add-mixin truncate;
composes: link; composes: link;
@ -61,6 +72,7 @@ $hoverScale: 1.05;
flex: 1 0 1px; flex: 1 0 1px;
font-weight: 300; font-weight: 300;
font-size: 30px; font-size: 30px;
line-height: 1;
} }
.actions { .actions {

View file

@ -3,27 +3,30 @@ import React, { Component } from 'react';
import TextTruncate from 'react-text-truncate'; import TextTruncate from 'react-text-truncate';
import CheckInput from 'Components/Form/CheckInput'; import CheckInput from 'Components/Form/CheckInput';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import ImportListListConnector from 'Components/ImportListListConnector';
import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import Popover from 'Components/Tooltip/Popover'; import Popover from 'Components/Tooltip/Popover';
import AddNewDiscoverMovieModal from 'DiscoverMovie/AddNewDiscoverMovieModal'; import AddNewDiscoverMovieModal from 'DiscoverMovie/AddNewDiscoverMovieModal';
import ExcludeMovieModal from 'DiscoverMovie/Exclusion/ExcludeMovieModal'; import ExcludeMovieModal from 'DiscoverMovie/Exclusion/ExcludeMovieModal';
import { icons } from 'Helpers/Props'; import { icons, kinds } from 'Helpers/Props';
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks'; import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
import MoviePoster from 'Movie/MoviePoster'; import MoviePoster from 'Movie/MoviePoster';
import dimensions from 'Styles/Variables/dimensions'; import dimensions from 'Styles/Variables/dimensions';
import fonts from 'Styles/Variables/fonts'; import fonts from 'Styles/Variables/fonts';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './AddListMovieOverview.css'; import DiscoverMovieOverviewInfo from './DiscoverMovieOverviewInfo';
import styles from './DiscoverMovieOverview.css';
const columnPadding = parseInt(dimensions.movieIndexColumnPadding); const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen); const columnPaddingSmallScreen = parseInt(dimensions.movieIndexColumnPaddingSmallScreen);
const defaultFontSize = parseInt(fonts.defaultFontSize); const defaultFontSize = parseInt(fonts.defaultFontSize);
const lineHeight = parseFloat(fonts.lineHeight); const lineHeight = parseFloat(fonts.lineHeight);
// Hardcoded height beased on line-height of 32 + bottom margin of 10. // Hardcoded height beased on line-height of 32 + bottom margin of 10. 19 + 5 for List Row
// Less side-effecty than using react-measure. // Less side-effecty than using react-measure.
const titleRowHeight = 42; const titleRowHeight = 66;
function getContentHeight(rowHeight, isSmallScreen) { function getContentHeight(rowHeight, isSmallScreen) {
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding; const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
@ -31,7 +34,7 @@ function getContentHeight(rowHeight, isSmallScreen) {
return rowHeight - padding; return rowHeight - padding;
} }
class AddListMovieOverview extends Component { class DiscoverMovieOverview extends Component {
// //
// Lifecycle // Lifecycle
@ -82,18 +85,21 @@ class AddListMovieOverview extends Component {
imdbId, imdbId,
youTubeTrailerId, youTubeTrailerId,
title, title,
titleSlug,
folder, folder,
year, year,
overview, overview,
images, images,
lists,
posterWidth, posterWidth,
posterHeight, posterHeight,
rowHeight, rowHeight,
isSmallScreen, isSmallScreen,
isExisting, isExisting,
isExcluded, isExcluded,
isSelected isRecommendation,
isSelected,
overviewOptions,
...otherProps
} = this.props; } = this.props;
const { const {
@ -106,7 +112,7 @@ class AddListMovieOverview extends Component {
height: `${posterHeight}px` height: `${posterHeight}px`
}; };
const linkProps = isExisting ? { to: `/movie/${titleSlug}` } : { onPress: this.onPress }; const linkProps = isExisting ? { to: `/movie/${tmdbId}` } : { onPress: this.onPress };
const contentHeight = getContentHeight(rowHeight, isSmallScreen); const contentHeight = getContentHeight(rowHeight, isSmallScreen);
const overviewHeight = contentHeight - titleRowHeight; const overviewHeight = contentHeight - titleRowHeight;
@ -142,15 +148,24 @@ class AddListMovieOverview extends Component {
className={styles.title} className={styles.title}
{...linkProps} {...linkProps}
> >
{title} { year > 0 ? `(${year})` : ''} {title}
{
isExisting ?
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={30}
title={'Already in your Library'}
/> : null
}
{ {
isExcluded && isExcluded &&
<Icon <Icon
className={styles.exclusionIcon} className={styles.exclusionIcon}
name={icons.DANGER} name={icons.DANGER}
size={36} size={30}
title='Movie is on Net Import Exclusion List' title='Movie is on Import Exclusion List'
/> />
} }
</Link> </Link>
@ -184,12 +199,38 @@ class AddListMovieOverview extends Component {
</div> </div>
</div> </div>
<div className={styles.lists}>
{
isRecommendation ?
<Label
kind={kinds.INFO}
>
<Icon
name={icons.RECOMMENDED}
size={10}
/>
</Label> :
null
}
<ImportListListConnector
lists={lists}
/>
</div>
<div className={styles.details}> <div className={styles.details}>
<TextTruncate <TextTruncate
line={Math.floor(overviewHeight / (defaultFontSize * lineHeight))} line={Math.floor(overviewHeight / (defaultFontSize * lineHeight))}
text={overview} text={overview}
/> />
<DiscoverMovieOverviewInfo
height={overviewHeight}
year={year}
{...overviewOptions}
{...otherProps}
/>
</div> </div>
</div> </div>
</div> </div>
@ -217,7 +258,7 @@ class AddListMovieOverview extends Component {
} }
} }
AddListMovieOverview.propTypes = { DiscoverMovieOverview.propTypes = {
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
imdbId: PropTypes.string, imdbId: PropTypes.string,
youTubeTrailerId: PropTypes.string, youTubeTrailerId: PropTypes.string,
@ -226,7 +267,6 @@ AddListMovieOverview.propTypes = {
year: PropTypes.number.isRequired, year: PropTypes.number.isRequired,
overview: PropTypes.string.isRequired, overview: PropTypes.string.isRequired,
status: PropTypes.string.isRequired, status: PropTypes.string.isRequired,
titleSlug: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired, images: PropTypes.arrayOf(PropTypes.object).isRequired,
posterWidth: PropTypes.number.isRequired, posterWidth: PropTypes.number.isRequired,
posterHeight: PropTypes.number.isRequired, posterHeight: PropTypes.number.isRequired,
@ -239,8 +279,14 @@ AddListMovieOverview.propTypes = {
isSmallScreen: PropTypes.bool.isRequired, isSmallScreen: PropTypes.bool.isRequired,
isExisting: PropTypes.bool.isRequired, isExisting: PropTypes.bool.isRequired,
isExcluded: PropTypes.bool.isRequired, isExcluded: PropTypes.bool.isRequired,
isRecommendation: PropTypes.bool.isRequired,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };
export default AddListMovieOverview; DiscoverMovieOverview.defaultProps = {
lists: []
};
export default DiscoverMovieOverview;

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import AddListMovieOverview from './AddListMovieOverview'; import DiscoverMovieOverview from './DiscoverMovieOverview';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -14,4 +14,4 @@ function createMapStateToProps() {
); );
} }
export default connect(createMapStateToProps)(AddListMovieOverview); export default connect(createMapStateToProps)(DiscoverMovieOverview);

View file

@ -0,0 +1,12 @@
.infos {
display: flex;
flex: 0 0 250px;
flex-direction: column;
margin-left: 10px;
}
@media only screen and (max-width: $breakpointSmall) {
.infos {
margin-left: 0;
}
}

View file

@ -0,0 +1,147 @@
import PropTypes from 'prop-types';
import React from 'react';
import { icons } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import DiscoverMovieOverviewInfoRow from './DiscoverMovieOverviewInfoRow';
import styles from './DiscoverMovieOverviewInfo.css';
const infoRowHeight = parseInt(dimensions.movieIndexOverviewInfoRowHeight);
const rows = [
{
name: 'year',
showProp: 'showYear',
valueProp: 'year'
},
{
name: 'genres',
showProp: 'showGenres',
valueProp: 'genres'
},
{
name: 'ratings',
showProp: 'showRatings',
valueProp: 'ratings'
},
{
name: 'certification',
showProp: 'showCertification',
valueProp: 'certification'
},
{
name: 'studio',
showProp: 'showStudio',
valueProp: 'studio'
}
];
function isVisible(row, props) {
const {
name,
showProp,
valueProp
} = row;
if (props[valueProp] == null) {
return false;
}
return props[showProp] || props.sortKey === name;
}
function getInfoRowProps(row, props) {
const { name } = row;
if (name === 'year') {
return {
title: 'Year',
iconName: icons.CALENDAR,
label: props.year
};
}
if (name === 'genres') {
return {
title: 'Genres',
iconName: icons.GENRE,
label: props.genres.slice(0, 2).join(', ')
};
}
if (name === 'ratings') {
return {
title: 'Ratings',
iconName: icons.HEART,
label: `${props.ratings.value * 10}%`
};
}
if (name === 'certification') {
return {
title: 'Certification',
iconName: icons.FILM,
label: props.certification
};
}
if (name === 'studio') {
return {
title: 'Studio',
iconName: icons.STUDIO,
label: props.studio
};
}
}
function DiscoverMovieOverviewInfo(props) {
const {
height
} = props;
let shownRows = 1;
const maxRows = Math.floor(height / (infoRowHeight + 4));
return (
<div className={styles.infos}>
{
rows.map((row) => {
if (!isVisible(row, props)) {
return null;
}
if (shownRows >= maxRows) {
return null;
}
shownRows++;
const infoRowProps = getInfoRowProps(row, props);
return (
<DiscoverMovieOverviewInfoRow
key={row.name}
{...infoRowProps}
/>
);
})
}
</div>
);
}
DiscoverMovieOverviewInfo.propTypes = {
height: PropTypes.number.isRequired,
showStudio: PropTypes.bool.isRequired,
showYear: PropTypes.bool.isRequired,
showRatings: PropTypes.bool.isRequired,
showCertification: PropTypes.bool.isRequired,
showGenres: PropTypes.bool.isRequired,
studio: PropTypes.string,
year: PropTypes.number,
certification: PropTypes.string,
ratings: PropTypes.object,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
sortKey: PropTypes.string.isRequired
};
export default DiscoverMovieOverviewInfo;

View file

@ -0,0 +1,10 @@
.infoRow {
flex: 0 0 $movieIndexOverviewInfoRowHeight;
margin: 2px 0;
}
.icon {
margin-right: 5px;
width: 25px !important;
text-align: center;
}

View file

@ -0,0 +1,35 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import styles from './DiscoverMovieOverviewInfoRow.css';
function DiscoverMovieOverviewInfoRow(props) {
const {
title,
iconName,
label
} = props;
return (
<div
className={styles.infoRow}
title={title}
>
<Icon
className={styles.icon}
name={iconName}
size={14}
/>
{label}
</div>
);
}
DiscoverMovieOverviewInfoRow.propTypes = {
title: PropTypes.string,
iconName: PropTypes.object.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
};
export default DiscoverMovieOverviewInfoRow;

View file

@ -2,12 +2,12 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Grid, WindowScroller } from 'react-virtualized'; import { Grid, WindowScroller } from 'react-virtualized';
import Measure from 'Components/Measure'; import Measure from 'Components/Measure';
import AddListMovieItemConnector from 'DiscoverMovie/AddListMovieItemConnector'; import DiscoverMovieItemConnector from 'DiscoverMovie/DiscoverMovieItemConnector';
import dimensions from 'Styles/Variables/dimensions'; import dimensions from 'Styles/Variables/dimensions';
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter'; import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder'; import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import AddListMovieOverviewConnector from './AddListMovieOverviewConnector'; import DiscoverMovieOverviewConnector from './DiscoverMovieOverviewConnector';
import styles from './AddListMovieOverviews.css'; import styles from './DiscoverMovieOverviews.css';
// Poster container dimensions // Poster container dimensions
const columnPadding = parseInt(dimensions.movieIndexColumnPadding); const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
@ -41,7 +41,7 @@ function calculatePosterHeight(posterWidth) {
return Math.ceil((250 / 170) * posterWidth); return Math.ceil((250 / 170) * posterWidth);
} }
class AddListMovieOverviews extends Component { class DiscoverMovieOverviews extends Component {
// //
// Lifecycle // Lifecycle
@ -156,9 +156,9 @@ class AddListMovieOverviews extends Component {
key={key} key={key}
style={style} style={style}
> >
<AddListMovieItemConnector <DiscoverMovieItemConnector
key={movie.tmdbId} key={movie.tmdbId}
component={AddListMovieOverviewConnector} component={DiscoverMovieOverviewConnector}
sortKey={sortKey} sortKey={sortKey}
posterWidth={posterWidth} posterWidth={posterWidth}
posterHeight={posterHeight} posterHeight={posterHeight}
@ -243,7 +243,7 @@ class AddListMovieOverviews extends Component {
} }
} }
AddListMovieOverviews.propTypes = { DiscoverMovieOverviews.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string, sortKey: PropTypes.string,
overviewOptions: PropTypes.object.isRequired, overviewOptions: PropTypes.object.isRequired,
@ -258,4 +258,4 @@ AddListMovieOverviews.propTypes = {
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };
export default AddListMovieOverviews; export default DiscoverMovieOverviews;

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import AddListMovieOverviews from './AddListMovieOverviews'; import DiscoverMovieOverviews from './DiscoverMovieOverviews';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -22,4 +22,4 @@ function createMapStateToProps() {
); );
} }
export default connect(createMapStateToProps)(AddListMovieOverviews); export default connect(createMapStateToProps)(DiscoverMovieOverviews);

View file

@ -1,133 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } 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 } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const posterSizeOptions = [
{ key: 'small', value: 'Small' },
{ key: 'medium', value: 'Medium' },
{ key: 'large', value: 'Large' }
];
class AddListMovieOverviewOptionsModalContent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
size: props.size,
showStudio: props.showStudio
};
}
componentDidUpdate(prevProps) {
const {
size,
showStudio
} = this.props;
const state = {};
if (size !== prevProps.size) {
state.size = size;
}
if (showStudio !== prevProps.showStudio) {
state.showStudio = showStudio;
}
if (!_.isEmpty(state)) {
this.setState(state);
}
}
//
// Listeners
onChangeOverviewOption = ({ name, value }) => {
this.setState({
[name]: value
}, () => {
this.props.onChangeOverviewOption({ [name]: value });
});
}
//
// Render
render() {
const {
onModalClose
} = this.props;
const {
size,
showStudio
} = this.state;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Overview Options
</ModalHeader>
<ModalBody>
<Form>
<FormGroup>
<FormLabel>{translate('PosterSize')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="size"
value={size}
values={posterSizeOptions}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('ShowStudio')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showStudio"
value={showStudio}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button
onPress={onModalClose}
>
{translate('Close')}
</Button>
</ModalFooter>
</ModalContent>
);
}
}
AddListMovieOverviewOptionsModalContent.propTypes = {
size: PropTypes.string.isRequired,
showStudio: PropTypes.bool.isRequired,
onChangeOverviewOption: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default AddListMovieOverviewOptionsModalContent;

View file

@ -1,15 +1,15 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import AddListMovieOverviewOptionsModalContentConnector from './AddListMovieOverviewOptionsModalContentConnector'; import DiscoverMovieOverviewOptionsModalContentConnector from './DiscoverMovieOverviewOptionsModalContentConnector';
function AddListMovieOverviewOptionsModal({ isOpen, onModalClose, ...otherProps }) { function DiscoverMovieOverviewOptionsModal({ isOpen, onModalClose, ...otherProps }) {
return ( return (
<Modal <Modal
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
> >
<AddListMovieOverviewOptionsModalContentConnector <DiscoverMovieOverviewOptionsModalContentConnector
{...otherProps} {...otherProps}
onModalClose={onModalClose} onModalClose={onModalClose}
/> />
@ -17,9 +17,9 @@ function AddListMovieOverviewOptionsModal({ isOpen, onModalClose, ...otherProps
); );
} }
AddListMovieOverviewOptionsModal.propTypes = { DiscoverMovieOverviewOptionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default AddListMovieOverviewOptionsModal; export default DiscoverMovieOverviewOptionsModal;

View file

@ -0,0 +1,239 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } 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 } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
const posterSizeOptions = [
{ key: 'small', value: 'Small' },
{ key: 'medium', value: 'Medium' },
{ key: 'large', value: 'Large' }
];
class DiscoverMovieOverviewOptionsModalContent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
size: props.size,
showStudio: props.showStudio,
showCertification: props.showCertification,
showRatings: props.showRatings,
showYear: props.showYear,
showGenres: props.showGenres,
includeRecommendations: props.includeRecommendations
};
}
componentDidUpdate(prevProps) {
const {
size,
showStudio,
showYear,
showRatings,
showCertification,
showGenres,
includeRecommendations
} = this.props;
const state = {};
if (size !== prevProps.size) {
state.size = size;
}
if (showStudio !== prevProps.showStudio) {
state.showStudio = showStudio;
}
if (showYear !== prevProps.showYear) {
state.showYear = showYear;
}
if (showRatings !== prevProps.showRatings) {
state.showRatings = showRatings;
}
if (showCertification !== prevProps.showCertification) {
state.showCertification = showCertification;
}
if (showGenres !== prevProps.showGenres) {
state.showGenres = showGenres;
}
if (includeRecommendations !== prevProps.includeRecommendations) {
state.includeRecommendations = includeRecommendations;
}
if (!_.isEmpty(state)) {
this.setState(state);
}
}
//
// Listeners
onChangeOverviewOption = ({ name, value }) => {
this.setState({
[name]: value
}, () => {
this.props.onChangeOverviewOption({ [name]: value });
});
}
onChangeOption = ({ name, value }) => {
this.setState({
[name]: value
}, () => {
this.props.onChangeOption({
[name]: value
});
});
}
//
// Render
render() {
const {
onModalClose
} = this.props;
const {
size,
showStudio,
showCertification,
showRatings,
showYear,
showGenres,
includeRecommendations
} = this.state;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Overview Options
</ModalHeader>
<ModalBody>
<Form>
<FormGroup>
<FormLabel>Include Radarr Recommendations</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeRecommendations"
value={includeRecommendations}
helpText="Include Radarr recommended movies in discovery view"
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('PosterSize')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="size"
value={size}
values={posterSizeOptions}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Genres</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showGenres"
value={showGenres}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('ShowStudio')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showStudio"
value={showStudio}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Year</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showYear"
value={showYear}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Ratings</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showRatings"
value={showRatings}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
<FormGroup>
<FormLabel>Show Certification</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="showCertification"
value={showCertification}
onChange={this.onChangeOverviewOption}
/>
</FormGroup>
</Form>
</ModalBody>
<ModalFooter>
<Button
onPress={onModalClose}
>
{translate('Close')}
</Button>
</ModalFooter>
</ModalContent>
);
}
}
DiscoverMovieOverviewOptionsModalContent.propTypes = {
size: PropTypes.string.isRequired,
showStudio: PropTypes.bool.isRequired,
showYear: PropTypes.bool.isRequired,
showRatings: PropTypes.bool.isRequired,
showCertification: PropTypes.bool.isRequired,
showGenres: PropTypes.bool.isRequired,
includeRecommendations: PropTypes.bool.isRequired,
onChangeOverviewOption: PropTypes.func.isRequired,
onChangeOption: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default DiscoverMovieOverviewOptionsModalContent;

View file

@ -1,13 +1,16 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { setListMovieOverviewOption } from 'Store/Actions/discoverMovieActions'; import { setListMovieOption, setListMovieOverviewOption } from 'Store/Actions/discoverMovieActions';
import AddListMovieOverviewOptionsModalContent from './AddListMovieOverviewOptionsModalContent'; import DiscoverMovieOverviewOptionsModalContent from './DiscoverMovieOverviewOptionsModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.discoverMovie, (state) => state.discoverMovie,
(discoverMovie) => { (discoverMovie) => {
return discoverMovie.overviewOptions; return {
...discoverMovie.options,
...discoverMovie.overviewOptions
};
} }
); );
} }
@ -16,8 +19,11 @@ function createMapDispatchToProps(dispatch, props) {
return { return {
onChangeOverviewOption(payload) { onChangeOverviewOption(payload) {
dispatch(setListMovieOverviewOption(payload)); dispatch(setListMovieOverviewOption(payload));
},
onChangeOption(payload) {
dispatch(setListMovieOption(payload));
} }
}; };
} }
export default connect(createMapStateToProps, createMapDispatchToProps)(AddListMovieOverviewOptionsModalContent); export default connect(createMapStateToProps, createMapDispatchToProps)(DiscoverMovieOverviewOptionsModalContent);

View file

@ -1,30 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import styles from './AddListMoviePosterInfo.css';
function AddListMoviePosterInfo(props) {
const {
studio,
sortKey
} = props;
if (sortKey === 'studio' && studio) {
return (
<div className={styles.info}>
{studio}
</div>
);
}
return null;
}
AddListMoviePosterInfo.propTypes = {
studio: PropTypes.string,
sortKey: PropTypes.string.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired
};
export default AddListMoviePosterInfo;

View file

@ -12,9 +12,10 @@ import { icons } from 'Helpers/Props';
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks'; import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
import MoviePoster from 'Movie/MoviePoster'; import MoviePoster from 'Movie/MoviePoster';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './AddListMoviePoster.css'; import DiscoverMoviePosterInfo from './DiscoverMoviePosterInfo';
import styles from './DiscoverMoviePoster.css';
class AddListMoviePoster extends Component { class DiscoverMoviePoster extends Component {
// //
// Lifecycle // Lifecycle
@ -81,14 +82,17 @@ class AddListMoviePoster extends Component {
year, year,
overview, overview,
folder, folder,
titleSlug,
images, images,
posterWidth, posterWidth,
posterHeight, posterHeight,
showTitle, showTitle,
isExisting, isExisting,
isExcluded, isExcluded,
isSelected isSelected,
showRelativeDates,
shortDateFormat,
timeFormat,
...otherProps
} = this.props; } = this.props;
const { const {
@ -97,7 +101,7 @@ class AddListMoviePoster extends Component {
isExcludeMovieModalOpen isExcludeMovieModalOpen
} = this.state; } = this.state;
const linkProps = isExisting ? { to: `/movie/${titleSlug}` } : { onPress: this.onPress }; const linkProps = isExisting ? { to: `/movie/${tmdbId}` } : { onPress: this.onPress };
const elementStyle = { const elementStyle = {
width: `${posterWidth}px`, width: `${posterWidth}px`,
@ -186,6 +190,13 @@ class AddListMoviePoster extends Component {
</div> </div>
} }
<DiscoverMoviePosterInfo
showRelativeDates={showRelativeDates}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
{...otherProps}
/>
<AddNewDiscoverMovieModal <AddNewDiscoverMovieModal
isOpen={isNewAddMovieModalOpen && !isExisting} isOpen={isNewAddMovieModalOpen && !isExisting}
tmdbId={tmdbId} tmdbId={tmdbId}
@ -209,7 +220,7 @@ class AddListMoviePoster extends Component {
} }
} }
AddListMoviePoster.propTypes = { DiscoverMoviePoster.propTypes = {
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
imdbId: PropTypes.string, imdbId: PropTypes.string,
youTubeTrailerId: PropTypes.string, youTubeTrailerId: PropTypes.string,
@ -218,7 +229,6 @@ AddListMoviePoster.propTypes = {
overview: PropTypes.string.isRequired, overview: PropTypes.string.isRequired,
folder: PropTypes.string.isRequired, folder: PropTypes.string.isRequired,
status: PropTypes.string.isRequired, status: PropTypes.string.isRequired,
titleSlug: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired, images: PropTypes.arrayOf(PropTypes.object).isRequired,
posterWidth: PropTypes.number.isRequired, posterWidth: PropTypes.number.isRequired,
posterHeight: PropTypes.number.isRequired, posterHeight: PropTypes.number.isRequired,
@ -232,4 +242,4 @@ AddListMoviePoster.propTypes = {
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };
export default AddListMoviePoster; export default DiscoverMoviePoster;

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import AddListMoviePoster from './AddListMoviePoster'; import DiscoverMoviePoster from './DiscoverMoviePoster';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -14,4 +14,4 @@ function createMapStateToProps() {
); );
} }
export default connect(createMapStateToProps)(AddListMoviePoster); export default connect(createMapStateToProps)(DiscoverMoviePoster);

View file

@ -0,0 +1,139 @@
import PropTypes from 'prop-types';
import React from 'react';
import HeartRating from 'Components/HeartRating';
import { getMovieStatusDetails } from 'Movie/MovieStatus';
import formatRuntime from 'Utilities/Date/formatRuntime';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import styles from './DiscoverMoviePosterInfo.css';
function DiscoverMoviePosterInfo(props) {
const {
status,
studio,
inCinemas,
digitalRelease,
physicalRelease,
certification,
runtime,
ratings,
sortKey,
showRelativeDates,
shortDateFormat,
timeFormat
} = props;
if (sortKey === 'status' && status) {
return (
<div className={styles.info}>
{getMovieStatusDetails(status).title}
</div>
);
}
if (sortKey === 'studio' && studio) {
return (
<div className={styles.info}>
{studio}
</div>
);
}
if (sortKey === 'inCinemas' && inCinemas) {
const inCinemasDate = getRelativeDate(
inCinemas,
shortDateFormat,
showRelativeDates,
{
timeFormat,
timeForToday: false
}
);
return (
<div className={styles.info}>
{`In Cinemas ${inCinemasDate}`}
</div>
);
}
if (sortKey === 'digitalRelease' && digitalRelease) {
const digitalReleaseDate = getRelativeDate(
digitalRelease,
shortDateFormat,
showRelativeDates,
{
timeFormat,
timeForToday: false
}
);
return (
<div className={styles.info}>
{`Digital ${digitalReleaseDate}`}
</div>
);
}
if (sortKey === 'physicalRelease' && physicalRelease) {
const physicalReleaseDate = getRelativeDate(
physicalRelease,
shortDateFormat,
showRelativeDates,
{
timeFormat,
timeForToday: false
}
);
return (
<div className={styles.info}>
{`Released ${physicalReleaseDate}`}
</div>
);
}
if (sortKey === 'certification' && certification) {
return (
<div className={styles.info}>
{certification}
</div>
);
}
if (sortKey === 'runtime' && runtime) {
return (
<div className={styles.info}>
{formatRuntime(runtime)}
</div>
);
}
if (sortKey === 'ratings' && ratings) {
return (
<div className={styles.info}>
<HeartRating
rating={ratings.value}
/>
</div>
);
}
return null;
}
DiscoverMoviePosterInfo.propTypes = {
status: PropTypes.string,
studio: PropTypes.string,
inCinemas: PropTypes.string,
certification: PropTypes.string,
digitalRelease: PropTypes.string,
physicalRelease: PropTypes.string,
runtime: PropTypes.number,
ratings: PropTypes.object,
sortKey: PropTypes.string.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired
};
export default DiscoverMoviePosterInfo;

View file

@ -2,12 +2,12 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Grid, WindowScroller } from 'react-virtualized'; import { Grid, WindowScroller } from 'react-virtualized';
import Measure from 'Components/Measure'; import Measure from 'Components/Measure';
import AddListMovieItemConnector from 'DiscoverMovie/AddListMovieItemConnector'; import DiscoverMovieItemConnector from 'DiscoverMovie/DiscoverMovieItemConnector';
import dimensions from 'Styles/Variables/dimensions'; import dimensions from 'Styles/Variables/dimensions';
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter'; import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder'; import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import AddListMoviePosterConnector from './AddListMoviePosterConnector'; import DiscoverMoviePosterConnector from './DiscoverMoviePosterConnector';
import styles from './AddListMoviePosters.css'; import styles from './DiscoverMoviePosters.css';
// Poster container dimensions // Poster container dimensions
const columnPadding = parseInt(dimensions.movieIndexColumnPadding); const columnPadding = parseInt(dimensions.movieIndexColumnPadding);
@ -65,7 +65,7 @@ function calculatePosterHeight(posterWidth) {
return Math.ceil((250 / 170) * posterWidth); return Math.ceil((250 / 170) * posterWidth);
} }
class AddListMoviePosters extends Component { class DiscoverMoviePosters extends Component {
// //
// Lifecycle // Lifecycle
@ -201,9 +201,9 @@ class AddListMoviePosters extends Component {
padding: this._padding padding: this._padding
}} }}
> >
<AddListMovieItemConnector <DiscoverMovieItemConnector
key={movie.tmdbId} key={movie.tmdbId}
component={AddListMoviePosterConnector} component={DiscoverMoviePosterConnector}
sortKey={sortKey} sortKey={sortKey}
posterWidth={posterWidth} posterWidth={posterWidth}
posterHeight={posterHeight} posterHeight={posterHeight}
@ -289,7 +289,7 @@ class AddListMoviePosters extends Component {
} }
} }
AddListMoviePosters.propTypes = { DiscoverMoviePosters.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string, sortKey: PropTypes.string,
posterOptions: PropTypes.object.isRequired, posterOptions: PropTypes.object.isRequired,
@ -303,4 +303,4 @@ AddListMoviePosters.propTypes = {
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };
export default AddListMoviePosters; export default DiscoverMoviePosters;

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import AddListMoviePosters from './AddListMoviePosters'; import DiscoverMoviePosters from './DiscoverMoviePosters';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -21,4 +21,4 @@ function createMapStateToProps() {
); );
} }
export default connect(createMapStateToProps)(AddListMoviePosters); export default connect(createMapStateToProps)(DiscoverMoviePosters);

View file

@ -1,15 +1,15 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import AddListMoviePosterOptionsModalContentConnector from './AddListMoviePosterOptionsModalContentConnector'; import DiscoverMoviePosterOptionsModalContentConnector from './DiscoverMoviePosterOptionsModalContentConnector';
function AddListMoviePosterOptionsModal({ isOpen, onModalClose, ...otherProps }) { function DiscoverMoviePosterOptionsModal({ isOpen, onModalClose, ...otherProps }) {
return ( return (
<Modal <Modal
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
> >
<AddListMoviePosterOptionsModalContentConnector <DiscoverMoviePosterOptionsModalContentConnector
{...otherProps} {...otherProps}
onModalClose={onModalClose} onModalClose={onModalClose}
/> />
@ -17,9 +17,9 @@ function AddListMoviePosterOptionsModal({ isOpen, onModalClose, ...otherProps })
); );
} }
AddListMoviePosterOptionsModal.propTypes = { DiscoverMoviePosterOptionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default AddListMoviePosterOptionsModal; export default DiscoverMoviePosterOptionsModal;

View file

@ -19,7 +19,7 @@ const posterSizeOptions = [
{ key: 'large', value: 'Large' } { key: 'large', value: 'Large' }
]; ];
class AddListMoviePosterOptionsModalContent extends Component { class DiscoverMoviePosterOptionsModalContent extends Component {
// //
// Lifecycle // Lifecycle
@ -29,14 +29,16 @@ class AddListMoviePosterOptionsModalContent extends Component {
this.state = { this.state = {
size: props.size, size: props.size,
showTitle: props.showTitle showTitle: props.showTitle,
includeRecommendations: props.includeRecommendations
}; };
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { const {
size, size,
showTitle showTitle,
includeRecommendations
} = this.props; } = this.props;
const state = {}; const state = {};
@ -49,6 +51,10 @@ class AddListMoviePosterOptionsModalContent extends Component {
state.showTitle = showTitle; state.showTitle = showTitle;
} }
if (includeRecommendations !== prevProps.includeRecommendations) {
state.includeRecommendations = includeRecommendations;
}
if (!_.isEmpty(state)) { if (!_.isEmpty(state)) {
this.setState(state); this.setState(state);
} }
@ -65,6 +71,16 @@ class AddListMoviePosterOptionsModalContent extends Component {
}); });
} }
onChangeOption = ({ name, value }) => {
this.setState({
[name]: value
}, () => {
this.props.onChangeOption({
[name]: value
});
});
}
// //
// Render // Render
@ -75,7 +91,8 @@ class AddListMoviePosterOptionsModalContent extends Component {
const { const {
size, size,
showTitle showTitle,
includeRecommendations
} = this.state; } = this.state;
return ( return (
@ -86,6 +103,18 @@ class AddListMoviePosterOptionsModalContent extends Component {
<ModalBody> <ModalBody>
<Form> <Form>
<FormGroup>
<FormLabel>Include Radarr Recommendations</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeRecommendations"
value={includeRecommendations}
helpText="Include Radarr recommended movies in discovery view"
onChange={this.onChangeOption}
/>
</FormGroup>
<FormGroup> <FormGroup>
<FormLabel>{translate('PosterSize')}</FormLabel> <FormLabel>{translate('PosterSize')}</FormLabel>
@ -124,11 +153,13 @@ class AddListMoviePosterOptionsModalContent extends Component {
} }
} }
AddListMoviePosterOptionsModalContent.propTypes = { DiscoverMoviePosterOptionsModalContent.propTypes = {
size: PropTypes.string.isRequired, size: PropTypes.string.isRequired,
showTitle: PropTypes.bool.isRequired, showTitle: PropTypes.bool.isRequired,
includeRecommendations: PropTypes.bool.isRequired,
onChangePosterOption: PropTypes.func.isRequired, onChangePosterOption: PropTypes.func.isRequired,
onChangeOption: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default AddListMoviePosterOptionsModalContent; export default DiscoverMoviePosterOptionsModalContent;

View file

@ -1,13 +1,16 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { setListMoviePosterOption } from 'Store/Actions/discoverMovieActions'; import { setListMovieOption, setListMoviePosterOption } from 'Store/Actions/discoverMovieActions';
import AddListMoviePosterOptionsModalContent from './AddListMoviePosterOptionsModalContent'; import DiscoverMoviePosterOptionsModalContent from './DiscoverMoviePosterOptionsModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.discoverMovie, (state) => state.discoverMovie,
(discoverMovie) => { (discoverMovie) => {
return discoverMovie.posterOptions; return {
...discoverMovie.options,
...discoverMovie.posterOptions
};
} }
); );
} }
@ -16,8 +19,11 @@ function createMapDispatchToProps(dispatch, props) {
return { return {
onChangePosterOption(payload) { onChangePosterOption(payload) {
dispatch(setListMoviePosterOption(payload)); dispatch(setListMoviePosterOption(payload));
},
onChangeOption(payload) {
dispatch(setListMovieOption(payload));
} }
}; };
} }
export default connect(createMapStateToProps, createMapDispatchToProps)(AddListMoviePosterOptionsModalContent); export default connect(createMapStateToProps, createMapDispatchToProps)(DiscoverMoviePosterOptionsModalContent);

View file

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell'; import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
class AddListMovieActionsCell extends Component { class DiscoverMovieActionsCell extends Component {
// //
// Render // Render
@ -47,8 +47,8 @@ class AddListMovieActionsCell extends Component {
} }
} }
AddListMovieActionsCell.propTypes = { DiscoverMovieActionsCell.propTypes = {
id: PropTypes.number.isRequired id: PropTypes.number.isRequired
}; };
export default AddListMovieActionsCell; export default DiscoverMovieActionsCell;

View file

@ -1,9 +1,10 @@
.status { .status {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 60px; flex: 0 0 30px;
} }
.collection,
.sortTitle { .sortTitle {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
@ -18,23 +19,36 @@
.inCinemas, .inCinemas,
.physicalRelease, .physicalRelease,
.digitalRelease,
.genres { .genres {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 180px; flex: 0 0 180px;
} }
.movieStatus, .ratings,
.runtime {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 90px;
}
.lists {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 1 0 110px;
}
.certification { .certification {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 100px; flex: 0 0 115px;
} }
.ratings { .isRecommendation {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 80px; flex: 0 0 50px;
} }
.actions { .actions {

View file

@ -1,14 +1,16 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Icon from 'Components/Icon';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import TableOptionsModal from 'Components/Table/TableOptions/TableOptionsModal'; import TableOptionsModal from 'Components/Table/TableOptions/TableOptionsModal';
import VirtualTableHeader from 'Components/Table/VirtualTableHeader'; import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell'; import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell'; import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import styles from './AddListMovieHeader.css'; import DiscoverMovieTableOptionsConnector from './DiscoverMovieTableOptionsConnector';
import styles from './DiscoverMovieHeader.css';
class AddListMovieHeader extends Component { class DiscoverMovieHeader extends Component {
// //
// Lifecycle // Lifecycle
@ -84,6 +86,23 @@ class AddListMovieHeader extends Component {
); );
} }
if (name === 'isRecommendation') {
return (
<VirtualTableHeaderCell
key={name}
className={styles[name]}
name={name}
isSortable={true}
{...otherProps}
>
<Icon
name={icons.RECOMMENDED}
size={12}
/>
</VirtualTableHeaderCell>
);
}
return ( return (
<VirtualTableHeaderCell <VirtualTableHeaderCell
key={name} key={name}
@ -102,6 +121,7 @@ class AddListMovieHeader extends Component {
isOpen={this.state.isTableOptionsModalOpen} isOpen={this.state.isTableOptionsModalOpen}
columns={columns} columns={columns}
onTableOptionChange={onTableOptionChange} onTableOptionChange={onTableOptionChange}
optionsComponent={DiscoverMovieTableOptionsConnector}
onModalClose={this.onTableOptionsModalClose} onModalClose={this.onTableOptionsModalClose}
/> />
</VirtualTableHeader> </VirtualTableHeader>
@ -109,7 +129,7 @@ class AddListMovieHeader extends Component {
} }
} }
AddListMovieHeader.propTypes = { DiscoverMovieHeader.propTypes = {
columns: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onTableOptionChange: PropTypes.func.isRequired, onTableOptionChange: PropTypes.func.isRequired,
allSelected: PropTypes.bool.isRequired, allSelected: PropTypes.bool.isRequired,
@ -117,4 +137,4 @@ AddListMovieHeader.propTypes = {
onSelectAllChange: PropTypes.func.isRequired onSelectAllChange: PropTypes.func.isRequired
}; };
export default AddListMovieHeader; export default DiscoverMovieHeader;

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { setListMovieTableOption } from 'Store/Actions/discoverMovieActions'; import { setListMovieTableOption } from 'Store/Actions/discoverMovieActions';
import AddListMovieHeader from './AddListMovieHeader'; import DiscoverMovieHeader from './DiscoverMovieHeader';
function createMapDispatchToProps(dispatch, props) { function createMapDispatchToProps(dispatch, props) {
return { return {
@ -10,4 +10,4 @@ function createMapDispatchToProps(dispatch, props) {
}; };
} }
export default connect(undefined, createMapDispatchToProps)(AddListMovieHeader); export default connect(undefined, createMapDispatchToProps)(DiscoverMovieHeader);

View file

@ -8,7 +8,19 @@
.status { .status {
composes: cell; composes: cell;
flex: 0 0 60px; flex: 0 0 30px;
}
.alreadyExistsIcon {
width: 20px !important;
color: #37bc9b;
pointer-events: all;
}
.exclusionIcon {
width: 20px !important;
color: $dangerColor;
pointer-events: all;
} }
.collection, .collection,
@ -26,29 +38,36 @@
.inCinemas, .inCinemas,
.physicalRelease, .physicalRelease,
.digitalRelease,
.genres { .genres {
composes: cell; composes: cell;
flex: 0 0 180px; flex: 0 0 180px;
} }
.movieStatus,
.certification { .certification {
composes: cell; composes: cell;
flex: 0 0 100px; flex: 0 0 115px;
} }
.ratings { .ratings,
.runtime {
composes: cell; composes: cell;
flex: 0 0 80px; flex: 0 0 90px;
} }
.tags { .lists {
composes: cell; composes: cell;
flex: 1 0 60px; flex: 1 0 110px;
}
.isRecommendation {
composes: cell;
flex: 0 0 50px;
} }
.actions { .actions {

View file

@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import HeartRating from 'Components/HeartRating'; import HeartRating from 'Components/HeartRating';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import ImportListListConnector from 'Components/ImportListListConnector';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
@ -12,11 +13,12 @@ import AddNewDiscoverMovieModal from 'DiscoverMovie/AddNewDiscoverMovieModal';
import ExcludeMovieModal from 'DiscoverMovie/Exclusion/ExcludeMovieModal'; import ExcludeMovieModal from 'DiscoverMovie/Exclusion/ExcludeMovieModal';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks'; import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks';
import formatRuntime from 'Utilities/Date/formatRuntime';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import ListMovieStatusCell from './ListMovieStatusCell'; import ListMovieStatusCell from './ListMovieStatusCell';
import styles from './AddListMovieRow.css'; import styles from './DiscoverMovieRow.css';
class AddListMovieRow extends Component { class DiscoverMovieRow extends Component {
// //
// Lifecycle // Lifecycle
@ -59,10 +61,11 @@ class AddListMovieRow extends Component {
imdbId, imdbId,
youTubeTrailerId, youTubeTrailerId,
title, title,
titleSlug,
studio, studio,
inCinemas, inCinemas,
physicalRelease, physicalRelease,
digitalRelease,
runtime,
year, year,
overview, overview,
folder, folder,
@ -70,10 +73,13 @@ class AddListMovieRow extends Component {
genres, genres,
ratings, ratings,
certification, certification,
collection,
columns, columns,
isExisting, isExisting,
isExcluded, isExcluded,
isRecommendation,
isSelected, isSelected,
lists,
onSelectedChange onSelectedChange
} = this.props; } = this.props;
@ -82,7 +88,7 @@ class AddListMovieRow extends Component {
isExcludeMovieModalOpen isExcludeMovieModalOpen
} = this.state; } = this.state;
const linkProps = isExisting ? { to: `/movie/${titleSlug}` } : { onPress: this.onAddMoviePress }; const linkProps = isExisting ? { to: `/movie/${tmdbId}` } : { onPress: this.onAddMoviePress };
return ( return (
<> <>
@ -113,6 +119,7 @@ class AddListMovieRow extends Component {
className={styles[name]} className={styles[name]}
status={status} status={status}
isExclusion={isExcluded} isExclusion={isExcluded}
isExisting={isExisting}
component={VirtualTableRowCell} component={VirtualTableRowCell}
/> />
); );
@ -129,6 +136,35 @@ class AddListMovieRow extends Component {
> >
{title} {title}
</Link> </Link>
{
isExisting ?
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
title={'Already in your Library'}
/> : null
}
{
isExcluded ?
<Icon
className={styles.exclusionIcon}
name={icons.DANGER}
title={'Movie Excluded From Automatic Add'}
/> : null
}
</VirtualTableRowCell>
);
}
if (name === 'collection') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{collection ? collection.name : null }
</VirtualTableRowCell> </VirtualTableRowCell>
); );
} }
@ -166,6 +202,28 @@ class AddListMovieRow extends Component {
); );
} }
if (name === 'digitalRelease') {
return (
<RelativeDateCellConnector
key={name}
className={styles[name]}
date={digitalRelease}
component={VirtualTableRowCell}
/>
);
}
if (name === 'runtime') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{formatRuntime(runtime)}
</VirtualTableRowCell>
);
}
if (name === 'genres') { if (name === 'genres') {
const joinedGenres = genres.join(', '); const joinedGenres = genres.join(', ');
@ -205,6 +263,38 @@ class AddListMovieRow extends Component {
); );
} }
if (name === 'lists') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
<ImportListListConnector
lists={lists}
/>
</VirtualTableRowCell>
);
}
if (name === 'isRecommendation') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{
isRecommendation ?
<Icon
name={icons.RECOMMENDED}
size={12}
title='Recommended'
/> :
null
}
</VirtualTableRowCell>
);
}
if (name === 'actions') { if (name === 'actions') {
return ( return (
<VirtualTableRowCell <VirtualTableRowCell
@ -267,7 +357,7 @@ class AddListMovieRow extends Component {
} }
} }
AddListMovieRow.propTypes = { DiscoverMovieRow.propTypes = {
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
imdbId: PropTypes.string, imdbId: PropTypes.string,
youTubeTrailerId: PropTypes.string, youTubeTrailerId: PropTypes.string,
@ -277,23 +367,27 @@ AddListMovieRow.propTypes = {
overview: PropTypes.string.isRequired, overview: PropTypes.string.isRequired,
folder: PropTypes.string.isRequired, folder: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired, images: PropTypes.arrayOf(PropTypes.object).isRequired,
titleSlug: PropTypes.string.isRequired,
studio: PropTypes.string, studio: PropTypes.string,
inCinemas: PropTypes.string, inCinemas: PropTypes.string,
physicalRelease: PropTypes.string, physicalRelease: PropTypes.string,
digitalRelease: PropTypes.string,
runtime: PropTypes.number,
genres: PropTypes.arrayOf(PropTypes.string).isRequired, genres: PropTypes.arrayOf(PropTypes.string).isRequired,
ratings: PropTypes.object.isRequired, ratings: PropTypes.object.isRequired,
certification: PropTypes.string, certification: PropTypes.string,
collection: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired,
isExisting: PropTypes.bool.isRequired, isExisting: PropTypes.bool.isRequired,
isExcluded: PropTypes.bool.isRequired, isExcluded: PropTypes.bool.isRequired,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
isRecommendation: PropTypes.bool.isRequired,
lists: PropTypes.arrayOf(PropTypes.number).isRequired,
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };
AddListMovieRow.defaultProps = { DiscoverMovieRow.defaultProps = {
genres: [], genres: [],
tags: [] lists: []
}; };
export default AddListMovieRow; export default DiscoverMovieRow;

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import AddListMovieRow from './AddListMovieRow'; import DiscoverMovieRow from './DiscoverMovieRow';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -14,4 +14,4 @@ function createMapStateToProps() {
); );
} }
export default connect(createMapStateToProps)(AddListMovieRow); export default connect(createMapStateToProps)(DiscoverMovieRow);

View file

@ -2,14 +2,14 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import VirtualTable from 'Components/Table/VirtualTable'; import VirtualTable from 'Components/Table/VirtualTable';
import VirtualTableRow from 'Components/Table/VirtualTableRow'; import VirtualTableRow from 'Components/Table/VirtualTableRow';
import AddListMovieItemConnector from 'DiscoverMovie/AddListMovieItemConnector'; import DiscoverMovieItemConnector from 'DiscoverMovie/DiscoverMovieItemConnector';
import { sortDirections } from 'Helpers/Props'; import { sortDirections } from 'Helpers/Props';
import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter'; import getIndexOfFirstCharacter from 'Utilities/Array/getIndexOfFirstCharacter';
import AddListMovieHeaderConnector from './AddListMovieHeaderConnector'; import DiscoverMovieHeaderConnector from './DiscoverMovieHeaderConnector';
import AddListMovieRowConnector from './AddListMovieRowConnector'; import DiscoverMovieRowConnector from './DiscoverMovieRowConnector';
import styles from './AddListMovieTable.css'; import styles from './DiscoverMovieTable.css';
class AddListMovieTable extends Component { class DiscoverMovieTable extends Component {
// //
// Lifecycle // Lifecycle
@ -58,9 +58,9 @@ class AddListMovieTable extends Component {
key={key} key={key}
style={style} style={style}
> >
<AddListMovieItemConnector <DiscoverMovieItemConnector
key={movie.tmdbId} key={movie.tmdbId}
component={AddListMovieRowConnector} component={DiscoverMovieRowConnector}
columns={columns} columns={columns}
movieId={movie.tmdbId} movieId={movie.tmdbId}
isSelected={selectedState[movie.tmdbId]} isSelected={selectedState[movie.tmdbId]}
@ -99,7 +99,7 @@ class AddListMovieTable extends Component {
overscanRowCount={2} overscanRowCount={2}
rowRenderer={this.rowRenderer} rowRenderer={this.rowRenderer}
header={ header={
<AddListMovieHeaderConnector <DiscoverMovieHeaderConnector
columns={columns} columns={columns}
sortKey={sortKey} sortKey={sortKey}
sortDirection={sortDirection} sortDirection={sortDirection}
@ -116,7 +116,7 @@ class AddListMovieTable extends Component {
} }
} }
AddListMovieTable.propTypes = { DiscoverMovieTable.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string, sortKey: PropTypes.string,
@ -132,4 +132,4 @@ AddListMovieTable.propTypes = {
onSelectAllChange: PropTypes.func.isRequired onSelectAllChange: PropTypes.func.isRequired
}; };
export default AddListMovieTable; export default DiscoverMovieTable;

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { setListMovieSort } from 'Store/Actions/discoverMovieActions'; import { setListMovieSort } from 'Store/Actions/discoverMovieActions';
import AddListMovieTable from './AddListMovieTable'; import DiscoverMovieTable from './DiscoverMovieTable';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -24,4 +24,4 @@ function createMapDispatchToProps(dispatch, props) {
}; };
} }
export default connect(createMapStateToProps, createMapDispatchToProps)(AddListMovieTable); export default connect(createMapStateToProps, createMapDispatchToProps)(DiscoverMovieTable);

View file

@ -0,0 +1,73 @@
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 { inputTypes } from 'Helpers/Props';
class DiscoverMovieTableOptions extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
includeRecommendations: props.includeRecommendations
};
}
componentDidUpdate(prevProps) {
const { includeRecommendations } = this.props;
if (includeRecommendations !== prevProps.includeRecommendations) {
this.setState({
includeRecommendations
});
}
}
//
// Listeners
onChangeOption = ({ name, value }) => {
this.setState({
[name]: value
}, () => {
this.props.onChangeOption({
[name]: value
});
});
}
//
// Render
render() {
const {
includeRecommendations
} = this.state;
return (
<FormGroup>
<FormLabel>Include Radarr Recommendations</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeRecommendations"
value={includeRecommendations}
helpText="Include Radarr recommended movies in discovery view"
onChange={this.onChangeOption}
/>
</FormGroup>
);
}
}
DiscoverMovieTableOptions.propTypes = {
includeRecommendations: PropTypes.bool.isRequired,
onChangeOption: PropTypes.func.isRequired
};
export default DiscoverMovieTableOptions;

View file

@ -0,0 +1,24 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { setListMovieOption } from 'Store/Actions/discoverMovieActions';
import DiscoverMovieTableOptions from './DiscoverMovieTableOptions';
function createMapStateToProps() {
return createSelector(
(state) => state.discoverMovie,
(discoverMovie) => {
return discoverMovie.options;
}
);
}
function createMapDispatchToProps(dispatch, props) {
return {
onChangeOption(payload) {
dispatch(setListMovieOption(payload));
}
};
}
export default connect(createMapStateToProps, createMapDispatchToProps)(DiscoverMovieTableOptions);

View file

@ -1,13 +1,3 @@
.status {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 60px;
}
.statusIcon { .statusIcon {
width: 20px !important; width: 20px !important;
} }
.exclusionIcon {
color: $dangerColor;
}

View file

@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Icon from 'Components/Icon'; import Icon from 'Components/Icon';
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell'; import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
import { icons } from 'Helpers/Props';
import { getMovieStatusDetails } from 'Movie/MovieStatus'; import { getMovieStatusDetails } from 'Movie/MovieStatus';
import styles from './ListMovieStatusCell.css'; import styles from './ListMovieStatusCell.css';
@ -10,7 +9,6 @@ function ListMovieStatusCell(props) {
const { const {
className, className,
status, status,
isExclusion,
component: Component, component: Component,
...otherProps ...otherProps
} = props; } = props;
@ -28,15 +26,6 @@ function ListMovieStatusCell(props) {
title={`${statusDetails.title}: ${statusDetails.message}`} title={`${statusDetails.title}: ${statusDetails.message}`}
/> />
{
isExclusion ?
<Icon
className={styles.exclusionIcon}
name={icons.DANGER}
title={'Movie Excluded From Automatic Add'}
/> : null
}
</Component> </Component>
); );
} }
@ -44,7 +33,6 @@ function ListMovieStatusCell(props) {
ListMovieStatusCell.propTypes = { ListMovieStatusCell.propTypes = {
className: PropTypes.string.isRequired, className: PropTypes.string.isRequired,
status: PropTypes.string.isRequired, status: PropTypes.string.isRequired,
isExclusion: PropTypes.bool.isRequired,
component: PropTypes.elementType component: PropTypes.elementType
}; };

View file

@ -8,3 +8,4 @@ export const QUALITY = 'quality';
export const QUALITY_PROFILE = 'qualityProfile'; export const QUALITY_PROFILE = 'qualityProfile';
export const MOVIE_STATUS = 'movieStatus'; export const MOVIE_STATUS = 'movieStatus';
export const TAG = 'tag'; export const TAG = 'tag';
export const IMPORTLIST = 'importList';

View file

@ -29,6 +29,7 @@ import {
faBookReader as fasBookReader, faBookReader as fasBookReader,
faBroadcastTower as fasBroadcastTower, faBroadcastTower as fasBroadcastTower,
faBug as fasBug, faBug as fasBug,
faBuilding as fasBuilding,
faBullhorn as fasBullhorn, faBullhorn as fasBullhorn,
faCalendarAlt as fasCalendarAlt, faCalendarAlt as fasCalendarAlt,
faCaretDown as fasCaretDown, faCaretDown as fasCaretDown,
@ -92,7 +93,9 @@ import {
faTable as fasTable, faTable as fasTable,
faTags as fasTags, faTags as fasTags,
faTh as fasTh, faTh as fasTh,
faTheaterMasks as fasTheaterMasks,
faThList as fasThList, faThList as fasThList,
faThumbsUp as fasThumbsUp,
faTicketAlt as fasTicketAlt, faTicketAlt as fasTicketAlt,
faTimes as fasTimes, faTimes as fasTimes,
faTimesCircle as fasTimesCircle, faTimesCircle as fasTimesCircle,
@ -145,10 +148,12 @@ export const EXPAND_INDETERMINATE = fasChevronCircleRight;
export const EXTERNAL_LINK = fasExternalLinkAlt; export const EXTERNAL_LINK = fasExternalLinkAlt;
export const FATAL = fasTimesCircle; export const FATAL = fasTimesCircle;
export const FILE = farFile; export const FILE = farFile;
export const FILM = fasFilm;
export const FILTER = fasFilter; export const FILTER = fasFilter;
export const FLAG = fasFlag; export const FLAG = fasFlag;
export const FOLDER = farFolder; export const FOLDER = farFolder;
export const FOLDER_OPEN = fasFolderOpen; export const FOLDER_OPEN = fasFolderOpen;
export const GENRE = fasTheaterMasks;
export const GROUP = farObjectGroup; export const GROUP = farObjectGroup;
export const HEALTH = fasMedkit; export const HEALTH = fasMedkit;
export const HEART = fasHeart; export const HEART = fasHeart;
@ -180,6 +185,7 @@ export const PROFILE = fasUser;
export const POSTER = fasTh; export const POSTER = fasTh;
export const QUEUED = fasCloud; export const QUEUED = fasCloud;
export const QUICK = fasRocket; export const QUICK = fasRocket;
export const RECOMMENDED = fasThumbsUp;
export const REFRESH = fasSync; export const REFRESH = fasSync;
export const REMOVE = fasTimes; export const REMOVE = fasTimes;
export const RESTART = fasRedoAlt; export const RESTART = fasRedoAlt;
@ -199,7 +205,7 @@ export const SORT = fasSort;
export const SORT_ASCENDING = fasSortUp; export const SORT_ASCENDING = fasSortUp;
export const SORT_DESCENDING = fasSortDown; export const SORT_DESCENDING = fasSortDown;
export const SPINNER = fasSpinner; export const SPINNER = fasSpinner;
export const STUDIO = fasFilm; export const STUDIO = fasBuilding;
export const SUBTRACT = fasMinus; export const SUBTRACT = fasMinus;
export const SYSTEM = fasLaptop; export const SYSTEM = fasLaptop;
export const TABLE = fasTable; export const TABLE = fasTable;

View file

@ -24,7 +24,7 @@ class DeleteMovieModalContent extends Component {
this.state = { this.state = {
deleteFiles: false, deleteFiles: false,
addNetImportExclusion: false addImportExclusion: false
}; };
} }
@ -35,16 +35,16 @@ class DeleteMovieModalContent extends Component {
this.setState({ deleteFiles: value }); this.setState({ deleteFiles: value });
} }
onAddNetImportExclusionChange = ({ value }) => { onAddImportExclusionChange = ({ value }) => {
this.setState({ addNetImportExclusion: value }); this.setState({ addImportExclusion: value });
} }
onDeleteMovieConfirmed = () => { onDeleteMovieConfirmed = () => {
const deleteFiles = this.state.deleteFiles; const deleteFiles = this.state.deleteFiles;
const addNetImportExclusion = this.state.addNetImportExclusion; const addImportExclusion = this.state.addImportExclusion;
this.setState({ deleteFiles: false, addNetImportExclusion: false }); this.setState({ deleteFiles: false, addImportExclusion: false });
this.props.onDeletePress(deleteFiles, addNetImportExclusion); this.props.onDeletePress(deleteFiles, addImportExclusion);
} }
// //
@ -64,7 +64,7 @@ class DeleteMovieModalContent extends Component {
} = statistics; } = statistics;
const deleteFiles = this.state.deleteFiles; const deleteFiles = this.state.deleteFiles;
const addNetImportExclusion = this.state.addNetImportExclusion; const addImportExclusion = this.state.addImportExclusion;
let deleteFilesLabel = `Delete ${movieFileCount} Movie Files`; let deleteFilesLabel = `Delete ${movieFileCount} Movie Files`;
let deleteFilesHelpText = 'Delete the movie files and movie folder'; let deleteFilesHelpText = 'Delete the movie files and movie folder';
@ -122,11 +122,11 @@ class DeleteMovieModalContent extends Component {
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="addNetImportExclusion" name="addImportExclusion"
value={addNetImportExclusion} value={addImportExclusion}
helpText="Prevent movie from being added to Radarr by lists" helpText="Prevent movie from being added to Radarr by lists"
kind={kinds.DANGER} kind={kinds.DANGER}
onChange={this.onAddNetImportExclusionChange} onChange={this.onAddImportExclusionChange}
/> />
</FormGroup> </FormGroup>

View file

@ -24,11 +24,11 @@ class DeleteMovieModalContentConnector extends Component {
// //
// Listeners // Listeners
onDeletePress = (deleteFiles, addNetImportExclusion) => { onDeletePress = (deleteFiles, addImportExclusion) => {
this.props.deleteMovie({ this.props.deleteMovie({
id: this.props.movieId, id: this.props.movieId,
deleteFiles, deleteFiles,
addNetImportExclusion addImportExclusion
}); });
this.props.onModalClose(true); this.props.onModalClose(true);

View file

@ -4,7 +4,7 @@ import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import MovieHeadshot from 'Movie/MovieHeadshot'; import MovieHeadshot from 'Movie/MovieHeadshot';
import EditNetImportModalConnector from 'Settings/NetImport/NetImport/EditNetImportModalConnector'; import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from '../MovieCreditPoster.css'; import styles from '../MovieCreditPoster.css';
@ -18,24 +18,24 @@ class MovieCastPoster extends Component {
this.state = { this.state = {
hasPosterError: false, hasPosterError: false,
isEditNetImportModalOpen: false isEditImportListModalOpen: false
}; };
} }
// //
// Listeners // Listeners
onEditNetImportPress = () => { onEditImportListPress = () => {
this.setState({ isEditNetImportModalOpen: true }); this.setState({ isEditImportListModalOpen: true });
} }
onAddNetImportPress = () => { onAddImportListPress = () => {
this.props.onNetImportSelect(); this.props.onImportListSelect();
this.setState({ isEditNetImportModalOpen: true }); this.setState({ isEditImportListModalOpen: true });
} }
onEditNetImportModalClose = () => { onEditImportListModalClose = () => {
this.setState({ isEditNetImportModalOpen: false }); this.setState({ isEditImportListModalOpen: false });
} }
onPosterLoad = () => { onPosterLoad = () => {
@ -60,7 +60,7 @@ class MovieCastPoster extends Component {
images, images,
posterWidth, posterWidth,
posterHeight, posterHeight,
netImportId importListId
} = this.props; } = this.props;
const { const {
@ -84,18 +84,18 @@ class MovieCastPoster extends Component {
<div className={styles.posterContainer}> <div className={styles.posterContainer}>
<Label className={styles.controls}> <Label className={styles.controls}>
{ {
netImportId > 0 ? importListId > 0 ?
<IconButton <IconButton
className={styles.action} className={styles.action}
name={icons.EDIT} name={icons.EDIT}
title={translate('EditPerson')} title={translate('EditPerson')}
onPress={this.onEditNetImportPress} onPress={this.onEditImportListPress}
/> : /> :
<IconButton <IconButton
className={styles.action} className={styles.action}
name={icons.ADD} name={icons.ADD}
title={translate('FollowPerson')} title={translate('FollowPerson')}
onPress={this.onAddNetImportPress} onPress={this.onAddImportListPress}
/> />
} }
</Label> </Label>
@ -130,11 +130,11 @@ class MovieCastPoster extends Component {
{character} {character}
</div> </div>
<EditNetImportModalConnector <EditImportListModalConnector
id={netImportId} id={importListId}
isOpen={this.state.isEditNetImportModalOpen} isOpen={this.state.isEditImportListModalOpen}
onModalClose={this.onEditNetImportModalClose} onModalClose={this.onEditImportListModalClose}
onDeleteNetImportPress={this.onDeleteNetImportPress} onDeleteImportListPress={this.onDeleteImportListPress}
/> />
</div> </div>
); );
@ -148,12 +148,12 @@ MovieCastPoster.propTypes = {
images: PropTypes.arrayOf(PropTypes.object).isRequired, images: PropTypes.arrayOf(PropTypes.object).isRequired,
posterWidth: PropTypes.number.isRequired, posterWidth: PropTypes.number.isRequired,
posterHeight: PropTypes.number.isRequired, posterHeight: PropTypes.number.isRequired,
netImportId: PropTypes.number.isRequired, importListId: PropTypes.number.isRequired,
onNetImportSelect: PropTypes.func.isRequired onImportListSelect: PropTypes.func.isRequired
}; };
MovieCastPoster.defaultProps = { MovieCastPoster.defaultProps = {
netImportId: 0 importListId: 0
}; };
export default MovieCastPoster; export default MovieCastPoster;

View file

@ -4,7 +4,7 @@ import Label from 'Components/Label';
import IconButton from 'Components/Link/IconButton'; import IconButton from 'Components/Link/IconButton';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import MovieHeadshot from 'Movie/MovieHeadshot'; import MovieHeadshot from 'Movie/MovieHeadshot';
import EditNetImportModalConnector from 'Settings/NetImport/NetImport/EditNetImportModalConnector'; import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from '../MovieCreditPoster.css'; import styles from '../MovieCreditPoster.css';
@ -18,24 +18,24 @@ class MovieCrewPoster extends Component {
this.state = { this.state = {
hasPosterError: false, hasPosterError: false,
isEditNetImportModalOpen: false isEditImportListModalOpen: false
}; };
} }
// //
// Listeners // Listeners
onEditNetImportPress = () => { onEditImportListPress = () => {
this.setState({ isEditNetImportModalOpen: true }); this.setState({ isEditImportListModalOpen: true });
} }
onAddNetImportPress = () => { onAddImportListPress = () => {
this.props.onNetImportSelect(); this.props.onImportListSelect();
this.setState({ isEditNetImportModalOpen: true }); this.setState({ isEditImportListModalOpen: true });
} }
onEditNetImportModalClose = () => { onEditImportListModalClose = () => {
this.setState({ isEditNetImportModalOpen: false }); this.setState({ isEditImportListModalOpen: false });
} }
onPosterLoad = () => { onPosterLoad = () => {
@ -60,7 +60,7 @@ class MovieCrewPoster extends Component {
images, images,
posterWidth, posterWidth,
posterHeight, posterHeight,
netImportId importListId
} = this.props; } = this.props;
const { const {
@ -84,18 +84,18 @@ class MovieCrewPoster extends Component {
<div className={styles.posterContainer}> <div className={styles.posterContainer}>
<Label className={styles.controls}> <Label className={styles.controls}>
{ {
netImportId > 0 ? importListId > 0 ?
<IconButton <IconButton
className={styles.action} className={styles.action}
name={icons.EDIT} name={icons.EDIT}
title={translate('EditPerson')} title={translate('EditPerson')}
onPress={this.onEditNetImportPress} onPress={this.onEditImportListPress}
/> : /> :
<IconButton <IconButton
className={styles.action} className={styles.action}
name={icons.ADD} name={icons.ADD}
title={translate('FollowPerson')} title={translate('FollowPerson')}
onPress={this.onAddNetImportPress} onPress={this.onAddImportListPress}
/> />
} }
</Label> </Label>
@ -130,11 +130,11 @@ class MovieCrewPoster extends Component {
{job} {job}
</div> </div>
<EditNetImportModalConnector <EditImportListModalConnector
id={netImportId} id={importListId}
isOpen={this.state.isEditNetImportModalOpen} isOpen={this.state.isEditImportListModalOpen}
onModalClose={this.onEditNetImportModalClose} onModalClose={this.onEditImportListModalClose}
onDeleteNetImportPress={this.onDeleteNetImportPress} onDeleteImportListPress={this.onDeleteImportListPress}
/> />
</div> </div>
); );
@ -148,12 +148,12 @@ MovieCrewPoster.propTypes = {
images: PropTypes.arrayOf(PropTypes.object).isRequired, images: PropTypes.arrayOf(PropTypes.object).isRequired,
posterWidth: PropTypes.number.isRequired, posterWidth: PropTypes.number.isRequired,
posterHeight: PropTypes.number.isRequired, posterHeight: PropTypes.number.isRequired,
netImportId: PropTypes.number.isRequired, importListId: PropTypes.number.isRequired,
onNetImportSelect: PropTypes.func.isRequired onImportListSelect: PropTypes.func.isRequired
}; };
MovieCrewPoster.defaultProps = { MovieCrewPoster.defaultProps = {
netImportId: 0 importListId: 0
}; };
export default MovieCrewPoster; export default MovieCrewPoster;

View file

@ -1,7 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectNetImportSchema, setNetImportFieldValue, setNetImportValue } from 'Store/Actions/settingsActions'; import { selectImportListSchema, setImportListFieldValue, setImportListValue } from 'Store/Actions/settingsActions';
import createMovieCreditListSelector from 'Store/Selectors/createMovieCreditListSelector'; import createMovieCreditListSelector from 'Store/Selectors/createMovieCreditListSelector';
function createMapStateToProps() { function createMapStateToProps() {
@ -9,9 +9,9 @@ function createMapStateToProps() {
} }
const mapDispatchToProps = { const mapDispatchToProps = {
selectNetImportSchema, selectImportListSchema,
setNetImportFieldValue, setImportListFieldValue,
setNetImportValue setImportListValue
}; };
class MovieCreditPosterConnector extends Component { class MovieCreditPosterConnector extends Component {
@ -19,10 +19,10 @@ class MovieCreditPosterConnector extends Component {
// //
// Listeners // Listeners
onNetImportSelect = () => { onImportListSelect = () => {
this.props.selectNetImportSchema({ implementation: 'TMDbPersonImport', presetName: undefined }); this.props.selectImportListSchema({ implementation: 'TMDbPersonImport', presetName: undefined });
this.props.setNetImportFieldValue({ name: 'personId', value: this.props.tmdbId.toString() }); this.props.setImportListFieldValue({ name: 'personId', value: this.props.tmdbId.toString() });
this.props.setNetImportValue({ name: 'name', value: `${this.props.personName} - ${this.props.tmdbId}` }); this.props.setImportListValue({ name: 'name', value: `${this.props.personName} - ${this.props.tmdbId}` });
} }
// //
@ -40,7 +40,7 @@ class MovieCreditPosterConnector extends Component {
{...this.props} {...this.props}
tmdbId={tmdbId} tmdbId={tmdbId}
personName={personName} personName={personName}
onNetImportSelect={this.onNetImportSelect} onImportListSelect={this.onImportListSelect}
/> />
); );
} }
@ -50,9 +50,9 @@ MovieCreditPosterConnector.propTypes = {
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
personName: PropTypes.string.isRequired, personName: PropTypes.string.isRequired,
component: PropTypes.elementType.isRequired, component: PropTypes.elementType.isRequired,
selectNetImportSchema: PropTypes.func.isRequired, selectImportListSchema: PropTypes.func.isRequired,
setNetImportFieldValue: PropTypes.func.isRequired, setImportListFieldValue: PropTypes.func.isRequired,
setNetImportValue: PropTypes.func.isRequired setImportListValue: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(MovieCreditPosterConnector); export default connect(createMapStateToProps, mapDispatchToProps)(MovieCreditPosterConnector);

View file

@ -12,7 +12,7 @@ import { clearMovieCredits, fetchMovieCredits } from 'Store/Actions/movieCredits
import { clearMovieFiles, fetchMovieFiles } from 'Store/Actions/movieFileActions'; import { clearMovieFiles, fetchMovieFiles } from 'Store/Actions/movieFileActions';
import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions'; import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions'; import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
import { fetchNetImportSchema } from 'Store/Actions/settingsActions'; import { fetchImportListSchema } from 'Store/Actions/settingsActions';
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector'; import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
@ -198,8 +198,8 @@ function createMapDispatchToProps(dispatch, props) {
dispatchClearQueueDetails() { dispatchClearQueueDetails() {
dispatch(clearQueueDetails()); dispatch(clearQueueDetails());
}, },
dispatchFetchNetImportSchema() { dispatchFetchImportListSchema() {
dispatch(fetchNetImportSchema()); dispatch(fetchImportListSchema());
}, },
dispatchToggleMovieMonitored(payload) { dispatchToggleMovieMonitored(payload) {
dispatch(toggleMovieMonitored(payload)); dispatch(toggleMovieMonitored(payload));
@ -265,7 +265,7 @@ class MovieDetailsConnector extends Component {
this.props.dispatchFetchExtraFiles({ movieId }); this.props.dispatchFetchExtraFiles({ movieId });
this.props.dispatchFetchMovieCredits({ movieId }); this.props.dispatchFetchMovieCredits({ movieId });
this.props.dispatchFetchQueueDetails({ movieId }); this.props.dispatchFetchQueueDetails({ movieId });
this.props.dispatchFetchNetImportSchema(); this.props.dispatchFetchImportListSchema();
} }
unpopulate = () => { unpopulate = () => {
@ -336,7 +336,7 @@ MovieDetailsConnector.propTypes = {
dispatchToggleMovieMonitored: PropTypes.func.isRequired, dispatchToggleMovieMonitored: PropTypes.func.isRequired,
dispatchFetchQueueDetails: PropTypes.func.isRequired, dispatchFetchQueueDetails: PropTypes.func.isRequired,
dispatchClearQueueDetails: PropTypes.func.isRequired, dispatchClearQueueDetails: PropTypes.func.isRequired,
dispatchFetchNetImportSchema: PropTypes.func.isRequired, dispatchFetchImportListSchema: PropTypes.func.isRequired,
dispatchExecuteCommand: PropTypes.func.isRequired, dispatchExecuteCommand: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired onGoToMovie: PropTypes.func.isRequired
}; };

View file

@ -22,7 +22,7 @@ class DeleteMovieModalContent extends Component {
this.state = { this.state = {
deleteFiles: false, deleteFiles: false,
addNetImportExclusion: false addImportExclusion: false
}; };
} }
@ -33,16 +33,16 @@ class DeleteMovieModalContent extends Component {
this.setState({ deleteFiles: value }); this.setState({ deleteFiles: value });
} }
onAddNetImportExclusionChange = ({ value }) => { onAddImportExclusionChange = ({ value }) => {
this.setState({ addNetImportExclusion: value }); this.setState({ addImportExclusion: value });
} }
onDeleteMovieConfirmed = () => { onDeleteMovieConfirmed = () => {
const deleteFiles = this.state.deleteFiles; const deleteFiles = this.state.deleteFiles;
const addNetImportExclusion = this.state.addNetImportExclusion; const addImportExclusion = this.state.addImportExclusion;
this.setState({ deleteFiles: false, addNetImportExclusion: false }); this.setState({ deleteFiles: false, addImportExclusion: false });
this.props.onDeleteSelectedPress(deleteFiles, addNetImportExclusion); this.props.onDeleteSelectedPress(deleteFiles, addImportExclusion);
} }
// //
@ -55,7 +55,7 @@ class DeleteMovieModalContent extends Component {
} = this.props; } = this.props;
const deleteFiles = this.state.deleteFiles; const deleteFiles = this.state.deleteFiles;
const addNetImportExclusion = this.state.addNetImportExclusion; const addImportExclusion = this.state.addImportExclusion;
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
@ -83,11 +83,11 @@ class DeleteMovieModalContent extends Component {
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="addNetImportExclusion" name="addImportExclusion"
value={addNetImportExclusion} value={addImportExclusion}
helpText={translate('AddNetImportExclusionHelpText')} helpText={translate('AddImportExclusionHelpText')}
kind={kinds.DANGER} kind={kinds.DANGER}
onChange={this.onAddNetImportExclusionChange} onChange={this.onAddImportExclusionChange}
/> />
</FormGroup> </FormGroup>
</div> </div>

View file

@ -31,11 +31,11 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) { function createMapDispatchToProps(dispatch, props) {
return { return {
onDeleteSelectedPress(deleteFiles, addNetImportExclusion) { onDeleteSelectedPress(deleteFiles, addImportExclusion) {
dispatch(bulkDeleteMovie({ dispatch(bulkDeleteMovie({
movieIds: props.movieIds, movieIds: props.movieIds,
deleteFiles, deleteFiles,
addNetImportExclusion addImportExclusion
})); }));
props.onModalClose(); props.onModalClose();

View file

@ -1,7 +1,7 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import MonitorToggleButton from 'Components/MonitorToggleButton'; import MonitorToggleButton from 'Components/MonitorToggleButton';
import EditNetImportModalConnector from 'Settings/NetImport/NetImport/EditNetImportModalConnector'; import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
import styles from './MovieCollection.css'; import styles from './MovieCollection.css';
class MovieCollection extends Component { class MovieCollection extends Component {
@ -14,21 +14,21 @@ class MovieCollection extends Component {
this.state = { this.state = {
hasPosterError: false, hasPosterError: false,
isEditNetImportModalOpen: false isEditImportListModalOpen: false
}; };
} }
onAddNetImportPress = (monitored) => { onAddImportListPress = (monitored) => {
if (this.props.collectionList) { if (this.props.collectionList) {
this.props.onMonitorTogglePress(monitored); this.props.onMonitorTogglePress(monitored);
} else { } else {
this.props.onMonitorTogglePress(monitored); this.props.onMonitorTogglePress(monitored);
this.setState({ isEditNetImportModalOpen: true }); this.setState({ isEditImportListModalOpen: true });
} }
} }
onEditNetImportModalClose = () => { onEditImportListModalClose = () => {
this.setState({ isEditNetImportModalOpen: false }); this.setState({ isEditImportListModalOpen: false });
} }
render() { render() {
@ -39,7 +39,7 @@ class MovieCollection extends Component {
} = this.props; } = this.props;
const monitored = collectionList !== undefined && collectionList.enabled && collectionList.enableAuto; const monitored = collectionList !== undefined && collectionList.enabled && collectionList.enableAuto;
const netImportId = collectionList ? collectionList.id : 0; const importListId = collectionList ? collectionList.id : 0;
return ( return (
<div> <div>
@ -48,14 +48,14 @@ class MovieCollection extends Component {
monitored={monitored} monitored={monitored}
isSaving={isSaving} isSaving={isSaving}
size={15} size={15}
onPress={this.onAddNetImportPress} onPress={this.onAddImportListPress}
/> />
{name} {name}
<EditNetImportModalConnector <EditImportListModalConnector
id={netImportId} id={importListId}
isOpen={this.state.isEditNetImportModalOpen} isOpen={this.state.isEditImportListModalOpen}
onModalClose={this.onEditNetImportModalClose} onModalClose={this.onEditImportListModalClose}
onDeleteNetImportPress={this.onDeleteNetImportPress} onDeleteImportListPress={this.onDeleteImportListPress}
/> />
</div> </div>
); );

View file

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { saveNetImport, selectNetImportSchema, setNetImportFieldValue, setNetImportValue } from 'Store/Actions/settingsActions'; import { saveImportList, selectImportListSchema, setImportListFieldValue, setImportListValue } from 'Store/Actions/settingsActions';
import createMovieCollectionListSelector from 'Store/Selectors/createMovieCollectionListSelector'; import createMovieCollectionListSelector from 'Store/Selectors/createMovieCollectionListSelector';
import createMovieSelector from 'Store/Selectors/createMovieSelector'; import createMovieSelector from 'Store/Selectors/createMovieSelector';
import MovieCollection from './MovieCollection'; import MovieCollection from './MovieCollection';
@ -11,8 +11,8 @@ function createMapStateToProps() {
return createSelector( return createSelector(
createMovieSelector(), createMovieSelector(),
createMovieCollectionListSelector(), createMovieCollectionListSelector(),
(state) => state.settings.netImports, (state) => state.settings.importLists,
(movie, collectionList, netImports) => { (movie, collectionList, importLists) => {
const { const {
monitored, monitored,
qualityProfileId, qualityProfileId,
@ -24,17 +24,17 @@ function createMapStateToProps() {
monitored, monitored,
qualityProfileId, qualityProfileId,
minimumAvailability, minimumAvailability,
isSaving: netImports.isSaving isSaving: importLists.isSaving
}; };
} }
); );
} }
const mapDispatchToProps = { const mapDispatchToProps = {
selectNetImportSchema, selectImportListSchema,
setNetImportFieldValue, setImportListFieldValue,
setNetImportValue, setImportListValue,
saveNetImport saveImportList
}; };
class MovieCollectionConnector extends Component { class MovieCollectionConnector extends Component {
@ -44,18 +44,18 @@ class MovieCollectionConnector extends Component {
onMonitorTogglePress = (monitored) => { onMonitorTogglePress = (monitored) => {
if (this.props.collectionList) { if (this.props.collectionList) {
this.props.setNetImportValue({ name: 'enabled', value: monitored }); this.props.setImportListValue({ name: 'enabled', value: monitored });
this.props.setNetImportValue({ name: 'enableAuto', value: monitored }); this.props.setImportListValue({ name: 'enableAuto', value: monitored });
this.props.saveNetImport({ id: this.props.collectionList.id }); this.props.saveImportList({ id: this.props.collectionList.id });
} else { } else {
this.props.selectNetImportSchema({ implementation: 'TMDbCollectionImport', presetName: undefined }); this.props.selectImportListSchema({ implementation: 'TMDbCollectionImport', presetName: undefined });
this.props.setNetImportFieldValue({ name: 'collectionId', value: this.props.tmdbId.toString() }); this.props.setImportListFieldValue({ name: 'collectionId', value: this.props.tmdbId.toString() });
this.props.setNetImportValue({ name: 'enabled', value: true }); this.props.setImportListValue({ name: 'enabled', value: true });
this.props.setNetImportValue({ name: 'enableAuto', value: true }); this.props.setImportListValue({ name: 'enableAuto', value: true });
this.props.setNetImportValue({ name: 'name', value: `${this.props.name} - ${this.props.tmdbId}` }); this.props.setImportListValue({ name: 'name', value: `${this.props.name} - ${this.props.tmdbId}` });
this.props.setNetImportValue({ name: 'qualityProfileId', value: this.props.qualityProfileId }); this.props.setImportListValue({ name: 'qualityProfileId', value: this.props.qualityProfileId });
this.props.setNetImportValue({ name: 'monitored', value: this.props.monitored }); this.props.setImportListValue({ name: 'monitored', value: this.props.monitored });
this.props.setNetImportValue({ name: 'minimumAvailability', value: this.props.minimumAvailability }); this.props.setImportListValue({ name: 'minimumAvailability', value: this.props.minimumAvailability });
} }
} }
@ -81,10 +81,10 @@ MovieCollectionConnector.propTypes = {
qualityProfileId: PropTypes.number.isRequired, qualityProfileId: PropTypes.number.isRequired,
minimumAvailability: PropTypes.string.isRequired, minimumAvailability: PropTypes.string.isRequired,
isSaving: PropTypes.bool.isRequired, isSaving: PropTypes.bool.isRequired,
selectNetImportSchema: PropTypes.func.isRequired, selectImportListSchema: PropTypes.func.isRequired,
setNetImportFieldValue: PropTypes.func.isRequired, setImportListFieldValue: PropTypes.func.isRequired,
setNetImportValue: PropTypes.func.isRequired, setImportListValue: PropTypes.func.isRequired,
saveNetImport: PropTypes.func.isRequired saveImportList: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(MovieCollectionConnector); export default connect(createMapStateToProps, mapDispatchToProps)(MovieCollectionConnector);

View file

@ -2,16 +2,16 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props'; import { sizes } from 'Helpers/Props';
import EditNetImportExclusionModalContentConnector from './EditNetImportExclusionModalContentConnector'; import EditImportExclusionModalContentConnector from './EditImportExclusionModalContentConnector';
function EditNetImportExclusionModal({ isOpen, onModalClose, ...otherProps }) { function EditImportExclusionModal({ isOpen, onModalClose, ...otherProps }) {
return ( return (
<Modal <Modal
size={sizes.MEDIUM} size={sizes.MEDIUM}
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
> >
<EditNetImportExclusionModalContentConnector <EditImportExclusionModalContentConnector
{...otherProps} {...otherProps}
onModalClose={onModalClose} onModalClose={onModalClose}
/> />
@ -19,9 +19,9 @@ function EditNetImportExclusionModal({ isOpen, onModalClose, ...otherProps }) {
); );
} }
EditNetImportExclusionModal.propTypes = { EditImportExclusionModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default EditNetImportExclusionModal; export default EditImportExclusionModal;

View file

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { clearPendingChanges } from 'Store/Actions/baseActions'; import { clearPendingChanges } from 'Store/Actions/baseActions';
import EditNetImportExclusionModal from './EditNetImportExclusionModal'; import EditImportExclusionModal from './EditImportExclusionModal';
function mapStateToProps() { function mapStateToProps() {
return {}; return {};
@ -12,13 +12,13 @@ const mapDispatchToProps = {
clearPendingChanges clearPendingChanges
}; };
class EditNetImportExclusionModalConnector extends Component { class EditImportExclusionModalConnector extends Component {
// //
// Listeners // Listeners
onModalClose = () => { onModalClose = () => {
this.props.clearPendingChanges({ section: 'settings.netImportExclusions' }); this.props.clearPendingChanges({ section: 'settings.importExclusions' });
this.props.onModalClose(); this.props.onModalClose();
} }
@ -27,7 +27,7 @@ class EditNetImportExclusionModalConnector extends Component {
render() { render() {
return ( return (
<EditNetImportExclusionModal <EditImportExclusionModal
{...this.props} {...this.props}
onModalClose={this.onModalClose} onModalClose={this.onModalClose}
/> />
@ -35,9 +35,9 @@ class EditNetImportExclusionModalConnector extends Component {
} }
} }
EditNetImportExclusionModalConnector.propTypes = { EditImportExclusionModalConnector.propTypes = {
onModalClose: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired clearPendingChanges: PropTypes.func.isRequired
}; };
export default connect(mapStateToProps, mapDispatchToProps)(EditNetImportExclusionModalConnector); export default connect(mapStateToProps, mapDispatchToProps)(EditImportExclusionModalConnector);

View file

@ -13,9 +13,9 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props'; import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './EditNetImportExclusionModalContent.css'; import styles from './EditImportExclusionModalContent.css';
function EditNetImportExclusionModalContent(props) { function EditImportExclusionModalContent(props) {
const { const {
id, id,
isFetching, isFetching,
@ -26,7 +26,7 @@ function EditNetImportExclusionModalContent(props) {
onInputChange, onInputChange,
onSavePress, onSavePress,
onModalClose, onModalClose,
onDeleteNetImportExclusionPress, onDeleteImportExclusionPress,
...otherProps ...otherProps
} = props; } = props;
@ -104,7 +104,7 @@ function EditNetImportExclusionModalContent(props) {
<Button <Button
className={styles.deleteButton} className={styles.deleteButton}
kind={kinds.DANGER} kind={kinds.DANGER}
onPress={onDeleteNetImportExclusionPress} onPress={onDeleteImportExclusionPress}
> >
{translate('Delete')} {translate('Delete')}
</Button> </Button>
@ -128,7 +128,7 @@ function EditNetImportExclusionModalContent(props) {
); );
} }
EditNetImportExclusionModalContent.propTypes = { EditImportExclusionModalContent.propTypes = {
id: PropTypes.number, id: PropTypes.number,
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
@ -138,7 +138,7 @@ EditNetImportExclusionModalContent.propTypes = {
onInputChange: PropTypes.func.isRequired, onInputChange: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired, onSavePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired,
onDeleteNetImportExclusionPress: PropTypes.func onDeleteImportExclusionPress: PropTypes.func
}; };
export default EditNetImportExclusionModalContent; export default EditImportExclusionModalContent;

View file

@ -3,21 +3,21 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { saveNetImportExclusion, setNetImportExclusionValue } from 'Store/Actions/settingsActions'; import { saveImportExclusion, setImportExclusionValue } from 'Store/Actions/settingsActions';
import selectSettings from 'Store/Selectors/selectSettings'; import selectSettings from 'Store/Selectors/selectSettings';
import EditNetImportExclusionModalContent from './EditNetImportExclusionModalContent'; import EditImportExclusionModalContent from './EditImportExclusionModalContent';
const newNetImportExclusion = { const newImportExclusion = {
movieTitle: '', movieTitle: '',
tmdbId: 0, tmdbId: 0,
movieYear: 0 movieYear: 0
}; };
function createNetImportExclusionSelector() { function createImportExclusionSelector() {
return createSelector( return createSelector(
(state, { id }) => id, (state, { id }) => id,
(state) => state.settings.netImportExclusions, (state) => state.settings.importExclusions,
(id, netImportExclusions) => { (id, importExclusions) => {
const { const {
isFetching, isFetching,
error, error,
@ -25,9 +25,9 @@ function createNetImportExclusionSelector() {
saveError, saveError,
pendingChanges, pendingChanges,
items items
} = netImportExclusions; } = importExclusions;
const mapping = id ? _.find(items, { id }) : newNetImportExclusion; const mapping = id ? _.find(items, { id }) : newImportExclusion;
const settings = selectSettings(mapping, pendingChanges, saveError); const settings = selectSettings(mapping, pendingChanges, saveError);
return { return {
@ -45,31 +45,31 @@ function createNetImportExclusionSelector() {
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
createNetImportExclusionSelector(), createImportExclusionSelector(),
(netImportExclusion) => { (importExclusion) => {
return { return {
...netImportExclusion ...importExclusion
}; };
} }
); );
} }
const mapDispatchToProps = { const mapDispatchToProps = {
setNetImportExclusionValue, setImportExclusionValue,
saveNetImportExclusion saveImportExclusion
}; };
class EditNetImportExclusionModalContentConnector extends Component { class EditImportExclusionModalContentConnector extends Component {
// //
// Lifecycle // Lifecycle
componentDidMount() { componentDidMount() {
if (!this.props.id) { if (!this.props.id) {
Object.keys(newNetImportExclusion).forEach((name) => { Object.keys(newImportExclusion).forEach((name) => {
this.props.setNetImportExclusionValue({ this.props.setImportExclusionValue({
name, name,
value: newNetImportExclusion[name] value: newImportExclusion[name]
}); });
}); });
} }
@ -85,11 +85,11 @@ class EditNetImportExclusionModalContentConnector extends Component {
// Listeners // Listeners
onInputChange = ({ name, value }) => { onInputChange = ({ name, value }) => {
this.props.setNetImportExclusionValue({ name, value }); this.props.setImportExclusionValue({ name, value });
} }
onSavePress = () => { onSavePress = () => {
this.props.saveNetImportExclusion({ id: this.props.id }); this.props.saveImportExclusion({ id: this.props.id });
} }
// //
@ -97,7 +97,7 @@ class EditNetImportExclusionModalContentConnector extends Component {
render() { render() {
return ( return (
<EditNetImportExclusionModalContent <EditImportExclusionModalContent
{...this.props} {...this.props}
onSavePress={this.onSavePress} onSavePress={this.onSavePress}
onInputChange={this.onInputChange} onInputChange={this.onInputChange}
@ -106,14 +106,14 @@ class EditNetImportExclusionModalContentConnector extends Component {
} }
} }
EditNetImportExclusionModalContentConnector.propTypes = { EditImportExclusionModalContentConnector.propTypes = {
id: PropTypes.number, id: PropTypes.number,
isSaving: PropTypes.bool.isRequired, isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object, saveError: PropTypes.object,
item: PropTypes.object.isRequired, item: PropTypes.object.isRequired,
setNetImportExclusionValue: PropTypes.func.isRequired, setImportExclusionValue: PropTypes.func.isRequired,
saveNetImportExclusion: PropTypes.func.isRequired, saveImportExclusion: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(EditNetImportExclusionModalContentConnector); export default connect(createMapStateToProps, mapDispatchToProps)(EditImportExclusionModalContentConnector);

View file

@ -1,4 +1,4 @@
.netImportExclusion { .importExclusion {
display: flex; display: flex;
align-items: stretch; align-items: stretch;
margin-bottom: 10px; margin-bottom: 10px;

View file

@ -6,10 +6,10 @@ import Link from 'Components/Link/Link';
import ConfirmModal from 'Components/Modal/ConfirmModal'; import ConfirmModal from 'Components/Modal/ConfirmModal';
import { icons, kinds } from 'Helpers/Props'; import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import EditNetImportExclusionModalConnector from './EditNetImportExclusionModalConnector'; import EditImportExclusionModalConnector from './EditImportExclusionModalConnector';
import styles from './NetImportExclusion.css'; import styles from './ImportExclusion.css';
class NetImportExclusion extends Component { class ImportExclusion extends Component {
// //
// Lifecycle // Lifecycle
@ -18,35 +18,35 @@ class NetImportExclusion extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
isEditNetImportExclusionModalOpen: false, isEditImportExclusionModalOpen: false,
isDeleteNetImportExclusionModalOpen: false isDeleteImportExclusionModalOpen: false
}; };
} }
// //
// Listeners // Listeners
onEditNetImportExclusionPress = () => { onEditImportExclusionPress = () => {
this.setState({ isEditNetImportExclusionModalOpen: true }); this.setState({ isEditImportExclusionModalOpen: true });
} }
onEditNetImportExclusionModalClose = () => { onEditImportExclusionModalClose = () => {
this.setState({ isEditNetImportExclusionModalOpen: false }); this.setState({ isEditImportExclusionModalOpen: false });
} }
onDeleteNetImportExclusionPress = () => { onDeleteImportExclusionPress = () => {
this.setState({ this.setState({
isEditNetImportExclusionModalOpen: false, isEditImportExclusionModalOpen: false,
isDeleteNetImportExclusionModalOpen: true isDeleteImportExclusionModalOpen: true
}); });
} }
onDeleteNetImportExclusionModalClose = () => { onDeleteImportExclusionModalClose = () => {
this.setState({ isDeleteNetImportExclusionModalOpen: false }); this.setState({ isDeleteImportExclusionModalOpen: false });
} }
onConfirmDeleteNetImportExclusion = () => { onConfirmDeleteImportExclusion = () => {
this.props.onConfirmDeleteNetImportExclusion(this.props.id); this.props.onConfirmDeleteImportExclusion(this.props.id);
} }
// //
@ -63,7 +63,7 @@ class NetImportExclusion extends Component {
return ( return (
<div <div
className={classNames( className={classNames(
styles.netImportExclusion styles.importExclusion
)} )}
> >
<div className={styles.tmdbId}>{tmdbId}</div> <div className={styles.tmdbId}>{tmdbId}</div>
@ -72,44 +72,44 @@ class NetImportExclusion extends Component {
<div className={styles.actions}> <div className={styles.actions}>
<Link <Link
onPress={this.onEditNetImportExclusionPress} onPress={this.onEditImportExclusionPress}
> >
<Icon name={icons.EDIT} /> <Icon name={icons.EDIT} />
</Link> </Link>
</div> </div>
<EditNetImportExclusionModalConnector <EditImportExclusionModalConnector
id={id} id={id}
isOpen={this.state.isEditNetImportExclusionModalOpen} isOpen={this.state.isEditImportExclusionModalOpen}
onModalClose={this.onEditNetImportExclusionModalClose} onModalClose={this.onEditImportExclusionModalClose}
onDeleteNetImportExclusionPress={this.onDeleteNetImportExclusionPress} onDeleteImportExclusionPress={this.onDeleteImportExclusionPress}
/> />
<ConfirmModal <ConfirmModal
isOpen={this.state.isDeleteNetImportExclusionModalOpen} isOpen={this.state.isDeleteImportExclusionModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title={translate('DeleteImportListExclusion')} title={translate('DeleteImportListExclusion')}
message={translate('AreYouSureYouWantToDeleteThisImportListExclusion')} message={translate('AreYouSureYouWantToDeleteThisImportListExclusion')}
confirmLabel={translate('Delete')} confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteNetImportExclusion} onConfirm={this.onConfirmDeleteImportExclusion}
onCancel={this.onDeleteNetImportExclusionModalClose} onCancel={this.onDeleteImportExclusionModalClose}
/> />
</div> </div>
); );
} }
} }
NetImportExclusion.propTypes = { ImportExclusion.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
movieTitle: PropTypes.string.isRequired, movieTitle: PropTypes.string.isRequired,
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
movieYear: PropTypes.number.isRequired, movieYear: PropTypes.number.isRequired,
onConfirmDeleteNetImportExclusion: PropTypes.func.isRequired onConfirmDeleteImportExclusion: PropTypes.func.isRequired
}; };
NetImportExclusion.defaultProps = { ImportExclusion.defaultProps = {
// The drag preview will not connect the drag handle. // The drag preview will not connect the drag handle.
connectDragSource: (node) => node connectDragSource: (node) => node
}; };
export default NetImportExclusion; export default ImportExclusion;

View file

@ -1,4 +1,4 @@
.netImportExclusionsHeader { .importExclusionsHeader {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
font-weight: bold; font-weight: bold;
@ -13,7 +13,7 @@
flex: 0 0 200px; flex: 0 0 200px;
} }
.addNetImportExclusion { .addImportExclusion {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
padding-right: 10px; padding-right: 10px;

View file

@ -6,11 +6,11 @@ import Link from 'Components/Link/Link';
import PageSectionContent from 'Components/Page/PageSectionContent'; import PageSectionContent from 'Components/Page/PageSectionContent';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import EditNetImportExclusionModalConnector from './EditNetImportExclusionModalConnector'; import EditImportExclusionModalConnector from './EditImportExclusionModalConnector';
import NetImportExclusion from './NetImportExclusion'; import ImportExclusion from './ImportExclusion';
import styles from './NetImportExclusions.css'; import styles from './ImportExclusions.css';
class NetImportExclusions extends Component { class ImportExclusions extends Component {
// //
// Lifecycle // Lifecycle
@ -19,19 +19,19 @@ class NetImportExclusions extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
isAddNetImportExclusionModalOpen: false isAddImportExclusionModalOpen: false
}; };
} }
// //
// Listeners // Listeners
onAddNetImportExclusionPress = () => { onAddImportExclusionPress = () => {
this.setState({ isAddNetImportExclusionModalOpen: true }); this.setState({ isAddImportExclusionModalOpen: true });
} }
onModalClose = () => { onModalClose = () => {
this.setState({ isAddNetImportExclusionModalOpen: false }); this.setState({ isAddImportExclusionModalOpen: false });
} }
// //
@ -40,7 +40,7 @@ class NetImportExclusions extends Component {
render() { render() {
const { const {
items, items,
onConfirmDeleteNetImportExclusion, onConfirmDeleteImportExclusion,
...otherProps ...otherProps
} = this.props; } = this.props;
@ -50,7 +50,7 @@ class NetImportExclusions extends Component {
errorMessage={translate('UnableToLoadListExclusions')} errorMessage={translate('UnableToLoadListExclusions')}
{...otherProps} {...otherProps}
> >
<div className={styles.netImportExclusionsHeader}> <div className={styles.importExclusionsHeader}>
<div className={styles.tmdbId}>TMDB Id</div> <div className={styles.tmdbId}>TMDB Id</div>
<div className={styles.title}>Title</div> <div className={styles.title}>Title</div>
<div className={styles.movieYear}>Year</div> <div className={styles.movieYear}>Year</div>
@ -60,29 +60,29 @@ class NetImportExclusions extends Component {
{ {
items.map((item, index) => { items.map((item, index) => {
return ( return (
<NetImportExclusion <ImportExclusion
key={item.id} key={item.id}
{...item} {...item}
{...otherProps} {...otherProps}
index={index} index={index}
onConfirmDeleteNetImportExclusion={onConfirmDeleteNetImportExclusion} onConfirmDeleteImportExclusion={onConfirmDeleteImportExclusion}
/> />
); );
}) })
} }
</div> </div>
<div className={styles.addNetImportExclusion}> <div className={styles.addImportExclusion}>
<Link <Link
className={styles.addButton} className={styles.addButton}
onPress={this.onAddNetImportExclusionPress} onPress={this.onAddImportExclusionPress}
> >
<Icon name={icons.ADD} /> <Icon name={icons.ADD} />
</Link> </Link>
</div> </div>
<EditNetImportExclusionModalConnector <EditImportExclusionModalConnector
isOpen={this.state.isAddNetImportExclusionModalOpen} isOpen={this.state.isAddImportExclusionModalOpen}
onModalClose={this.onModalClose} onModalClose={this.onModalClose}
/> />
@ -92,11 +92,11 @@ class NetImportExclusions extends Component {
} }
} }
NetImportExclusions.propTypes = { ImportExclusions.propTypes = {
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
onConfirmDeleteNetImportExclusion: PropTypes.func.isRequired onConfirmDeleteImportExclusion: PropTypes.func.isRequired
}; };
export default NetImportExclusions; export default ImportExclusions;

View file

@ -0,0 +1,59 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { deleteImportExclusion, fetchImportExclusions } from 'Store/Actions/settingsActions';
import ImportExclusions from './ImportExclusions';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.importExclusions,
(importExclusions) => {
return {
...importExclusions
};
}
);
}
const mapDispatchToProps = {
fetchImportExclusions,
deleteImportExclusion
};
class ImportExclusionsConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.fetchImportExclusions();
}
//
// Listeners
onConfirmDeleteImportExclusion = (id) => {
this.props.deleteImportExclusion({ id });
}
//
// Render
render() {
return (
<ImportExclusions
{...this.state}
{...this.props}
onConfirmDeleteImportExclusion={this.onConfirmDeleteImportExclusion}
/>
);
}
}
ImportExclusionsConnector.propTypes = {
fetchImportExclusions: PropTypes.func.isRequired,
deleteImportExclusion: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(ImportExclusionsConnector);

View file

@ -7,11 +7,11 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import { icons } from 'Helpers/Props'; import { icons } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import NetImportsConnector from './NetImport/NetImportsConnector'; import ImportExclusionsConnector from './ImportExclusions/ImportExclusionsConnector';
import NetImportExclusionsConnector from './NetImportExclusions/NetImportExclusionsConnector'; import ImportListsConnector from './ImportLists/ImportListsConnector';
import NetImportOptionsConnector from './Options/NetImportOptionsConnector'; import ImportListOptionsConnector from './Options/ImportListOptionsConnector';
class NetImportSettings extends Component { class ImportListSettings extends Component {
// //
// Lifecycle // Lifecycle
@ -50,7 +50,7 @@ class NetImportSettings extends Component {
render() { render() {
const { const {
isTestingAll, isTestingAll,
dispatchTestAllNetImport dispatchTestAllImportList
} = this.props; } = this.props;
const { const {
@ -71,7 +71,7 @@ class NetImportSettings extends Component {
label={translate('TestAllLists')} label={translate('TestAllLists')}
iconName={icons.TEST} iconName={icons.TEST}
isSpinning={isTestingAll} isSpinning={isTestingAll}
onPress={dispatchTestAllNetImport} onPress={dispatchTestAllImportList}
/> />
</Fragment> </Fragment>
} }
@ -79,14 +79,14 @@ class NetImportSettings extends Component {
/> />
<PageContentBody> <PageContentBody>
<NetImportsConnector /> <ImportListsConnector />
<NetImportOptionsConnector <ImportListOptionsConnector
onChildMounted={this.onChildMounted} onChildMounted={this.onChildMounted}
onChildStateChange={this.onChildStateChange} onChildStateChange={this.onChildStateChange}
/> />
<NetImportExclusionsConnector /> <ImportExclusionsConnector />
</PageContentBody> </PageContentBody>
</PageContent> </PageContent>
@ -94,9 +94,9 @@ class NetImportSettings extends Component {
} }
} }
NetImportSettings.propTypes = { ImportListSettings.propTypes = {
isTestingAll: PropTypes.bool.isRequired, isTestingAll: PropTypes.bool.isRequired,
dispatchTestAllNetImport: PropTypes.func.isRequired dispatchTestAllImportList: PropTypes.func.isRequired
}; };
export default NetImportSettings; export default ImportListSettings;

View file

@ -1,11 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { testAllNetImport } from 'Store/Actions/settingsActions'; import { testAllImportList } from 'Store/Actions/settingsActions';
import NetImportSettings from './NetImportSettings'; import ImportListSettings from './ImportListSettings';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.settings.netImports.isTestingAll, (state) => state.settings.importLists.isTestingAll,
(isTestingAll) => { (isTestingAll) => {
return { return {
isTestingAll isTestingAll
@ -15,7 +15,7 @@ function createMapStateToProps() {
} }
const mapDispatchToProps = { const mapDispatchToProps = {
dispatchTestAllNetImport: testAllNetImport dispatchTestAllImportList: testAllImportList
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(NetImportSettings); export default connect(createMapStateToProps, mapDispatchToProps)(ImportListSettings);

View file

@ -1,4 +1,4 @@
.netImport { .importList {
composes: card from '~Components/Card.css'; composes: card from '~Components/Card.css';
position: relative; position: relative;

View file

@ -6,20 +6,20 @@ import Menu from 'Components/Menu/Menu';
import MenuContent from 'Components/Menu/MenuContent'; import MenuContent from 'Components/Menu/MenuContent';
import { sizes } from 'Helpers/Props'; import { sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import AddNetImportPresetMenuItem from './AddNetImportPresetMenuItem'; import AddImportListPresetMenuItem from './AddImportListPresetMenuItem';
import styles from './AddNetImportItem.css'; import styles from './AddImportListItem.css';
class AddNetImportItem extends Component { class AddImportListItem extends Component {
// //
// Listeners // Listeners
onNetImportSelect = () => { onImportListSelect = () => {
const { const {
implementation implementation
} = this.props; } = this.props;
this.props.onNetImportSelect({ implementation }); this.props.onImportListSelect({ implementation });
} }
// //
@ -31,18 +31,18 @@ class AddNetImportItem extends Component {
implementationName, implementationName,
infoLink, infoLink,
presets, presets,
onNetImportSelect onImportListSelect
} = this.props; } = this.props;
const hasPresets = !!presets && !!presets.length; const hasPresets = !!presets && !!presets.length;
return ( return (
<div <div
className={styles.netImport} className={styles.importList}
> >
<Link <Link
className={styles.underlay} className={styles.underlay}
onPress={this.onNetImportSelect} onPress={this.onImportListSelect}
/> />
<div className={styles.overlay}> <div className={styles.overlay}>
@ -56,7 +56,7 @@ class AddNetImportItem extends Component {
<span> <span>
<Button <Button
size={sizes.SMALL} size={sizes.SMALL}
onPress={this.onNetImportSelect} onPress={this.onImportListSelect}
> >
Custom Custom
</Button> </Button>
@ -73,11 +73,11 @@ class AddNetImportItem extends Component {
{ {
presets.map((preset) => { presets.map((preset) => {
return ( return (
<AddNetImportPresetMenuItem <AddImportListPresetMenuItem
key={preset.name} key={preset.name}
name={preset.name} name={preset.name}
implementation={implementation} implementation={implementation}
onPress={onNetImportSelect} onPress={onImportListSelect}
/> />
); );
}) })
@ -100,12 +100,12 @@ class AddNetImportItem extends Component {
} }
} }
AddNetImportItem.propTypes = { AddImportListItem.propTypes = {
implementation: PropTypes.string.isRequired, implementation: PropTypes.string.isRequired,
implementationName: PropTypes.string.isRequired, implementationName: PropTypes.string.isRequired,
infoLink: PropTypes.string.isRequired, infoLink: PropTypes.string.isRequired,
presets: PropTypes.arrayOf(PropTypes.object), presets: PropTypes.arrayOf(PropTypes.object),
onNetImportSelect: PropTypes.func.isRequired onImportListSelect: PropTypes.func.isRequired
}; };
export default AddNetImportItem; export default AddImportListItem;

View file

@ -1,15 +1,15 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import AddNetImportModalContentConnector from './AddNetImportModalContentConnector'; import AddImportListModalContentConnector from './AddImportListModalContentConnector';
function AddNetImportModal({ isOpen, onModalClose, ...otherProps }) { function AddImportListModal({ isOpen, onModalClose, ...otherProps }) {
return ( return (
<Modal <Modal
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
> >
<AddNetImportModalContentConnector <AddImportListModalContentConnector
{...otherProps} {...otherProps}
onModalClose={onModalClose} onModalClose={onModalClose}
/> />
@ -17,9 +17,9 @@ function AddNetImportModal({ isOpen, onModalClose, ...otherProps }) {
); );
} }
AddNetImportModal.propTypes = { AddImportListModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default AddNetImportModal; export default AddImportListModal;

View file

@ -1,4 +1,4 @@
.netImports { .importLists {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;

View file

@ -11,10 +11,10 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
import titleCase from 'Utilities/String/titleCase'; import titleCase from 'Utilities/String/titleCase';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import AddNetImportItem from './AddNetImportItem'; import AddImportListItem from './AddImportListItem';
import styles from './AddNetImportModalContent.css'; import styles from './AddImportListModalContent.css';
class AddNetImportModalContent extends Component { class AddImportListModalContent extends Component {
// //
// Render // Render
@ -25,7 +25,7 @@ class AddNetImportModalContent extends Component {
isSchemaPopulated, isSchemaPopulated,
schemaError, schemaError,
listGroups, listGroups,
onNetImportSelect, onImportListSelect,
onModalClose onModalClose
} = this.props; } = this.props;
@ -52,22 +52,22 @@ class AddNetImportModalContent extends Component {
<Alert kind={kinds.INFO}> <Alert kind={kinds.INFO}>
<div>Radarr supports any RSS movie lists as well as the one stated below.</div> <div>Radarr supports any RSS movie lists as well as the one stated below.</div>
<div>For more information on the individual netImports, clink on the info buttons.</div> <div>For more information on the individual importLists, clink on the info buttons.</div>
</Alert> </Alert>
{ {
Object.keys(listGroups).map((key) => { Object.keys(listGroups).map((key) => {
return ( return (
<FieldSet legend={`${titleCase(key)} List`} key={key}> <FieldSet legend={`${titleCase(key)} List`} key={key}>
<div className={styles.netImports}> <div className={styles.importLists}>
{ {
listGroups[key].map((netImport) => { listGroups[key].map((importList) => {
return ( return (
<AddNetImportItem <AddImportListItem
key={netImport.implementation} key={importList.implementation}
implementation={netImport.implementation} implementation={importList.implementation}
{...netImport} {...importList}
onNetImportSelect={onNetImportSelect} onImportListSelect={onImportListSelect}
/> />
); );
}) })
@ -92,13 +92,13 @@ class AddNetImportModalContent extends Component {
} }
} }
AddNetImportModalContent.propTypes = { AddImportListModalContent.propTypes = {
isSchemaFetching: PropTypes.bool.isRequired, isSchemaFetching: PropTypes.bool.isRequired,
isSchemaPopulated: PropTypes.bool.isRequired, isSchemaPopulated: PropTypes.bool.isRequired,
schemaError: PropTypes.object, schemaError: PropTypes.object,
listGroups: PropTypes.object.isRequired, listGroups: PropTypes.object.isRequired,
onNetImportSelect: PropTypes.func.isRequired, onImportListSelect: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default AddNetImportModalContent; export default AddImportListModalContent;

View file

@ -3,19 +3,19 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { fetchNetImportSchema, selectNetImportSchema } from 'Store/Actions/settingsActions'; import { fetchImportListSchema, selectImportListSchema } from 'Store/Actions/settingsActions';
import AddNetImportModalContent from './AddNetImportModalContent'; import AddImportListModalContent from './AddImportListModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.settings.netImports, (state) => state.settings.importLists,
(netImports) => { (importLists) => {
const { const {
isSchemaFetching, isSchemaFetching,
isSchemaPopulated, isSchemaPopulated,
schemaError, schemaError,
schema schema
} = netImports; } = importLists;
const listGroups = _.chain(schema) const listGroups = _.chain(schema)
.sortBy((o) => o.listOrder) .sortBy((o) => o.listOrder)
@ -33,25 +33,25 @@ function createMapStateToProps() {
} }
const mapDispatchToProps = { const mapDispatchToProps = {
fetchNetImportSchema, fetchImportListSchema,
selectNetImportSchema selectImportListSchema
}; };
class AddNetImportModalContentConnector extends Component { class AddImportListModalContentConnector extends Component {
// //
// Lifecycle // Lifecycle
componentDidMount() { componentDidMount() {
this.props.fetchNetImportSchema(); this.props.fetchImportListSchema();
} }
// //
// Listeners // Listeners
onNetImportSelect = ({ implementation, name }) => { onImportListSelect = ({ implementation, name }) => {
this.props.selectNetImportSchema({ implementation, presetName: name }); this.props.selectImportListSchema({ implementation, presetName: name });
this.props.onModalClose({ netImportSelected: true }); this.props.onModalClose({ importListSelected: true });
} }
// //
@ -59,18 +59,18 @@ class AddNetImportModalContentConnector extends Component {
render() { render() {
return ( return (
<AddNetImportModalContent <AddImportListModalContent
{...this.props} {...this.props}
onNetImportSelect={this.onNetImportSelect} onImportListSelect={this.onImportListSelect}
/> />
); );
} }
} }
AddNetImportModalContentConnector.propTypes = { AddImportListModalContentConnector.propTypes = {
fetchNetImportSchema: PropTypes.func.isRequired, fetchImportListSchema: PropTypes.func.isRequired,
selectNetImportSchema: PropTypes.func.isRequired, selectImportListSchema: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(AddNetImportModalContentConnector); export default connect(createMapStateToProps, mapDispatchToProps)(AddImportListModalContentConnector);

View file

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import MenuItem from 'Components/Menu/MenuItem'; import MenuItem from 'Components/Menu/MenuItem';
class AddNetImportPresetMenuItem extends Component { class AddImportListPresetMenuItem extends Component {
// //
// Listeners // Listeners
@ -40,10 +40,10 @@ class AddNetImportPresetMenuItem extends Component {
} }
} }
AddNetImportPresetMenuItem.propTypes = { AddImportListPresetMenuItem.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
implementation: PropTypes.string.isRequired, implementation: PropTypes.string.isRequired,
onPress: PropTypes.func.isRequired onPress: PropTypes.func.isRequired
}; };
export default AddNetImportPresetMenuItem; export default AddImportListPresetMenuItem;

View file

@ -2,16 +2,16 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import { sizes } from 'Helpers/Props'; import { sizes } from 'Helpers/Props';
import EditNetImportModalContentConnector from './EditNetImportModalContentConnector'; import EditImportListModalContentConnector from './EditImportListModalContentConnector';
function EditNetImportModal({ isOpen, onModalClose, ...otherProps }) { function EditImportListModal({ isOpen, onModalClose, ...otherProps }) {
return ( return (
<Modal <Modal
size={sizes.MEDIUM} size={sizes.MEDIUM}
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
> >
<EditNetImportModalContentConnector <EditImportListModalContentConnector
{...otherProps} {...otherProps}
onModalClose={onModalClose} onModalClose={onModalClose}
/> />
@ -19,9 +19,9 @@ function EditNetImportModal({ isOpen, onModalClose, ...otherProps }) {
); );
} }
EditNetImportModal.propTypes = { EditImportListModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default EditNetImportModal; export default EditImportListModal;

View file

@ -0,0 +1,65 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import { cancelSaveImportList, cancelTestImportList } from 'Store/Actions/settingsActions';
import EditImportListModal from './EditImportListModal';
function createMapDispatchToProps(dispatch, props) {
const section = 'settings.importLists';
return {
dispatchClearPendingChanges() {
dispatch(clearPendingChanges({ section }));
},
dispatchCancelTestImportList() {
dispatch(cancelTestImportList({ section }));
},
dispatchCancelSaveImportList() {
dispatch(cancelSaveImportList({ section }));
}
};
}
class EditImportListModalConnector extends Component {
//
// Listeners
onModalClose = () => {
this.props.dispatchClearPendingChanges();
this.props.dispatchCancelTestImportList();
this.props.dispatchCancelSaveImportList();
this.props.onModalClose();
}
//
// Render
render() {
const {
dispatchClearPendingChanges,
dispatchCancelTestImportList,
dispatchCancelSaveImportList,
...otherProps
} = this.props;
return (
<EditImportListModal
{...otherProps}
onModalClose={this.onModalClose}
/>
);
}
}
EditImportListModalConnector.propTypes = {
onModalClose: PropTypes.func.isRequired,
dispatchClearPendingChanges: PropTypes.func.isRequired,
dispatchCancelTestImportList: PropTypes.func.isRequired,
dispatchCancelSaveImportList: PropTypes.func.isRequired
};
export default connect(null, createMapDispatchToProps)(EditImportListModalConnector);

View file

@ -14,9 +14,9 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import { inputTypes, kinds } from 'Helpers/Props'; import { inputTypes, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './EditNetImportModalContent.css'; import styles from './EditImportListModalContent.css';
function EditNetImportModalContent(props) { function EditImportListModalContent(props) {
const { const {
advancedSettings, advancedSettings,
isFetching, isFetching,
@ -30,7 +30,7 @@ function EditNetImportModalContent(props) {
onModalClose, onModalClose,
onSavePress, onSavePress,
onTestPress, onTestPress,
onDeleteNetImportPress, onDeleteImportListPress,
...otherProps ...otherProps
} = props; } = props;
@ -44,6 +44,7 @@ function EditNetImportModalContent(props) {
minimumAvailability, minimumAvailability,
qualityProfileId, qualityProfileId,
rootFolderPath, rootFolderPath,
searchOnAdd,
tags, tags,
fields fields
} = item; } = item;
@ -117,6 +118,21 @@ function EditNetImportModalContent(props) {
/> />
</FormGroup> </FormGroup>
{
shouldMonitor &&
<FormGroup>
<FormLabel>Search on Add</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="searchOnAdd"
helpText="Search for movies on this list when added to Radarr"
{...searchOnAdd}
onChange={onInputChange}
/>
</FormGroup>
}
<FormGroup> <FormGroup>
<FormLabel>{translate('MinimumAvailability')}</FormLabel> <FormLabel>{translate('MinimumAvailability')}</FormLabel>
<FormInputGroup <FormInputGroup
@ -167,7 +183,7 @@ function EditNetImportModalContent(props) {
<ProviderFieldFormGroup <ProviderFieldFormGroup
key={field.name} key={field.name}
advancedSettings={advancedSettings} advancedSettings={advancedSettings}
provider="netImport" provider="importList"
providerData={item} providerData={item}
{...field} {...field}
onChange={onFieldChange} onChange={onFieldChange}
@ -185,7 +201,7 @@ function EditNetImportModalContent(props) {
<Button <Button
className={styles.deleteButton} className={styles.deleteButton}
kind={kinds.DANGER} kind={kinds.DANGER}
onPress={onDeleteNetImportPress} onPress={onDeleteImportListPress}
> >
{translate('Delete')} {translate('Delete')}
</Button> </Button>
@ -217,7 +233,7 @@ function EditNetImportModalContent(props) {
); );
} }
EditNetImportModalContent.propTypes = { EditImportListModalContent.propTypes = {
advancedSettings: PropTypes.bool.isRequired, advancedSettings: PropTypes.bool.isRequired,
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
@ -230,7 +246,7 @@ EditNetImportModalContent.propTypes = {
onModalClose: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired,
onSavePress: PropTypes.func.isRequired, onSavePress: PropTypes.func.isRequired,
onTestPress: PropTypes.func.isRequired, onTestPress: PropTypes.func.isRequired,
onDeleteNetImportPress: PropTypes.func onDeleteImportListPress: PropTypes.func
}; };
export default EditNetImportModalContent; export default EditImportListModalContent;

View file

@ -2,31 +2,31 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { saveNetImport, setNetImportFieldValue, setNetImportValue, testNetImport } from 'Store/Actions/settingsActions'; import { saveImportList, setImportListFieldValue, setImportListValue, testImportList } from 'Store/Actions/settingsActions';
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector'; import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
import EditNetImportModalContent from './EditNetImportModalContent'; import EditImportListModalContent from './EditImportListModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state) => state.settings.advancedSettings, (state) => state.settings.advancedSettings,
createProviderSettingsSelector('netImports'), createProviderSettingsSelector('importLists'),
(advancedSettings, netImport) => { (advancedSettings, importList) => {
return { return {
advancedSettings, advancedSettings,
...netImport ...importList
}; };
} }
); );
} }
const mapDispatchToProps = { const mapDispatchToProps = {
setNetImportValue, setImportListValue,
setNetImportFieldValue, setImportListFieldValue,
saveNetImport, saveImportList,
testNetImport testImportList
}; };
class EditNetImportModalContentConnector extends Component { class EditImportListModalContentConnector extends Component {
// //
// Lifecycle // Lifecycle
@ -41,19 +41,19 @@ class EditNetImportModalContentConnector extends Component {
// Listeners // Listeners
onInputChange = ({ name, value }) => { onInputChange = ({ name, value }) => {
this.props.setNetImportValue({ name, value }); this.props.setImportListValue({ name, value });
} }
onFieldChange = ({ name, value }) => { onFieldChange = ({ name, value }) => {
this.props.setNetImportFieldValue({ name, value }); this.props.setImportListFieldValue({ name, value });
} }
onSavePress = () => { onSavePress = () => {
this.props.saveNetImport({ id: this.props.id }); this.props.saveImportList({ id: this.props.id });
} }
onTestPress = () => { onTestPress = () => {
this.props.testNetImport({ id: this.props.id }); this.props.testImportList({ id: this.props.id });
} }
// //
@ -61,7 +61,7 @@ class EditNetImportModalContentConnector extends Component {
render() { render() {
return ( return (
<EditNetImportModalContent <EditImportListModalContent
{...this.props} {...this.props}
onSavePress={this.onSavePress} onSavePress={this.onSavePress}
onTestPress={this.onTestPress} onTestPress={this.onTestPress}
@ -72,17 +72,17 @@ class EditNetImportModalContentConnector extends Component {
} }
} }
EditNetImportModalContentConnector.propTypes = { EditImportListModalContentConnector.propTypes = {
id: PropTypes.number, id: PropTypes.number,
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
isSaving: PropTypes.bool.isRequired, isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object, saveError: PropTypes.object,
item: PropTypes.object.isRequired, item: PropTypes.object.isRequired,
setNetImportValue: PropTypes.func.isRequired, setImportListValue: PropTypes.func.isRequired,
setNetImportFieldValue: PropTypes.func.isRequired, setImportListFieldValue: PropTypes.func.isRequired,
saveNetImport: PropTypes.func.isRequired, saveImportList: PropTypes.func.isRequired,
testNetImport: PropTypes.func.isRequired, testImportList: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(EditNetImportModalContentConnector); export default connect(createMapStateToProps, mapDispatchToProps)(EditImportListModalContentConnector);

View file

@ -1,4 +1,4 @@
.netImport { .importList {
composes: card from '~Components/Card.css'; composes: card from '~Components/Card.css';
width: 290px; width: 290px;

View file

@ -5,10 +5,10 @@ import Label from 'Components/Label';
import ConfirmModal from 'Components/Modal/ConfirmModal'; import ConfirmModal from 'Components/Modal/ConfirmModal';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import EditNetImportModalConnector from './EditNetImportModalConnector'; import EditImportListModalConnector from './EditImportListModalConnector';
import styles from './NetImport.css'; import styles from './ImportList.css';
class NetImport extends Component { class ImportList extends Component {
// //
// Lifecycle // Lifecycle
@ -17,35 +17,35 @@ class NetImport extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
isEditNetImportModalOpen: false, isEditImportListModalOpen: false,
isDeleteNetImportModalOpen: false isDeleteImportListModalOpen: false
}; };
} }
// //
// Listeners // Listeners
onEditNetImportPress = () => { onEditImportListPress = () => {
this.setState({ isEditNetImportModalOpen: true }); this.setState({ isEditImportListModalOpen: true });
} }
onEditNetImportModalClose = () => { onEditImportListModalClose = () => {
this.setState({ isEditNetImportModalOpen: false }); this.setState({ isEditImportListModalOpen: false });
} }
onDeleteNetImportPress = () => { onDeleteImportListPress = () => {
this.setState({ this.setState({
isEditNetImportModalOpen: false, isEditImportListModalOpen: false,
isDeleteNetImportModalOpen: true isDeleteImportListModalOpen: true
}); });
} }
onDeleteNetImportModalClose= () => { onDeleteImportListModalClose= () => {
this.setState({ isDeleteNetImportModalOpen: false }); this.setState({ isDeleteImportListModalOpen: false });
} }
onConfirmDeleteNetImport = () => { onConfirmDeleteImportList = () => {
this.props.onConfirmDeleteNetImport(this.props.id); this.props.onConfirmDeleteImportList(this.props.id);
} }
// //
@ -61,9 +61,9 @@ class NetImport extends Component {
return ( return (
<Card <Card
className={styles.netImport} className={styles.importList}
overlayContent={true} overlayContent={true}
onPress={this.onEditNetImportPress} onPress={this.onEditImportListPress}
> >
<div className={styles.name}> <div className={styles.name}>
{name} {name}
@ -96,33 +96,33 @@ class NetImport extends Component {
} }
</div> </div>
<EditNetImportModalConnector <EditImportListModalConnector
id={id} id={id}
isOpen={this.state.isEditNetImportModalOpen} isOpen={this.state.isEditImportListModalOpen}
onModalClose={this.onEditNetImportModalClose} onModalClose={this.onEditImportListModalClose}
onDeleteNetImportPress={this.onDeleteNetImportPress} onDeleteImportListPress={this.onDeleteImportListPress}
/> />
<ConfirmModal <ConfirmModal
isOpen={this.state.isDeleteNetImportModalOpen} isOpen={this.state.isDeleteImportListModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title={translate('DeleteList')} title={translate('DeleteList')}
message={`Are you sure you want to delete the list '${name}'?`} message={`Are you sure you want to delete the list '${name}'?`}
confirmLabel={translate('Delete')} confirmLabel={translate('Delete')}
onConfirm={this.onConfirmDeleteNetImport} onConfirm={this.onConfirmDeleteImportList}
onCancel={this.onDeleteNetImportModalClose} onCancel={this.onDeleteImportListModalClose}
/> />
</Card> </Card>
); );
} }
} }
NetImport.propTypes = { ImportList.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
enabled: PropTypes.bool.isRequired, enabled: PropTypes.bool.isRequired,
enableAuto: PropTypes.bool.isRequired, enableAuto: PropTypes.bool.isRequired,
onConfirmDeleteNetImport: PropTypes.func.isRequired onConfirmDeleteImportList: PropTypes.func.isRequired
}; };
export default NetImport; export default ImportList;

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