New: Add runtime format option in Settings > UI so users can choose between a mins view and h/m view

pull/4949/head
nitsua 4 years ago committed by Qstick
parent 361ffe353d
commit 3548433a10

@ -118,7 +118,6 @@
} }
.links, .links,
.tags,
.rating, .rating,
.year, .year,
.runtime { .runtime {
@ -209,10 +208,13 @@
.certification, .certification,
.links, .links,
.tags,
.rating, .rating,
.year, .year,
.runtime { .runtime {
margin-right: 10px; margin-right: 9px;
}
.details {
font-size: 19px;
} }
} }

@ -268,7 +268,8 @@ class MovieDetails extends Component {
nextMovie, nextMovie,
onMonitorTogglePress, onMonitorTogglePress,
onRefreshPress, onRefreshPress,
onSearchPress onSearchPress,
movieRuntimeFormat
} = this.props; } = this.props;
const { const {
@ -426,7 +427,7 @@ class MovieDetails extends Component {
{ {
!!runtime && !!runtime &&
<span className={styles.runtime}> <span className={styles.runtime}>
{formatRuntime(runtime)} {formatRuntime(runtime, movieRuntimeFormat)}
</span> </span>
} }
@ -465,7 +466,7 @@ class MovieDetails extends Component {
{ {
!!tags.length && !!tags.length &&
<span className={styles.tags}> <span>
<Popover <Popover
anchor={ anchor={
<Icon <Icon
@ -773,7 +774,8 @@ MovieDetails.propTypes = {
onMonitorTogglePress: PropTypes.func.isRequired, onMonitorTogglePress: PropTypes.func.isRequired,
onRefreshPress: PropTypes.func.isRequired, onRefreshPress: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired, onSearchPress: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired onGoToMovie: PropTypes.func.isRequired,
movieRuntimeFormat: PropTypes.string.isRequired
}; };
MovieDetails.defaultProps = { MovieDetails.defaultProps = {

@ -88,7 +88,8 @@ function createMapStateToProps() {
createCommandsSelector(), createCommandsSelector(),
createDimensionsSelector(), createDimensionsSelector(),
(state) => state.app.isSidebarVisible, (state) => state.app.isSidebarVisible,
(titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions, isSidebarVisible) => { (state) => state.settings.ui.item.movieRuntimeFormat,
(titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions, isSidebarVisible, movieRuntimeFormat) => {
const sortedMovies = _.orderBy(allMovies, 'sortTitle'); const sortedMovies = _.orderBy(allMovies, 'sortTitle');
const movieIndex = _.findIndex(sortedMovies, { titleSlug }); const movieIndex = _.findIndex(sortedMovies, { titleSlug });
const movie = sortedMovies[movieIndex]; const movie = sortedMovies[movieIndex];
@ -160,7 +161,8 @@ function createMapStateToProps() {
previousMovie, previousMovie,
nextMovie, nextMovie,
isSmallScreen: dimensions.isSmallScreen, isSmallScreen: dimensions.isSmallScreen,
isSidebarVisible isSidebarVisible,
movieRuntimeFormat
}; };
} }
); );

@ -97,7 +97,8 @@ class MovieIndexRow extends Component {
isSelected, isSelected,
onRefreshMoviePress, onRefreshMoviePress,
onSearchPress, onSearchPress,
onSelectedChange onSelectedChange,
movieRuntimeFormat
} = this.props; } = this.props;
const { const {
@ -253,7 +254,7 @@ class MovieIndexRow extends Component {
key={name} key={name}
className={styles[name]} className={styles[name]}
> >
{formatRuntime(runtime)} {formatRuntime(runtime, movieRuntimeFormat)}
</VirtualTableRowCell> </VirtualTableRowCell>
); );
} }
@ -462,7 +463,8 @@ MovieIndexRow.propTypes = {
onSelectedChange: PropTypes.func.isRequired, onSelectedChange: PropTypes.func.isRequired,
tmdbId: PropTypes.number.isRequired, tmdbId: PropTypes.number.isRequired,
imdbId: PropTypes.string, imdbId: PropTypes.string,
youTubeTrailerId: PropTypes.string youTubeTrailerId: PropTypes.string,
movieRuntimeFormat: PropTypes.string.isRequired
}; };
MovieIndexRow.defaultProps = { MovieIndexRow.defaultProps = {

@ -49,7 +49,8 @@ class MovieIndexTable extends Component {
columns, columns,
selectedState, selectedState,
onSelectedChange, onSelectedChange,
isMovieEditorActive isMovieEditorActive,
movieRuntimeFormat
} = this.props; } = this.props;
const movie = items[rowIndex]; const movie = items[rowIndex];
@ -68,6 +69,7 @@ class MovieIndexTable extends Component {
isSelected={selectedState[movie.id]} isSelected={selectedState[movie.id]}
onSelectedChange={onSelectedChange} onSelectedChange={onSelectedChange}
isMovieEditorActive={isMovieEditorActive} isMovieEditorActive={isMovieEditorActive}
movieRuntimeFormat={movieRuntimeFormat}
/> />
</VirtualTableRow> </VirtualTableRow>
); );
@ -135,7 +137,8 @@ MovieIndexTable.propTypes = {
selectedState: PropTypes.object.isRequired, selectedState: PropTypes.object.isRequired,
onSelectedChange: PropTypes.func.isRequired, onSelectedChange: PropTypes.func.isRequired,
onSelectAllChange: PropTypes.func.isRequired, onSelectAllChange: PropTypes.func.isRequired,
isMovieEditorActive: PropTypes.bool.isRequired isMovieEditorActive: PropTypes.bool.isRequired,
movieRuntimeFormat: PropTypes.string.isRequired
}; };
export default MovieIndexTable; export default MovieIndexTable;

@ -8,11 +8,13 @@ function createMapStateToProps() {
(state) => state.app.dimensions, (state) => state.app.dimensions,
(state) => state.movieIndex.tableOptions, (state) => state.movieIndex.tableOptions,
(state) => state.movieIndex.columns, (state) => state.movieIndex.columns,
(dimensions, tableOptions, columns) => { (state) => state.settings.ui.item.movieRuntimeFormat,
(dimensions, tableOptions, columns, movieRuntimeFormat) => {
return { return {
isSmallScreen: dimensions.isSmallScreen, isSmallScreen: dimensions.isSmallScreen,
showBanners: tableOptions.showBanners, showBanners: tableOptions.showBanners,
columns columns,
movieRuntimeFormat
}; };
} }
); );

@ -43,6 +43,11 @@ export const timeFormatOptions = [
{ key: 'HH:mm', value: '17:00/17:30' } { key: 'HH:mm', value: '17:00/17:30' }
]; ];
export const movieRuntimeFormatOptions = [
{ key: 'hoursMinutes', value: '1h 15m' },
{ key: 'minutes', value: '75 mins' }
];
class UISettings extends Component { class UISettings extends Component {
// //
@ -111,9 +116,21 @@ class UISettings extends Component {
</FormGroup> </FormGroup>
</FieldSet> </FieldSet>
<FieldSet <FieldSet legend={translate('Movies')}>
legend={translate('Dates')} <FormGroup>
> <FormLabel>{translate('SettingsRuntimeFormat')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="movieRuntimeFormat"
values={movieRuntimeFormatOptions}
onChange={onInputChange}
{...settings.movieRuntimeFormat}
/>
</FormGroup>
</FieldSet>
<FieldSet legend={translate('Dates')}>
<FormGroup> <FormGroup>
<FormLabel>{translate('SettingsShortDateFormat')}</FormLabel> <FormLabel>{translate('SettingsShortDateFormat')}</FormLabel>
@ -162,9 +179,7 @@ class UISettings extends Component {
</FormGroup> </FormGroup>
</FieldSet> </FieldSet>
<FieldSet <FieldSet legend={translate('Style')}>
legend={translate('Style')}
>
<FormGroup> <FormGroup>
<FormLabel>{translate('SettingsEnableColorImpairedMode')}</FormLabel> <FormLabel>{translate('SettingsEnableColorImpairedMode')}</FormLabel>
<FormInputGroup <FormInputGroup
@ -177,9 +192,7 @@ class UISettings extends Component {
</FormGroup> </FormGroup>
</FieldSet> </FieldSet>
<FieldSet <FieldSet legend={translate('Language')}>
legend={translate('Language')}
>
<FormGroup> <FormGroup>
<FormLabel>{translate('SettingsUiLanguage')}</FormLabel> <FormLabel>{translate('SettingsUiLanguage')}</FormLabel>
<FormInputGroup <FormInputGroup

@ -1,13 +1,15 @@
function formatRuntime(minutes) { function formatRuntime(minutes, format) {
if (!minutes) { if (!minutes) {
return '0m'; return (format === 'hoursMinutes') ? '0m' : '0 mins';
}
if (format === 'minutes') {
return `${minutes} mins`;
} }
const movieHours = Math.floor(minutes / 60); const movieHours = Math.floor(minutes / 60);
const movieMinutes = (minutes <= 59) ? minutes : minutes % 60; const movieMinutes = (minutes <= 59) ? minutes : minutes % 60;
const formattedRuntime = `${((movieHours > 0) ? `${movieHours}h ` : '') + movieMinutes}m`; return `${((movieHours > 0) ? `${movieHours}h ` : '') + movieMinutes}m`;
return formattedRuntime;
} }
export default formatRuntime; export default formatRuntime;

@ -5,6 +5,7 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
@ -336,6 +337,13 @@ namespace NzbDrone.Core.Configuration
set { SetValue("CalendarWeekColumnHeader", value); } set { SetValue("CalendarWeekColumnHeader", value); }
} }
public MovieRuntimeFormatType MovieRuntimeFormat
{
get { return GetValueEnum("MovieRuntimeFormat", MovieRuntimeFormatType.HoursMinutes); }
set { SetValue("MovieRuntimeFormat", value); }
}
public string ShortDateFormat public string ShortDateFormat
{ {
get { return GetValue("ShortDateFormat", "MMM D YYYY"); } get { return GetValue("ShortDateFormat", "MMM D YYYY"); }

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MetadataSource.SkyHook.Resource; using NzbDrone.Core.MetadataSource.SkyHook.Resource;
using NzbDrone.Core.Security; using NzbDrone.Core.Security;
@ -68,6 +69,7 @@ namespace NzbDrone.Core.Configuration
//UI //UI
int FirstDayOfWeek { get; set; } int FirstDayOfWeek { get; set; }
string CalendarWeekColumnHeader { get; set; } string CalendarWeekColumnHeader { get; set; }
MovieRuntimeFormatType MovieRuntimeFormat { get; set; }
string ShortDateFormat { get; set; } string ShortDateFormat { get; set; }
string LongDateFormat { get; set; } string LongDateFormat { get; set; }

@ -0,0 +1,8 @@
namespace NzbDrone.Core.Configuration
{
public enum MovieRuntimeFormatType
{
HoursMinutes,
Minutes
}
}

@ -523,6 +523,7 @@
"SettingsRemotePathMappingLocalPathHelpText": "Path that Radarr should use to access the remote path locally", "SettingsRemotePathMappingLocalPathHelpText": "Path that Radarr should use to access the remote path locally",
"SettingsRemotePathMappingRemotePath": "Remote Path", "SettingsRemotePathMappingRemotePath": "Remote Path",
"SettingsRemotePathMappingRemotePathHelpText": "Root path to the directory that the Download Client accesses", "SettingsRemotePathMappingRemotePathHelpText": "Root path to the directory that the Download Client accesses",
"SettingsRuntimeFormat": "Runtime Format",
"SettingsShortDateFormat": "Short Date Format", "SettingsShortDateFormat": "Short Date Format",
"SettingsShowRelativeDates": "Show Relative Dates", "SettingsShowRelativeDates": "Show Relative Dates",
"SettingsShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates", "SettingsShowRelativeDatesHelpText": "Show relative (Today/Yesterday/etc) or absolute dates",

@ -9,6 +9,9 @@ namespace Radarr.Api.V3.Config
public int FirstDayOfWeek { get; set; } public int FirstDayOfWeek { get; set; }
public string CalendarWeekColumnHeader { get; set; } public string CalendarWeekColumnHeader { get; set; }
// Movies
public MovieRuntimeFormatType MovieRuntimeFormat { get; set; }
//Dates //Dates
public string ShortDateFormat { get; set; } public string ShortDateFormat { get; set; }
public string LongDateFormat { get; set; } public string LongDateFormat { get; set; }
@ -28,6 +31,8 @@ namespace Radarr.Api.V3.Config
FirstDayOfWeek = model.FirstDayOfWeek, FirstDayOfWeek = model.FirstDayOfWeek,
CalendarWeekColumnHeader = model.CalendarWeekColumnHeader, CalendarWeekColumnHeader = model.CalendarWeekColumnHeader,
MovieRuntimeFormat = model.MovieRuntimeFormat,
ShortDateFormat = model.ShortDateFormat, ShortDateFormat = model.ShortDateFormat,
LongDateFormat = model.LongDateFormat, LongDateFormat = model.LongDateFormat,
TimeFormat = model.TimeFormat, TimeFormat = model.TimeFormat,

Loading…
Cancel
Save