diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js index 470dd0d3e..e1d630260 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovie.js @@ -81,7 +81,8 @@ class AddNewMovie extends Component { const { error, items, - hasExistingMovies + hasExistingMovies, + colorImpairedMode } = this.props; const term = this.state.term; @@ -141,6 +142,7 @@ class AddNewMovie extends Component { return ( ); @@ -213,7 +215,8 @@ AddNewMovie.propTypes = { items: PropTypes.arrayOf(PropTypes.object).isRequired, hasExistingMovies: PropTypes.bool.isRequired, onMovieLookupChange: PropTypes.func.isRequired, - onClearMovieLookup: PropTypes.func.isRequired + onClearMovieLookup: PropTypes.func.isRequired, + colorImpairedMode: PropTypes.bool.isRequired }; export default AddNewMovie; diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js index 252d30058..b8d0e0c3e 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieConnector.js @@ -3,8 +3,10 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions'; +import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions'; import { fetchRootFolders } from 'Store/Actions/rootFolderActions'; import { fetchImportExclusions } from 'Store/Actions/Settings/importExclusions'; +import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; import parseUrl from 'Utilities/String/parseUrl'; import AddNewMovie from './AddNewMovie'; @@ -13,13 +15,15 @@ function createMapStateToProps() { (state) => state.addMovie, (state) => state.movies.items.length, (state) => state.router.location, - (addMovie, existingMoviesCount, location) => { + createUISettingsSelector(), + (addMovie, existingMoviesCount, location, uiSettings) => { const { params } = parseUrl(location.search); return { ...addMovie, term: params.term, - hasExistingMovies: existingMoviesCount > 0 + hasExistingMovies: existingMoviesCount > 0, + colorImpairedMode: uiSettings.enableColorImpairedMode }; } ); @@ -29,7 +33,9 @@ const mapDispatchToProps = { lookupMovie, clearAddMovie, fetchRootFolders, - fetchImportExclusions + fetchImportExclusions, + fetchQueueDetails, + clearQueueDetails }; class AddNewMovieConnector extends Component { @@ -46,6 +52,7 @@ class AddNewMovieConnector extends Component { componentDidMount() { this.props.fetchRootFolders(); this.props.fetchImportExclusions(); + this.props.fetchQueueDetails(); } componentWillUnmount() { @@ -54,6 +61,7 @@ class AddNewMovieConnector extends Component { } this.props.clearAddMovie(); + this.props.clearQueueDetails(); } // @@ -102,7 +110,9 @@ AddNewMovieConnector.propTypes = { lookupMovie: PropTypes.func.isRequired, clearAddMovie: PropTypes.func.isRequired, fetchRootFolders: PropTypes.func.isRequired, - fetchImportExclusions: PropTypes.func.isRequired + fetchImportExclusions: PropTypes.func.isRequired, + fetchQueueDetails: PropTypes.func.isRequired, + clearQueueDetails: PropTypes.func.isRequired }; export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector); diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.css b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.css index ffa89f5e6..a631f7dcf 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.css +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.css @@ -27,9 +27,11 @@ } .poster { - flex: 0 0 170px; + position: relative; + display: block; margin-right: 20px; height: 250px; + background-color: $defaultColor; } .content { @@ -86,6 +88,15 @@ pointer-events: all; } +.posterContainer { + position: relative; +} + +.statusContainer { + margin-right: 22px; + font-weight: bold; +} + @media only screen and (max-width: $breakpointMedium) { .titleRow { justify-content: space-between; diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js index 44692a785..e6d8e3b11 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResult.js @@ -7,6 +7,8 @@ import Link from 'Components/Link/Link'; import Tooltip from 'Components/Tooltip/Tooltip'; import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props'; import MovieDetailsLinks from 'Movie/Details/MovieDetailsLinks'; +import MovieStatusLabel from 'Movie/Details/MovieStatusLabel'; +import MovieIndexProgressBar from 'Movie/Index/ProgressBar/MovieIndexProgressBar'; import MoviePoster from 'Movie/MoviePoster'; import translate from 'Utilities/String/translate'; import AddNewMovieModal from './AddNewMovieModal'; @@ -65,7 +67,14 @@ class AddNewMovieSearchResult extends Component { images, isExistingMovie, isExclusionMovie, - isSmallScreen + isSmallScreen, + colorImpairedMode, + id, + monitored, + hasFile, + isAvailable, + queueStatus, + queueState } = this.props; const { @@ -85,12 +94,30 @@ class AddNewMovieSearchResult extends Component { { isSmallScreen ? null : - +
+
+ +
+ + { + isExistingMovie && + + } +
}
@@ -176,13 +203,15 @@ class AddNewMovieSearchResult extends Component { /> { - status === 'ended' && - + isExistingMovie && isSmallScreen && + }
@@ -222,7 +251,15 @@ AddNewMovieSearchResult.propTypes = { images: PropTypes.arrayOf(PropTypes.object).isRequired, isExistingMovie: PropTypes.bool.isRequired, isExclusionMovie: PropTypes.bool.isRequired, - isSmallScreen: PropTypes.bool.isRequired + isSmallScreen: PropTypes.bool.isRequired, + id: PropTypes.number, + queueItems: PropTypes.arrayOf(PropTypes.object), + monitored: PropTypes.bool.isRequired, + hasFile: PropTypes.bool.isRequired, + isAvailable: PropTypes.bool.isRequired, + colorImpairedMode: PropTypes.bool, + queueStatus: PropTypes.string, + queueState: PropTypes.string }; export default AddNewMovieSearchResult; diff --git a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResultConnector.js b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResultConnector.js index fb843d67f..859abb57a 100644 --- a/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResultConnector.js +++ b/frontend/src/AddMovie/AddNewMovie/AddNewMovieSearchResultConnector.js @@ -10,11 +10,17 @@ function createMapStateToProps() { createExistingMovieSelector(), createExclusionMovieSelector(), createDimensionsSelector(), - (isExistingMovie, isExclusionMovie, dimensions) => { + (state) => state.queue.details.items, + (state) => state.tmdbId, + (isExistingMovie, isExclusionMovie, dimensions, queueItems, tmdbId) => { + const firstQueueItem = queueItems.find((q) => q.tmdbId === tmdbId); + return { isExistingMovie, isExclusionMovie, - isSmallScreen: dimensions.isSmallScreen + isSmallScreen: dimensions.isSmallScreen, + queueStatus: firstQueueItem ? firstQueueItem.status : null, + queueState: firstQueueItem ? firstQueueItem.trackedDownloadState : null }; } ); diff --git a/frontend/src/Components/Label.css b/frontend/src/Components/Label.css index b54bba63b..72cba7dd5 100644 --- a/frontend/src/Components/Label.css +++ b/frontend/src/Components/Label.css @@ -19,6 +19,10 @@ &.outline { color: $dangerColor; } + + &:global(.colorImpaired) { + background: repeating-linear-gradient(90deg, color($dangerColor shade(5%)), color($dangerColor shade(5%)) 5px, color($dangerColor shade(15%)) 5px, color($dangerColor shade(15%)) 10px); + } } .default { @@ -85,6 +89,10 @@ &.outline { color: $warningColor; } + + &:global(.colorImpaired) { + background: repeating-linear-gradient(45deg, $warningColor, $warningColor 5px, color($warningColor tint(15%)) 5px, color($warningColor tint(15%)) 10px); + } } .queue { diff --git a/frontend/src/Components/Label.js b/frontend/src/Components/Label.js index 6f662ec7d..26fdf2074 100644 --- a/frontend/src/Components/Label.js +++ b/frontend/src/Components/Label.js @@ -11,6 +11,7 @@ function Label(props) { size, outline, children, + colorImpairedMode, ...otherProps } = props; @@ -20,7 +21,8 @@ function Label(props) { className, styles[kind], styles[size], - outline && styles.outline + outline && styles.outline, + colorImpairedMode && 'colorImpaired' )} {...otherProps} > @@ -34,14 +36,16 @@ Label.propTypes = { kind: PropTypes.oneOf(kinds.all).isRequired, size: PropTypes.oneOf(sizes.all).isRequired, outline: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired + children: PropTypes.node.isRequired, + colorImpairedMode: PropTypes.bool }; Label.defaultProps = { className: styles.label, kind: kinds.DEFAULT, size: sizes.SMALL, - outline: false + outline: false, + colorImpairedMode: false }; export default Label; diff --git a/frontend/src/Movie/Details/MovieDetails.js b/frontend/src/Movie/Details/MovieDetails.js index 6f2bbe789..5ccb3b520 100644 --- a/frontend/src/Movie/Details/MovieDetails.js +++ b/frontend/src/Movie/Details/MovieDetails.js @@ -286,7 +286,7 @@ class MovieDetails extends Component { onMonitorTogglePress, onRefreshPress, onSearchPress, - queueDetails, + queueItems, movieRuntimeFormat } = this.props; @@ -523,7 +523,7 @@ class MovieDetails extends Component { hasMovieFiles={hasMovieFiles} monitored={monitored} isAvailable={isAvailable} - queueDetails={queueDetails} + queueItem={(queueItems.length > 0) ? queueItems[0] : null} /> @@ -794,7 +794,7 @@ MovieDetails.propTypes = { onRefreshPress: PropTypes.func.isRequired, onSearchPress: PropTypes.func.isRequired, onGoToMovie: PropTypes.func.isRequired, - queueDetails: PropTypes.object, + queueItems: PropTypes.arrayOf(PropTypes.object), movieRuntimeFormat: PropTypes.string.isRequired }; diff --git a/frontend/src/Movie/Details/MovieDetailsConnector.js b/frontend/src/Movie/Details/MovieDetailsConnector.js index 2ab6b9167..f2c51788b 100644 --- a/frontend/src/Movie/Details/MovieDetailsConnector.js +++ b/frontend/src/Movie/Details/MovieDetailsConnector.js @@ -89,10 +89,10 @@ function createMapStateToProps() { createAllMoviesSelector(), createCommandsSelector(), createDimensionsSelector(), - (state) => state.queue.details, + (state) => state.queue.details.items, (state) => state.app.isSidebarVisible, (state) => state.settings.ui.item.movieRuntimeFormat, - (titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions, queueDetails, isSidebarVisible, movieRuntimeFormat) => { + (titleSlug, movieFiles, movieCredits, extraFiles, allMovies, commands, dimensions, queueItems, isSidebarVisible, movieRuntimeFormat) => { const sortedMovies = _.orderBy(allMovies, 'sortTitle'); const movieIndex = _.findIndex(sortedMovies, { titleSlug }); const movie = sortedMovies[movieIndex]; @@ -165,7 +165,7 @@ function createMapStateToProps() { nextMovie, isSmallScreen: dimensions.isSmallScreen, isSidebarVisible, - queueDetails, + queueItems, movieRuntimeFormat }; } diff --git a/frontend/src/Movie/Details/MovieStatusLabel.css b/frontend/src/Movie/Details/MovieStatusLabel.css index fff54bd98..9a315858f 100644 --- a/frontend/src/Movie/Details/MovieStatusLabel.css +++ b/frontend/src/Movie/Details/MovieStatusLabel.css @@ -1,24 +1,29 @@ -.missing { - padding-left: 2px; - border-left: 4px solid $dangerColor; -} - -.downloaded { - padding-left: 2px; - border-left: 4px solid $successColor; -} - -.notAvailable { - padding-left: 2px; - border-left: 4px solid $primaryColor; -} - -.unmonitored { - padding-left: 2px; - border-left: 4px solid $warningColor; -} - .queue { padding-left: 2px; border-left: 4px solid $queueColor; } + +.continuing { + padding-left: 2px; + border-left: 4px solid $primaryColor; +} + +.availNotMonitored { + padding-left: 2px; + border-left: 4px solid $darkGray; +} + +.ended { + padding-left: 2px; + border-left: 4px solid $successColor; +} + +.missingMonitored { + padding-left: 2px; + border-left: 4px solid $dangerColor; +} + +.missingUnmonitored { + padding-left: 2px; + border-left: 4px solid $warningColor; +} diff --git a/frontend/src/Movie/Details/MovieStatusLabel.js b/frontend/src/Movie/Details/MovieStatusLabel.js index c605bb91d..c1b3a7a98 100644 --- a/frontend/src/Movie/Details/MovieStatusLabel.js +++ b/frontend/src/Movie/Details/MovieStatusLabel.js @@ -1,15 +1,17 @@ import PropTypes from 'prop-types'; import React from 'react'; +import Label from 'Components/Label'; +import { kinds, sizes } from 'Helpers/Props'; import getQueueStatusText from 'Utilities/Movie/getQueueStatusText'; import firstCharToUpper from 'Utilities/String/firstCharToUpper'; import translate from 'Utilities/String/translate'; import styles from './MovieStatusLabel.css'; -function getMovieStatus(hasFile, isMonitored, isAvailable, queueDetails = false) { +function getMovieStatus(hasFile, isMonitored, isAvailable, queueItem = false) { - if (queueDetails.items[0]) { - const queueStatus = queueDetails.items[0].status; - const queueState = queueDetails.items[0].trackedDownloadStatus; + if (queueItem) { + const queueStatus = queueItem.status; + const queueState = queueItem.trackedDownloadStatus; const queueStatusText = getQueueStatusText(queueStatus, queueState); if (queueStatusText) { @@ -17,19 +19,23 @@ function getMovieStatus(hasFile, isMonitored, isAvailable, queueDetails = false) } } - if (hasFile) { - return 'downloaded'; + if (hasFile && !isMonitored) { + return 'availNotMonitored'; } - if (!isMonitored) { - return 'unmonitored'; + if (hasFile) { + return 'ended'; + } + + if (isAvailable && !isMonitored && !hasFile) { + return 'missingUnmonitored'; } if (isAvailable && !hasFile) { - return 'missing'; + return 'missingMonitored'; } - return 'notAvailable'; + return 'continuing'; } function MovieStatusLabel(props) { @@ -37,16 +43,61 @@ function MovieStatusLabel(props) { hasMovieFiles, monitored, isAvailable, - queueDetails + queueItem, + useLabel, + colorImpairedMode } = props; - const status = getMovieStatus(hasMovieFiles, monitored, isAvailable, queueDetails); + let status = getMovieStatus(hasMovieFiles, monitored, isAvailable, queueItem); let statusClass = status; - if (queueDetails.items.length) { + if (status === 'availNotMonitored' || status === 'ended') { + status = 'downloaded'; + } + if (status === 'missingMonitored' || status === 'missingUnmonitored') { + status = 'missing'; + } + if (status === 'continuing') { + status = 'notAvailable'; + } + + if (queueItem) { statusClass = 'queue'; } + if (useLabel) { + let kind = kinds.SUCCESS; + + switch (statusClass) { + case 'queue': + kind = kinds.QUEUE; + break; + case 'missingMonitored': + kind = kinds.DANGER; + break; + case 'continuing': + kind = kinds.INFO; + break; + case 'availNotMonitored': + kind = kinds.DEFAULT; + break; + case 'missingUnmonitored': + kind = kinds.WARNING; + break; + default: + } + + return ( + + ); + } + return (