mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-17 15:02:34 +02:00
New: Add icons for search results to indicate if it has been previously grabbed, failed or is in the blacklist.
This commit is contained in:
parent
2ee77aa0a4
commit
4ea9ded0de
@ -23,6 +23,7 @@ import {
|
||||
faArrowCircleLeft as fasArrowCircleLeft,
|
||||
faArrowCircleRight as fasArrowCircleRight,
|
||||
faBackward as fasBackward,
|
||||
faBan as fasBan,
|
||||
faBars as fasBars,
|
||||
faBolt as fasBolt,
|
||||
faBookmark as fasBookmark,
|
||||
@ -223,3 +224,4 @@ export const UNSAVED_SETTING = farDotCircle;
|
||||
export const VIEW = fasEye;
|
||||
export const WARNING = fasExclamationTriangle;
|
||||
export const WIKI = fasBookReader;
|
||||
export const BLACKLIST = fasBan;
|
||||
|
@ -6,7 +6,7 @@ import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { icons, sortDirections } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import InteractiveSearchRow from './InteractiveSearchRow';
|
||||
import InteractiveSearchRowConnector from './InteractiveSearchRowConnector';
|
||||
import styles from './InteractiveSearchContent.css';
|
||||
|
||||
const columns = [
|
||||
@ -34,6 +34,13 @@ const columns = [
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'history',
|
||||
label: translate('History'),
|
||||
isSortable: true,
|
||||
fixedSortDirection: sortDirections.ASCENDING,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
label: translate('Size'),
|
||||
@ -151,7 +158,7 @@ function InteractiveSearchContent(props) {
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<InteractiveSearchRow
|
||||
<InteractiveSearchRowConnector
|
||||
key={item.guid}
|
||||
{...item}
|
||||
searchPayload={searchPayload}
|
||||
|
@ -62,3 +62,13 @@
|
||||
.title div {
|
||||
@add-mixin truncate;
|
||||
}
|
||||
|
||||
.history {
|
||||
composes: cell;
|
||||
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.blacklist {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
@ -124,7 +124,10 @@ class InteractiveSearchRow extends Component {
|
||||
isGrabbed,
|
||||
longDateFormat,
|
||||
timeFormat,
|
||||
grabError
|
||||
grabError,
|
||||
historyGrabbedData,
|
||||
historyFailedData,
|
||||
blacklistData
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -157,6 +160,37 @@ class InteractiveSearchRow extends Component {
|
||||
{indexer}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.history}>
|
||||
{
|
||||
historyGrabbedData?.date && !historyFailedData?.date &&
|
||||
<Icon
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.DEFAULT}
|
||||
title={`${translate('Grabbed')}: ${formatDateTime(historyGrabbedData.date, longDateFormat, timeFormat, { includeSeconds: true })}`}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
historyFailedData?.date &&
|
||||
<Icon
|
||||
className={styles.failed}
|
||||
name={icons.DOWNLOADING}
|
||||
kind={kinds.DANGER}
|
||||
title={`${translate('Failed')}: ${formatDateTime(historyFailedData.date, longDateFormat, timeFormat, { includeSeconds: true })}`}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
blacklistData?.date &&
|
||||
<Icon
|
||||
className={historyGrabbedData || historyFailedData ? styles.blacklist : ''}
|
||||
name={icons.BLACKLIST}
|
||||
kind={kinds.DANGER}
|
||||
title={`${translate('Blacklisted')}: ${formatDateTime(blacklistData.date, longDateFormat, timeFormat, { includeSeconds: true })}`}
|
||||
/>
|
||||
}
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.size}>
|
||||
{formatBytes(size)}
|
||||
</TableRowCell>
|
||||
@ -304,7 +338,10 @@ InteractiveSearchRow.propTypes = {
|
||||
longDateFormat: PropTypes.string.isRequired,
|
||||
timeFormat: PropTypes.string.isRequired,
|
||||
searchPayload: PropTypes.object.isRequired,
|
||||
onGrabPress: PropTypes.func.isRequired
|
||||
onGrabPress: PropTypes.func.isRequired,
|
||||
historyFailedData: PropTypes.object,
|
||||
historyGrabbedData: PropTypes.object,
|
||||
blacklistData: PropTypes.object
|
||||
};
|
||||
|
||||
InteractiveSearchRow.defaultProps = {
|
||||
|
@ -0,0 +1,62 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import InteractiveSearchRow from './InteractiveSearchRow';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state, { guid }) => guid,
|
||||
(state) => state.movieHistory.items,
|
||||
(state) => state.blacklist.items,
|
||||
(guid, movieHistory, blacklist) => {
|
||||
|
||||
let blacklistData = {};
|
||||
let historyFailedData = {};
|
||||
|
||||
const historyGrabbedData = movieHistory.find((movie) => movie.eventType === 'grabbed' && movie.data.guid === guid);
|
||||
if (historyGrabbedData) {
|
||||
historyFailedData = movieHistory.find((movie) => movie.eventType === 'downloadFailed' && movie.sourceTitle === historyGrabbedData.sourceTitle);
|
||||
blacklistData = blacklist.find((item) => item.sourceTitle === historyGrabbedData.sourceTitle);
|
||||
}
|
||||
|
||||
return {
|
||||
historyGrabbedData,
|
||||
historyFailedData,
|
||||
blacklistData
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
class InteractiveSearchRowConnector extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
historyGrabbedData,
|
||||
historyFailedData,
|
||||
blacklistData,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<InteractiveSearchRow
|
||||
historyGrabbedData={historyGrabbedData}
|
||||
historyFailedData={historyFailedData}
|
||||
blacklistData={blacklistData}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InteractiveSearchRowConnector.propTypes = {
|
||||
historyGrabbedData: PropTypes.object,
|
||||
historyFailedData: PropTypes.object,
|
||||
blacklistData: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps)(InteractiveSearchRowConnector);
|
@ -5,11 +5,13 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import * as commandNames from 'Commands/commandNames';
|
||||
import { fetchBlacklist } from 'Store/Actions/blacklistActions';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import { clearExtraFiles, fetchExtraFiles } from 'Store/Actions/extraFileActions';
|
||||
import { toggleMovieMonitored } from 'Store/Actions/movieActions';
|
||||
import { clearMovieCredits, fetchMovieCredits } from 'Store/Actions/movieCreditsActions';
|
||||
import { clearMovieFiles, fetchMovieFiles } from 'Store/Actions/movieFileActions';
|
||||
import { clearMovieHistory, fetchMovieHistory } from 'Store/Actions/movieHistoryActions';
|
||||
import { clearQueueDetails, fetchQueueDetails } from 'Store/Actions/queueActions';
|
||||
import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
|
||||
import { fetchImportListSchema } from 'Store/Actions/settingsActions';
|
||||
@ -178,6 +180,12 @@ function createMapDispatchToProps(dispatch, props) {
|
||||
dispatchClearMovieFiles() {
|
||||
dispatch(clearMovieFiles());
|
||||
},
|
||||
dispatchFetchMovieHistory({ movieId }) {
|
||||
dispatch(fetchMovieHistory({ movieId }));
|
||||
},
|
||||
dispatchClearMovieHistory() {
|
||||
dispatch(clearMovieHistory());
|
||||
},
|
||||
dispatchFetchMovieCredits({ movieId }) {
|
||||
dispatch(fetchMovieCredits({ movieId }));
|
||||
},
|
||||
@ -213,6 +221,10 @@ function createMapDispatchToProps(dispatch, props) {
|
||||
},
|
||||
onGoToMovie(titleSlug) {
|
||||
dispatch(push(`${window.Radarr.urlBase}/movie/${titleSlug}`));
|
||||
},
|
||||
dispatchFetchBlacklist() {
|
||||
// TODO: Allow for passing a movie id to fetch a single movie's blacklist data
|
||||
dispatch(fetchBlacklist());
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -266,15 +278,22 @@ class MovieDetailsConnector extends Component {
|
||||
const movieId = this.props.id;
|
||||
|
||||
this.props.dispatchFetchMovieFiles({ movieId });
|
||||
this.props.dispatchFetchMovieHistory({ movieId });
|
||||
this.props.dispatchFetchExtraFiles({ movieId });
|
||||
this.props.dispatchFetchMovieCredits({ movieId });
|
||||
this.props.dispatchFetchQueueDetails({ movieId });
|
||||
this.props.dispatchFetchImportListSchema();
|
||||
this.props.dispatchFetchBlacklist();
|
||||
}
|
||||
|
||||
repopulate = () => {
|
||||
this.props.dispatchFetchBlacklist();
|
||||
}
|
||||
|
||||
unpopulate = () => {
|
||||
this.props.dispatchCancelFetchReleases();
|
||||
this.props.dispatchClearMovieFiles();
|
||||
this.props.dispatchClearMovieHistory();
|
||||
this.props.dispatchClearExtraFiles();
|
||||
this.props.dispatchClearMovieCredits();
|
||||
this.props.dispatchClearQueueDetails();
|
||||
@ -331,6 +350,8 @@ MovieDetailsConnector.propTypes = {
|
||||
isSmallScreen: PropTypes.bool.isRequired,
|
||||
dispatchFetchMovieFiles: PropTypes.func.isRequired,
|
||||
dispatchClearMovieFiles: PropTypes.func.isRequired,
|
||||
dispatchFetchMovieHistory: PropTypes.func.isRequired,
|
||||
dispatchClearMovieHistory: PropTypes.func.isRequired,
|
||||
dispatchFetchExtraFiles: PropTypes.func.isRequired,
|
||||
dispatchClearExtraFiles: PropTypes.func.isRequired,
|
||||
dispatchFetchMovieCredits: PropTypes.func.isRequired,
|
||||
@ -342,6 +363,7 @@ MovieDetailsConnector.propTypes = {
|
||||
dispatchClearQueueDetails: PropTypes.func.isRequired,
|
||||
dispatchFetchImportListSchema: PropTypes.func.isRequired,
|
||||
dispatchExecuteCommand: PropTypes.func.isRequired,
|
||||
dispatchFetchBlacklist: PropTypes.func.isRequired,
|
||||
onGoToMovie: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { clearMovieHistory, fetchMovieHistory, movieHistoryMarkAsFailed } from 'Store/Actions/movieHistoryActions';
|
||||
import { movieHistoryMarkAsFailed } from 'Store/Actions/movieHistoryActions';
|
||||
import MovieHistoryTableContent from './MovieHistoryTableContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
@ -15,44 +15,11 @@ function createMapStateToProps() {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchMovieHistory,
|
||||
clearMovieHistory,
|
||||
movieHistoryMarkAsFailed
|
||||
};
|
||||
|
||||
class MovieHistoryTableContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
movieId
|
||||
} = this.props;
|
||||
|
||||
this.props.fetchMovieHistory({
|
||||
movieId
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
movieId
|
||||
} = this.props;
|
||||
|
||||
// If the id has changed we need to clear the history
|
||||
if (prevProps.movieId !== movieId) {
|
||||
this.props.clearMovieHistory();
|
||||
this.props.fetchMovieHistory({
|
||||
movieId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.clearMovieHistory();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
@ -82,8 +49,6 @@ class MovieHistoryTableContentConnector extends Component {
|
||||
|
||||
MovieHistoryTableContentConnector.propTypes = {
|
||||
movieId: PropTypes.number.isRequired,
|
||||
fetchMovieHistory: PropTypes.func.isRequired,
|
||||
clearMovieHistory: PropTypes.func.isRequired,
|
||||
movieHistoryMarkAsFailed: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
@ -63,6 +63,7 @@
|
||||
"BindAddress": "Bind Address",
|
||||
"BindAddressHelpText": "Valid IP4 address or '*' for all interfaces",
|
||||
"Blacklist": "Blacklist",
|
||||
"Blacklisted": "Blacklisted",
|
||||
"BlacklistHelpText": "Prevents Radarr from automatically grabbing this movie again",
|
||||
"BlacklistRelease": "Blacklist Release",
|
||||
"Branch": "Branch",
|
||||
|
Loading…
Reference in New Issue
Block a user