Fixed: Wanted/Cutoff Search from Index Page

This commit is contained in:
Qstick 2019-07-25 22:34:24 -04:00
parent 6705b59b23
commit d263ff9a6b
7 changed files with 161 additions and 10 deletions

View File

@ -3,12 +3,12 @@ export const BACKUP = 'Backup';
export const CHECK_FOR_FINISHED_DOWNLOAD = 'CheckForFinishedDownload';
export const CLEAR_BLACKLIST = 'ClearBlacklist';
export const CLEAR_LOGS = 'ClearLog';
export const CUTOFF_UNMET_EPISODE_SEARCH = 'CutoffUnmetEpisodeSearch';
export const CUTOFF_UNMET_MOVIES_SEARCH = 'CutoffUnmetMoviesSearch';
export const DELETE_LOG_FILES = 'DeleteLogFiles';
export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles';
export const DOWNLOADED_EPSIODES_SCAN = 'DownloadedEpisodesScan';
export const INTERACTIVE_IMPORT = 'ManualImport';
export const MISSING_EPISODE_SEARCH = 'MissingEpisodeSearch';
export const MISSING_MOVIES_SEARCH = 'MissingMoviesSearch';
export const MOVE_MOVIE = 'MoveMovie';
export const REFRESH_MOVIE = 'RefreshMovie';
export const RENAME_FILES = 'RenameFiles';

View File

@ -0,0 +1,47 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import MenuItem from './MenuItem';
class SearchMenuItem extends Component {
//
// Listeners
onPress = () => {
const {
name,
onPress
} = this.props;
onPress(name);
}
//
// Render
render() {
const {
children,
...otherProps
} = this.props;
return (
<MenuItem
{...otherProps}
onPress={this.onPress}
>
<div>
{children}
</div>
</MenuItem>
);
}
}
SearchMenuItem.propTypes = {
name: PropTypes.string,
children: PropTypes.node.isRequired,
onPress: PropTypes.func.isRequired
};
export default SearchMenuItem;

View File

@ -0,0 +1,52 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { align, icons } from 'Helpers/Props';
import Menu from 'Components/Menu/Menu';
import MenuContent from 'Components/Menu/MenuContent';
import ToolbarMenuButton from 'Components/Menu/ToolbarMenuButton';
import SearchMenuItem from 'Components/Menu/SearchMenuItem';
class MovieIndexSearchMenu extends Component {
render() {
const {
isDisabled,
onSearchPress
} = this.props;
return (
<Menu
isDisabled={isDisabled}
alignMenu={align.RIGHT}
>
<ToolbarMenuButton
iconName={icons.SEARCH}
text="Search"
isDisabled={isDisabled}
/>
<MenuContent>
<SearchMenuItem
name="missingMoviesSearch"
onPress={onSearchPress}
>
Search Missing
</SearchMenuItem>
<SearchMenuItem
name="cutoffUnmetMoviesSearch"
onPress={onSearchPress}
>
Search Cutoff Unmet
</SearchMenuItem>
</MenuContent>
</Menu>
);
}
}
MovieIndexSearchMenu.propTypes = {
isDisabled: PropTypes.bool.isRequired,
onSearchPress: PropTypes.func.isRequired
};
export default MovieIndexSearchMenu;

View File

