diff --git a/frontend/src/InteractiveSearch/InteractiveSearchContent.css b/frontend/src/InteractiveSearch/InteractiveSearch.css
similarity index 100%
rename from frontend/src/InteractiveSearch/InteractiveSearchContent.css
rename to frontend/src/InteractiveSearch/InteractiveSearch.css
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchContent.css.d.ts b/frontend/src/InteractiveSearch/InteractiveSearch.css.d.ts
similarity index 100%
rename from frontend/src/InteractiveSearch/InteractiveSearchContent.css.d.ts
rename to frontend/src/InteractiveSearch/InteractiveSearch.css.d.ts
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchContent.js b/frontend/src/InteractiveSearch/InteractiveSearch.js
similarity index 80%
rename from frontend/src/InteractiveSearch/InteractiveSearchContent.js
rename to frontend/src/InteractiveSearch/InteractiveSearch.js
index a4544514d..d3fa3ca14 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearchContent.js
+++ b/frontend/src/InteractiveSearch/InteractiveSearch.js
@@ -3,13 +3,16 @@ import React, { Fragment } from 'react';
import Alert from 'Components/Alert';
import Icon from 'Components/Icon';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
+import FilterMenu from 'Components/Menu/FilterMenu';
+import PageMenuButton from 'Components/Menu/PageMenuButton';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
-import { icons, kinds, sortDirections } from 'Helpers/Props';
+import { align, icons, kinds, sortDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
+import InteractiveSearchFilterModalConnector from './InteractiveSearchFilterModalConnector';
import InteractiveSearchRowConnector from './InteractiveSearchRowConnector';
-import styles from './InteractiveSearchContent.css';
+import styles from './InteractiveSearch.css';
const columns = [
{
@@ -24,23 +27,6 @@ const columns = [
isSortable: true,
isVisible: true
},
- {
- name: 'releaseWeight',
- label: React.createElement(Icon, { name: icons.DOWNLOAD }),
- isSortable: true,
- fixedSortDirection: sortDirections.ASCENDING,
- isVisible: true
- },
- {
- name: 'rejections',
- label: React.createElement(Icon, {
- name: icons.DANGER,
- title: () => translate('Rejections')
- }),
- isSortable: true,
- fixedSortDirection: sortDirections.ASCENDING,
- isVisible: true
- },
{
name: 'title',
label: () => translate('Title'),
@@ -84,12 +70,6 @@ const columns = [
isSortable: true,
isVisible: true
},
- {
- name: 'customFormat',
- label: () => translate('Formats'),
- isSortable: true,
- isVisible: true
- },
{
name: 'customFormatScore',
label: React.createElement(Icon, {
@@ -107,10 +87,27 @@ const columns = [
}),
isSortable: true,
isVisible: true
+ },
+ {
+ name: 'releaseWeight',
+ label: React.createElement(Icon, { name: icons.DOWNLOAD }),
+ isSortable: true,
+ fixedSortDirection: sortDirections.ASCENDING,
+ isVisible: true
+ },
+ {
+ name: 'rejections',
+ label: React.createElement(Icon, {
+ name: icons.DANGER,
+ title: () => translate('Rejections')
+ }),
+ isSortable: true,
+ fixedSortDirection: sortDirections.ASCENDING,
+ isVisible: true
}
];
-function InteractiveSearchContent(props) {
+function InteractiveSearch(props) {
const {
searchPayload,
isFetching,
@@ -118,18 +115,36 @@ function InteractiveSearchContent(props) {
error,
totalReleasesCount,
items,
+ selectedFilterKey,
+ filters,
+ customFilters,
sortKey,
sortDirection,
longDateFormat,
timeFormat,
onSortPress,
+ onFilterSelect,
onGrabPress
} = props;
const errorMessage = getErrorMessage(error);
+ const type = 'movies';
return (
+
+
+
+
{
isFetching ?
: null
}
@@ -203,19 +218,23 @@ function InteractiveSearchContent(props) {
);
}
-InteractiveSearchContent.propTypes = {
+InteractiveSearch.propTypes = {
searchPayload: PropTypes.object.isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
totalReleasesCount: PropTypes.number.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
+ selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ filters: PropTypes.arrayOf(PropTypes.object).isRequired,
+ customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.string,
longDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
onSortPress: PropTypes.func.isRequired,
+ onFilterSelect: PropTypes.func.isRequired,
onGrabPress: PropTypes.func.isRequired
};
-export default InteractiveSearchContent;
+export default InteractiveSearch;
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchContentConnector.js b/frontend/src/InteractiveSearch/InteractiveSearchConnector.js
similarity index 76%
rename from frontend/src/InteractiveSearch/InteractiveSearchContentConnector.js
rename to frontend/src/InteractiveSearch/InteractiveSearchConnector.js
index f4432d9e3..17b4d8743 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearchContentConnector.js
+++ b/frontend/src/InteractiveSearch/InteractiveSearchConnector.js
@@ -5,7 +5,7 @@ import { createSelector } from 'reselect';
import * as releaseActions from 'Store/Actions/releaseActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
-import InteractiveSearchContent from './InteractiveSearchContent';
+import InteractiveSearch from './InteractiveSearch';
function createMapStateToProps(appState) {
return createSelector(
@@ -29,17 +29,12 @@ 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 = releaseActions.setReleasesFilter;
- dispatch(action({ selectedFilterKey }));
+ dispatch(releaseActions.setReleasesFilter({ selectedFilterKey }));
},
onGrabPress(payload) {
@@ -48,7 +43,7 @@ function createMapDispatchToProps(dispatch, props) {
};
}
-class InteractiveSearchContentConnector extends Component {
+class InteractiveSearchConnector extends Component {
//
// Lifecycle
@@ -73,24 +68,22 @@ class InteractiveSearchContentConnector extends Component {
render() {
const {
dispatchFetchReleases,
- dispatchClearReleases,
...otherProps
} = this.props;
return (
-
);
}
}
-InteractiveSearchContentConnector.propTypes = {
+InteractiveSearchConnector.propTypes = {
searchPayload: PropTypes.object.isRequired,
isPopulated: PropTypes.bool.isRequired,
- dispatchFetchReleases: PropTypes.func.isRequired,
- dispatchClearReleases: PropTypes.func.isRequired
+ dispatchFetchReleases: PropTypes.func.isRequired
};
-export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchContentConnector);
+export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchConnector);
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js b/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js
index 10fc32c93..1a3081ad1 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js
+++ b/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js
@@ -4,7 +4,7 @@ import FilterMenu from 'Components/Menu/FilterMenu';
import PageMenuButton from 'Components/Menu/PageMenuButton';
import { align } from 'Helpers/Props';
import InteractiveSearchFilterModalConnector from './InteractiveSearchFilterModalConnector';
-import styles from './InteractiveSearchContent.css';
+import styles from './InteractiveSearch.css';
function InteractiveSearchFilterMenu(props) {
const {
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchRow.css b/frontend/src/InteractiveSearch/InteractiveSearchRow.css
index 74a84defb..929f22ab3 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearchRow.css
+++ b/frontend/src/InteractiveSearch/InteractiveSearchRow.css
@@ -1,23 +1,25 @@
-.cell {
- composes: cell from '~Components/Table/Cells/TableRowCell.css';
-}
-
.protocol {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 80px;
}
+.titleContent {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ word-break: break-all;
+}
+
.indexer {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 85px;
}
.quality,
-.customFormat,
.languages {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
}
.languages {
@@ -25,7 +27,7 @@
}
.customFormatScore {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 55px;
font-weight: bold;
@@ -33,31 +35,28 @@
}
.rejected,
-.indexerFlags {
- composes: cell;
+.indexerFlags,
+.download {
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 50px;
}
.age,
.size {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
white-space: nowrap;
}
.peers {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 75px;
}
-.titleContent {
- overflow-wrap: break-word;
-}
-
.history {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 75px;
}
@@ -67,7 +66,7 @@
}
.download {
- composes: cell;
+ composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 80px;
}
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchRow.css.d.ts b/frontend/src/InteractiveSearch/InteractiveSearchRow.css.d.ts
index 6b8ab7ae6..723fbbc15 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearchRow.css.d.ts
+++ b/frontend/src/InteractiveSearch/InteractiveSearchRow.css.d.ts
@@ -3,8 +3,6 @@
interface CssExports {
'age': string;
'blocklist': string;
- 'cell': string;
- 'customFormat': string;
'customFormatScore': string;
'download': string;
'downloadIcon': string;
diff --git a/frontend/src/InteractiveSearch/InteractiveSearchRow.tsx b/frontend/src/InteractiveSearch/InteractiveSearchRow.tsx
index aca3e910f..35be786dd 100644
--- a/frontend/src/InteractiveSearch/InteractiveSearchRow.tsx
+++ b/frontend/src/InteractiveSearch/InteractiveSearchRow.tsx
@@ -199,53 +199,6 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
{formatAge(age, ageHours, ageMinutes)}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {rejections.length ? (
- }
- title={translate('ReleaseRejected')}
- body={
-
- {rejections.map((rejection, index) => {
- return - {rejection}
;
- })}
-
- }
- position={tooltipPositions.RIGHT}
- />
- ) : null}
-
-
@@ -316,10 +269,6 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
-
-
-
-
+
+ {rejections.length ? (
+ }
+ title={translate('ReleaseRejected')}
+ body={
+
+ {rejections.map((rejection, index) => {
+ return - {rejection}
;
+ })}
+
+ }
+ position={tooltipPositions.LEFT}
+ />
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- );
-}
-
-InteractiveSearchTable.propTypes = {
-};
-
-export default InteractiveSearchTable;
diff --git a/frontend/src/Movie/Details/MovieDetails.js b/frontend/src/Movie/Details/MovieDetails.js
index fdde4d345..730e581f7 100644
--- a/frontend/src/Movie/Details/MovieDetails.js
+++ b/frontend/src/Movie/Details/MovieDetails.js
@@ -23,12 +23,11 @@ import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip';
import { icons, kinds, sizes, tooltipPositions } from 'Helpers/Props';
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
-import InteractiveSearchFilterMenuConnector from 'InteractiveSearch/InteractiveSearchFilterMenuConnector';
-import InteractiveSearchTable from 'InteractiveSearch/InteractiveSearchTable';
import DeleteMovieModal from 'Movie/Delete/DeleteMovieModal';
import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector';
import MovieHistoryTable from 'Movie/History/MovieHistoryTable';
import MoviePoster from 'Movie/MoviePoster';
+import MovieInteractiveSearchModalConnector from 'Movie/Search/MovieInteractiveSearchModalConnector';
import MovieFileEditorTable from 'MovieFile/Editor/MovieFileEditorTable';
import ExtraFileTable from 'MovieFile/Extras/ExtraFileTable';
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
@@ -78,6 +77,7 @@ class MovieDetails extends Component {
isEditMovieModalOpen: false,
isDeleteMovieModalOpen: false,
isInteractiveImportModalOpen: false,
+ isInteractiveSearchModalOpen: false,
allExpanded: false,
allCollapsed: false,
expandedState: {},
@@ -134,6 +134,14 @@ class MovieDetails extends Component {
this.setState({ isEditMovieModalOpen: false });
};
+ onInteractiveSearchPress = () => {
+ this.setState({ isInteractiveSearchModalOpen: true });
+ };
+
+ onInteractiveSearchModalClose = () => {
+ this.setState({ isInteractiveSearchModalOpen: false });
+ };
+
onDeleteMoviePress = () => {
this.setState({
isEditMovieModalOpen: false,
@@ -295,6 +303,7 @@ class MovieDetails extends Component {
isEditMovieModalOpen,
isDeleteMovieModalOpen,
isInteractiveImportModalOpen,
+ isInteractiveSearchModalOpen,
overviewHeight,
titleWidth,
selectedTabIndex
@@ -324,6 +333,14 @@ class MovieDetails extends Component {
onPress={onSearchPress}
/>
+
+
-
- {translate('Search')}
-
-
- {
- selectedTabIndex === 1 &&
-
-
-
- }
-
@@ -715,12 +718,6 @@ class MovieDetails extends Component {
/>
-
-
-
-
+
+
);
diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModal.js b/frontend/src/Movie/Search/MovieInteractiveSearchModal.js
new file mode 100644
index 000000000..ec8987dfa
--- /dev/null
+++ b/frontend/src/Movie/Search/MovieInteractiveSearchModal.js
@@ -0,0 +1,35 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Modal from 'Components/Modal/Modal';
+import { sizes } from 'Helpers/Props';
+import MovieInteractiveSearchModalContent from './MovieInteractiveSearchModalContent';
+
+function MovieInteractiveSearchModal(props) {
+ const {
+ isOpen,
+ movieId,
+ onModalClose
+ } = props;
+
+ return (
+
+
+
+ );
+}
+
+MovieInteractiveSearchModal.propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ movieId: PropTypes.number.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default MovieInteractiveSearchModal;
diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModalConnector.js b/frontend/src/Movie/Search/MovieInteractiveSearchModalConnector.js
new file mode 100644
index 000000000..f00b1cb4d
--- /dev/null
+++ b/frontend/src/Movie/Search/MovieInteractiveSearchModalConnector.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions';
+import MovieInteractiveSearchModal from './MovieInteractiveSearchModal';
+
+function createMapDispatchToProps(dispatch, props) {
+ return {
+ onModalClose() {
+ dispatch(cancelFetchReleases());
+ dispatch(clearReleases());
+ props.onModalClose();
+ }
+ };
+}
+
+export default connect(null, createMapDispatchToProps)(MovieInteractiveSearchModal);
diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.js b/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.js
new file mode 100644
index 000000000..dfcabc73e
--- /dev/null
+++ b/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.js
@@ -0,0 +1,44 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Button from 'Components/Link/Button';
+import ModalBody from 'Components/Modal/ModalBody';
+import ModalContent from 'Components/Modal/ModalContent';
+import ModalFooter from 'Components/Modal/ModalFooter';
+import ModalHeader from 'Components/Modal/ModalHeader';
+import { scrollDirections } from 'Helpers/Props';
+import InteractiveSearchConnector from 'InteractiveSearch/InteractiveSearchConnector';
+import translate from 'Utilities/String/translate';
+
+function MovieInteractiveSearchModalContent(props) {
+ const {
+ movieId,
+ onModalClose
+ } = props;
+
+ return (
+
+
+ {translate('InteractiveSearchModalHeader')}
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+MovieInteractiveSearchModalContent.propTypes = {
+ movieId: PropTypes.number.isRequired,
+ onModalClose: PropTypes.func.isRequired
+};
+
+export default MovieInteractiveSearchModalContent;
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index afe21df42..3e6a2bd9b 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -556,6 +556,7 @@
"InteractiveImportNoMovie": "Movie must be chosen for each selected file",
"InteractiveImportNoQuality": "Quality must be chosen for each selected file",
"InteractiveSearch": "Interactive Search",
+ "InteractiveSearchModalHeader": "Interactive Search",
"InteractiveSearchResultsFailedErrorMessage": "Search failed because its {message}. Try refreshing the movie info and verify the necessary information is present before searching again.",
"Interval": "Interval",
"InvalidFormat": "Invalid Format",