Fixed: Movie Details Tab (#3564)

* History Added

* History Cleanup

* History Mark Failed Fix

* History Lint Fix

* Search Tab Initial

* Interactive Search Cleanup

* Files Tab + Small Backend change to MovieFile api

* Reverse Movie History Items

* Grabbed files are not grabbable again.

* Partial movie title outline + Search not updating fix

* Lint Fix + InteractiveSearch refactor

* Rename movieLanguage.js to MovieLanguage.js

* Fixes for qstick's comments

* Rename language selector to allow for const languages

* Qstick comment changes.

* Activity Tabs - Language Column fixed

* Movie Details - MoveStatusLabel fixed

* Spaces + Lower Case added

* fixed DownloadAllowed

* Added padding to history and file tables

* Fix class =>  className

* Updated search to not refresh unless switching movie

* lint fix

* File Tab Converted to Inline Editting

* FIles tab fix + Alt Titles tab implemented

* lint fix

* Cleanup via qstick request
This commit is contained in:
devbrian
2019-07-06 08:47:11 -05:00
committed by Qstick
parent 06b1c03053
commit 12fba024f0
60 changed files with 1565 additions and 821 deletions

View File

@@ -9,7 +9,7 @@ import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import InteractiveSearchFilterModalConnector from './InteractiveSearchFilterModalConnector';
import InteractiveSearchRow from './InteractiveSearchRow';
import styles from './InteractiveSearch.css';
import styles from './InteractiveSearchContent.css';
const columns = [
{
@@ -48,6 +48,12 @@ const columns = [
isSortable: true,
isVisible: true
},
{
name: 'languageWeight',
label: 'Language',
isSortable: true,
isVisible: true
},
{
name: 'qualityWeight',
label: 'Quality',
@@ -70,7 +76,7 @@ const columns = [
}
];
function InteractiveSearch(props) {
function InteractiveSearchContent(props) {
const {
searchPayload,
isFetching,
@@ -83,7 +89,6 @@ function InteractiveSearch(props) {
customFilters,
sortKey,
sortDirection,
type,
longDateFormat,
timeFormat,
onSortPress,
@@ -101,7 +106,6 @@ function InteractiveSearch(props) {
customFilters={customFilters}
buttonComponent={PageMenuButton}
filterModalConnectorComponent={InteractiveSearchFilterModalConnector}
filterModalConnectorComponentProps={{ type }}
onFilterSelect={onFilterSelect}
/>
</div>
@@ -169,7 +173,7 @@ function InteractiveSearch(props) {
);
}
InteractiveSearch.propTypes = {
InteractiveSearchContent.propTypes = {
searchPayload: PropTypes.object.isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
@@ -181,7 +185,6 @@ InteractiveSearch.propTypes = {
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.string,
type: PropTypes.string.isRequired,
longDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
onSortPress: PropTypes.func.isRequired,
@@ -189,4 +192,4 @@ InteractiveSearch.propTypes = {
onGrabPress: PropTypes.func.isRequired
};
export default InteractiveSearch;
export default InteractiveSearchContent;

View File

@@ -5,12 +5,12 @@ import { createSelector } from 'reselect';
import * as releaseActions from 'Store/Actions/releaseActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import InteractiveSearch from './InteractiveSearch';
import InteractiveSearchContent from './InteractiveSearchContent';
function createMapStateToProps(appState, { type }) {
function createMapStateToProps(appState) {
return createSelector(
(state) => state.releases.items.length,
createClientSideCollectionSelector('releases', `releases.${type}`),
createClientSideCollectionSelector('releases'),
createUISettingsSelector(),
(totalReleasesCount, releases, uiSettings) => {
return {
@@ -29,15 +29,16 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(releaseActions.fetchReleases(payload));
},
dispatchClearReleases(payload) {
dispatch(releaseActions.clearReleases(payload));
},
onSortPress(sortKey, sortDirection) {
dispatch(releaseActions.setReleasesSort({ sortKey, sortDirection }));
},
onFilterSelect(selectedFilterKey) {
const action = props.type === 'episode' ?
releaseActions.setEpisodeReleasesFilter :
releaseActions.setSeasonReleasesFilter;
const action = releaseActions.setReleasesFilter;
dispatch(action({ selectedFilterKey }));
},
@@ -47,7 +48,7 @@ function createMapDispatchToProps(dispatch, props) {
};
}
class InteractiveSearchConnector extends Component {
class InteractiveSearchContentConnector extends Component {
//
// Lifecycle
@@ -61,7 +62,6 @@ class InteractiveSearchConnector extends Component {
// If search results are not yet isPopulated fetch them,
// otherwise re-show the existing props.
if (!isPopulated) {
dispatchFetchReleases(searchPayload);
}
@@ -73,22 +73,24 @@ class InteractiveSearchConnector extends Component {
render() {
const {
dispatchFetchReleases,
dispatchClearReleases,
...otherProps
} = this.props;
return (
<InteractiveSearch
<InteractiveSearchContent
{...otherProps}
/>
);
}
}
InteractiveSearchConnector.propTypes = {
InteractiveSearchContentConnector.propTypes = {
searchPayload: PropTypes.object.isRequired,
isPopulated: PropTypes.bool.isRequired,
dispatchFetchReleases: PropTypes.func.isRequired
dispatchFetchReleases: PropTypes.func.isRequired,
dispatchClearReleases: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchConnector);
export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchContentConnector);

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { setEpisodeReleasesFilter, setSeasonReleasesFilter } from 'Store/Actions/releaseActions';
import { setReleasesFilter } from 'Store/Actions/releaseActions';
import FilterModal from 'Components/Filter/FilterModal';
function createMapStateToProps() {
@@ -20,10 +20,7 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) {
return {
dispatchSetFilter(payload) {
const action = props.type === 'episode' ?
setEpisodeReleasesFilter:
setSeasonReleasesFilter;
const action = setReleasesFilter;
dispatch(action(payload));
}
};

View File

@@ -1,13 +1,10 @@
.title {
.quality,
.language {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
word-break: break-all;
}
.quality {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
text-align: center;
.language {
width: 100px;
}
.rejected,

View File

@@ -11,10 +11,11 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRow from 'Components/Table/TableRow';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Popover from 'Components/Tooltip/Popover';
import EpisodeQuality from 'Episode/EpisodeQuality';
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
import Peers from './Peers';
import styles from './InteractiveSearchRow.css';
import MovieQuality from 'Movie/MovieQuality';
import MovieLanguage from 'Movie/MovieLanguage';
function getDownloadIcon(isGrabbing, isGrabbed, grabError) {
if (isGrabbing) {
@@ -111,6 +112,7 @@ class InteractiveSearchRow extends Component {
seeders,
leechers,
quality,
languages,
rejections,
downloadAllowed,
isGrabbing,
@@ -159,8 +161,14 @@ class InteractiveSearchRow extends Component {
}
</TableRowCell>
<TableRowCell className={styles.language}>
<MovieLanguage
languages={languages}
/>
</TableRowCell>
<TableRowCell className={styles.quality}>
<EpisodeQuality
<MovieQuality
quality={quality}
/>
</TableRowCell>
@@ -199,6 +207,7 @@ class InteractiveSearchRow extends Component {
name={getDownloadIcon(isGrabbing, isGrabbed, grabError)}
kind={grabError ? kinds.DANGER : kinds.DEFAULT}
title={getDownloadTooltip(isGrabbing, isGrabbed, grabError)}
isDisabled={isGrabbed}
isSpinning={isGrabbing}
onPress={downloadAllowed ? this.onGrabPress : this.onConfirmGrabPress}
/>
@@ -208,7 +217,7 @@ class InteractiveSearchRow extends Component {
isOpen={this.state.isConfirmGrabModalOpen}
kind={kinds.WARNING}
title="Grab Release"
message={`Sonarr was unable to determine which series and episode this release was for. Sonarr may be unable to automatically import this release. Do you want to grab '${title}'?`}
message={`Radarr was unable to determine which movie this release was for. Radarr may be unable to automatically import this release. Do you want to grab '${title}'?`}
confirmLabel="Grab"
onConfirm={this.onGrabConfirm}
onCancel={this.onGrabCancel}
@@ -233,6 +242,7 @@ InteractiveSearchRow.propTypes = {
seeders: PropTypes.number,
leechers: PropTypes.number,
quality: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
rejections: PropTypes.arrayOf(PropTypes.string).isRequired,
downloadAllowed: PropTypes.bool.isRequired,
isGrabbing: PropTypes.bool.isRequired,

View File

@@ -0,0 +1,16 @@
import React from 'react';
import InteractiveSearchContentConnector from './InteractiveSearchContentConnector';
function InteractiveSearchTable(props) {
return (
<InteractiveSearchContentConnector
searchPayload={props}
/>
);
}
InteractiveSearchTable.propTypes = {
};
export default InteractiveSearchTable;