diff --git a/frontend/src/Movie/Details/MovieDetails.js b/frontend/src/Movie/Details/MovieDetails.js index 59b9e8f94..e9c110c4d 100644 --- a/frontend/src/Movie/Details/MovieDetails.js +++ b/frontend/src/Movie/Details/MovieDetails.js @@ -20,7 +20,7 @@ import RottenTomatoRating from 'Components/RottenTomatoRating'; import TmdbRating from 'Components/TmdbRating'; import Popover from 'Components/Tooltip/Popover'; import Tooltip from 'Components/Tooltip/Tooltip'; -import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props'; +import { align, icons, kinds, sizes, tooltipPositions } from 'Helpers/Props'; import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal'; import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveSearchFilterMenuConnector'; import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable'; @@ -36,6 +36,7 @@ import fonts from 'Styles/Variables/fonts'; import * as keyCodes from 'Utilities/Constants/keyCodes'; import formatRuntime from 'Utilities/Date/formatRuntime'; import formatBytes from 'Utilities/Number/formatBytes'; +import titleCase from 'Utilities/String/titleCase'; import translate from 'Utilities/String/translate'; import selectAll from 'Utilities/Table/selectAll'; import toggleSelected from 'Utilities/Table/toggleSelected'; @@ -290,7 +291,9 @@ class MovieDetails extends Component { onRefreshPress, onSearchPress, queueItems, - movieRuntimeFormat + movieRuntimeFormat, + indexFilter, + nextPrev } = this.props; const { @@ -355,6 +358,16 @@ class MovieDetails extends Component { onPress={this.onDeleteMoviePress} /> + + + + @@ -395,23 +408,26 @@ class MovieDetails extends Component { -
- + { + nextPrev && +
+ - -
+ +
+ } @@ -830,7 +846,9 @@ MovieDetails.propTypes = { onSearchPress: PropTypes.func.isRequired, onGoToMovie: PropTypes.func.isRequired, queueItems: PropTypes.arrayOf(PropTypes.object), - movieRuntimeFormat: PropTypes.string.isRequired + movieRuntimeFormat: PropTypes.string.isRequired, + indexFilter: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + nextPrev: PropTypes.bool.isRequired }; MovieDetails.defaultProps = { diff --git a/frontend/src/Movie/Details/MovieDetailsConnector.js b/frontend/src/Movie/Details/MovieDetailsConnector.js index 5364feeea..58c7271c8 100644 --- a/frontend/src/Movie/Details/MovieDetailsConnector.js +++ b/frontend/src/Movie/Details/MovieDetailsConnector.js @@ -18,6 +18,7 @@ import { fetchImportListSchema } from 'Store/Actions/settingsActions'; import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; +import createMovieClientSideCollectionItemsSelector from 'Store/Selectors/createMovieClientSideCollectionItemsSelector'; import { findCommand, isCommandExecuting } from 'Utilities/Command'; import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator'; import MovieDetails from './MovieDetails'; @@ -86,16 +87,37 @@ function createMapStateToProps() { selectMovieFiles, selectMovieCredits, selectExtraFiles, + createMovieClientSideCollectionItemsSelector('movieIndex'), createAllMoviesSelector(), createCommandsSelector(), createDimensionsSelector(), (state) => state.queue.details.items, (state) => state.app.isSidebarVisible, (state) => state.settings.ui.item.movieRuntimeFormat, - (titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions, queueItems, isSidebarVisible, movieRuntimeFormat) => { - const sortedMovies = _.orderBy(allMovies, 'sortTitle'); - const movieIndex = _.findIndex(sortedMovies, { titleSlug }); + (state) => state.settings.ui.item.movieDetailsNextPrevBehavior, + (state) => state.movieIndex.selectedFilterKey, + (state) => state.customFilters.items.filter((s) => s.type === 'movieIndex'), + (titleSlug, movieFiles, movieCredits, extraFiles, collectionMovies, allMovies, commands, dimensions, queueItems, isSidebarVisible, movieRuntimeFormat, movieDetailsNextPrevBehavior, indexFilter, customFilters) => { + let sortedMovies = []; + if (movieDetailsNextPrevBehavior === 'filter') { + collectionMovies.items.forEach((c) => sortedMovies.push(allMovies.find((a) => a.id === c.id))); + } else { + sortedMovies = _.orderBy(allMovies, 'sortTitle'); + } + + let movieIndex = _.findIndex(sortedMovies, { titleSlug }); + + if (customFilters.length && Number.isInteger(indexFilter)) { + indexFilter = customFilters.find((filter) => (filter.id === indexFilter)).label; + } + + if (movieIndex === -1) { + sortedMovies.push(allMovies.find((a) => a.titleSlug === titleSlug)); + movieIndex = sortedMovies.length - 1; + } + const movie = sortedMovies[movieIndex]; + const nextPrev = sortedMovies.length > 1; if (!movie) { return {}; @@ -166,7 +188,9 @@ function createMapStateToProps() { isSmallScreen: dimensions.isSmallScreen, isSidebarVisible, queueItems, - movieRuntimeFormat + movieRuntimeFormat, + indexFilter, + nextPrev }; } ); diff --git a/frontend/src/Settings/UI/UISettings.js b/frontend/src/Settings/UI/UISettings.js index 65462863c..107c937a2 100644 --- a/frontend/src/Settings/UI/UISettings.js +++ b/frontend/src/Settings/UI/UISettings.js @@ -12,6 +12,11 @@ import { inputTypes } from 'Helpers/Props'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import translate from 'Utilities/String/translate'; +export const movieDetailsNextPrevBehaviorOption = [ + { key: 'all', value: translate('All') }, + { key: 'filter', value: translate('Filter') } +]; + export const firstDayOfWeekOptions = [ { key: 0, value: translate('Sunday') }, { key: 1, value: translate('Monday') } @@ -132,6 +137,18 @@ class UISettings extends Component { {...settings.movieRuntimeFormat} /> + + {translate('NextPrevBehavior')} + + +
diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 8dc2b746e..0eddd2a92 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -81,6 +81,12 @@ namespace NzbDrone.Core.Configuration return _repository.Get(key.ToLower()) != null; } + public MovieDetailsNextPrevBehaviorOption MovieDetailsNextPrevBehavior + { + get { return GetValueEnum("MovieDetailsNextPrevBehavior", MovieDetailsNextPrevBehaviorOption.All); } + set { SetValue("MovieDetailsNextPrevBehavior", value); } + } + public bool AutoUnmonitorPreviouslyDownloadedMovies { get { return GetValueBoolean("AutoUnmonitorPreviouslyDownloadedMovies"); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index dade3eef1..b8c94baf0 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -70,6 +70,7 @@ namespace NzbDrone.Core.Configuration int FirstDayOfWeek { get; set; } string CalendarWeekColumnHeader { get; set; } MovieRuntimeFormatType MovieRuntimeFormat { get; set; } + MovieDetailsNextPrevBehaviorOption MovieDetailsNextPrevBehavior { get; set; } string ShortDateFormat { get; set; } string LongDateFormat { get; set; } diff --git a/src/NzbDrone.Core/Configuration/MovieDetailsNextPrevBehaviorOption.cs b/src/NzbDrone.Core/Configuration/MovieDetailsNextPrevBehaviorOption.cs new file mode 100644 index 000000000..4ae7571ff --- /dev/null +++ b/src/NzbDrone.Core/Configuration/MovieDetailsNextPrevBehaviorOption.cs @@ -0,0 +1,8 @@ +namespace NzbDrone.Core.Configuration +{ + public enum MovieDetailsNextPrevBehaviorOption + { + All, + Filter + } +} diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 15dcbe8ef..bcfcbde52 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -227,6 +227,7 @@ "DetailedProgressBar": "Detailed Progress Bar", "DetailedProgressBarHelpText": "Show text on progress bar", "Details": "Details", + "DetailsNextPrevBehavior": "What should be used to control Next/Prev buttons on the movie details page", "DigitalRelease": "Digital Release", "Disabled": "Disabled", "Discord": "Discord", @@ -593,6 +594,7 @@ "Never": "Never", "New": "New", "NextExecution": "Next Execution", + "NextPrevBehavior": "Next/Prev Behavior", "No": "No", "NoAltTitle": "No alternative titles.", "NoBackupsAreAvailable": "No backups are available", @@ -645,8 +647,6 @@ "OpenThisModal": "Open This Modal", "Options": "Options", "Organize": "Organize", - "OriginalTitle": "Original Title", - "OriginalLanguage": "Original Language", "OrganizeAndRename": "Organize & Rename", "OrganizeConfirm": "Are you sure you want to organize all files in the {0} selected movie(s)?", "OrganizeModalAllPathsRelative": "All paths are relative to:", @@ -655,6 +655,8 @@ "OrganizeModalSuccess": "Success! My work is done, no files to rename.", "OrganizeSelectedMovies": "Organize Selected Movies", "Original": "Original", + "OriginalLanguage": "Original Language", + "OriginalTitle": "Original Title", "OutputPath": "Output Path", "Overview": "Overview", "OverviewOptions": "Overview Options", diff --git a/src/Radarr.Api.V3/Config/UiConfigResource.cs b/src/Radarr.Api.V3/Config/UiConfigResource.cs index bb1fef871..668b8042a 100644 --- a/src/Radarr.Api.V3/Config/UiConfigResource.cs +++ b/src/Radarr.Api.V3/Config/UiConfigResource.cs @@ -11,6 +11,7 @@ namespace Radarr.Api.V3.Config // Movies public MovieRuntimeFormatType MovieRuntimeFormat { get; set; } + public MovieDetailsNextPrevBehaviorOption MovieDetailsNextPrevBehavior { get; set; } //Dates public string ShortDateFormat { get; set; } @@ -33,6 +34,7 @@ namespace Radarr.Api.V3.Config CalendarWeekColumnHeader = model.CalendarWeekColumnHeader, MovieRuntimeFormat = model.MovieRuntimeFormat, + MovieDetailsNextPrevBehavior = model.MovieDetailsNextPrevBehavior, ShortDateFormat = model.ShortDateFormat, LongDateFormat = model.LongDateFormat,