@ -5,7 +5,7 @@ import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import { align, icons, sortDirections } from 'Helpers/Props';
import { align, icons, kinds, sortDirections } from 'Helpers/Props';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
@ -15,6 +15,7 @@ import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import NoMovie from 'Movie/NoMovie';
import MovieIndexTableConnector from './Table/MovieIndexTableConnector';
import MovieIndexTableOptionsConnector from './Table/MovieIndexTableOptionsConnector';
@ -24,6 +25,7 @@ import MovieIndexOverviewOptionsModal from './Overview/Options/MovieIndexOvervie
import MovieIndexOverviewsConnector from './Overview/MovieIndexOverviewsConnector';
import MovieIndexFilterMenu from './Menus/MovieIndexFilterMenu';
import MovieIndexSortMenu from './Menus/MovieIndexSortMenu';
import MovieIndexSearchMenu from './Menus/MovieIndexSearchMenu';
import MovieIndexViewMenu from './Menus/MovieIndexViewMenu';
import MovieIndexFooterConnector from './MovieIndexFooterConnector';
import MovieEditorFooter from 'Movie/Editor/MovieEditorFooter.js';
@ -60,6 +62,8 @@ class MovieIndex extends Component {
isInteractiveImportModalOpen: false,
isMovieEditorActive: false,
isOrganizingMovieModalOpen: false,
isConfirmSearchModalOpen: false,
searchType: null,
allSelected: false,
allUnselected: false,
lastToggled: null,
@ -215,7 +219,7 @@ class MovieIndex extends Component {
if (this.state.isMovieEditorActive) {
this.setState({ isMovieEditorActive: false });
} else {
const newState = selectAll(this.state.selectedState, false)
const newState = selectAll(this.state.selectedState, false);
newState.isMovieEditorActive = true;
this.setState(newState);
}
@ -258,6 +262,19 @@ class MovieIndex extends Component {
}
}
onSearchPress = (command) => {
this.setState({ isConfirmSearchModalOpen: true, searchType: command });
}
onSearchConfirmed = () => {
this.props.onSearchPress(this.state.searchType);
this.setState({ isConfirmSearchModalOpen: false });
}
onConfirmSearchModalClose = () => {
this.setState({ isConfirmSearchModalOpen: false });
}
onRender = () => {
this.setState({ isRendered: true }, () => {
const {
@ -299,6 +316,7 @@ class MovieIndex extends Component {
isRefreshingMovie,
isRssSyncExecuting,
isOrganizingMovie,
isSearchingMovies,
isSaving,
saveError,
isDeleting,
@ -309,6 +327,7 @@ class MovieIndex extends Component {
onViewSelect,
onRefreshMoviePress,
onRssSyncPress,
onSearchPress,
...otherProps
} = this.props;
@ -319,6 +338,7 @@ class MovieIndex extends Component {
isPosterOptionsModalOpen,
isOverviewOptionsModalOpen,
isInteractiveImportModalOpen,
isConfirmSearchModalOpen,
isMovieEditorActive,
isRendered,
selectedState,
@ -355,10 +375,9 @@ class MovieIndex extends Component {
<PageToolbarSeparator />
<PageToolbarButton
label="Search Missing"
iconName={icons.SEARCH}
isDisabled={hasNoMovie}
<MovieIndexSearchMenu
isDisabled={isSearchingMovies}
onSearchPress={this.onSearchPress}
/>
<PageToolbarButton
@ -564,6 +583,25 @@ class MovieIndex extends Component {
movieIds={selectedMovieIds}
onModalClose={this.onOrganizeMovieModalClose}
/>
<ConfirmModal
isOpen={isConfirmSearchModalOpen}
kind={kinds.DANGER}
title="Mass Movie Search"
message={
<div>
<div>
Are you sure you want to perform mass movie search?
</div>
<div>
This cannot be cancelled once started without restarting Radarr.
</div>
</div>
}
confirmLabel="Search"
onConfirm={this.onSearchConfirmed}
onCancel={this.onConfirmSearchModalClose}
/>
</PageContent>
);
}
@ -584,6 +622,7 @@ MovieIndex.propTypes = {
view: PropTypes.string.isRequired,
isRefreshingMovie: PropTypes.bool.isRequired,
isOrganizingMovie: PropTypes.bool.isRequired,
isSearchingMovies: PropTypes.bool.isRequired,
isRssSyncExecuting: PropTypes.bool.isRequired,
scrollTop: PropTypes.number.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
@ -596,6 +635,7 @@ MovieIndex.propTypes = {
onViewSelect: PropTypes.func.isRequired,
onRefreshMoviePress: PropTypes.func.isRequired,
onRssSyncPress: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired,
onScroll: PropTypes.func.isRequired,
onSaveSelected: PropTypes.func.isRequired
};

View File

@ -43,12 +43,16 @@ function createMapStateToProps() {
createCommandExecutingSelector(commandNames.REFRESH_MOVIE),
createCommandExecutingSelector(commandNames.RSS_SYNC),
createCommandExecutingSelector(commandNames.RENAME_MOVIE),
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_MOVIES_SEARCH),
createCommandExecutingSelector(commandNames.MISSING_MOVIES_SEARCH),
createDimensionsSelector(),
(
movies,
isRefreshingMovie,
isRssSyncExecuting,
isOrganizingMovie,
isCutoffMoviesSearch,
isMissingMoviesSearch,
dimensionsState
) => {
return {
@ -56,6 +60,7 @@ function createMapStateToProps() {
isRefreshingMovie,
isRssSyncExecuting,
isOrganizingMovie,
isSearchingMovies: isCutoffMoviesSearch || isMissingMoviesSearch,
isSmallScreen: dimensionsState.isSmallScreen
};
}
@ -98,6 +103,12 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(executeCommand({
name: commandNames.RSS_SYNC
}));
},
onSearchPress(command) {
dispatch(executeCommand({
name: command
}));
}
};
}

View File

@ -90,7 +90,6 @@ namespace NzbDrone.Core.IndexerSearch
List<Movie> movies = _movieCutoffService.MoviesWhereCutoffUnmet(pagingSpec).Records.ToList();
var queue = _queueService.GetQueue().Select(q => q.Movie.Id);
var missing = movies.Where(e => !queue.Contains(e.Id)).ToList();

View File

@ -177,7 +177,9 @@ namespace NzbDrone.Core.Movies
private SortBuilder<Movie> MoviesWhereCutoffUnmetQuery(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
{
return Query.Where(pagingSpec.FilterExpressions.FirstOrDefault())
return Query
.Join<Movie, MovieFile>(JoinType.Left, e => e.MovieFile, (e, s) => e.MovieFileId == s.Id)
.Where(pagingSpec.FilterExpressions.FirstOrDefault())
.AndWhere(m => m.MovieFileId != 0)
.AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff))
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())