diff --git a/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js b/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js deleted file mode 100644 index 1a3081ad1..000000000 --- a/frontend/src/InteractiveSearch/InteractiveSearchFilterMenu.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import FilterMenu from 'Components/Menu/FilterMenu'; -import PageMenuButton from 'Components/Menu/PageMenuButton'; -import { align } from 'Helpers/Props'; -import InteractiveSearchFilterModalConnector from './InteractiveSearchFilterModalConnector'; -import styles from './InteractiveSearch.css'; - -function InteractiveSearchFilterMenu(props) { - const { - selectedFilterKey, - filters, - customFilters, - onFilterSelect - } = props; - - return ( -
- -
- ); -} - -InteractiveSearchFilterMenu.propTypes = { - selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - filters: PropTypes.arrayOf(PropTypes.object).isRequired, - customFilters: PropTypes.arrayOf(PropTypes.object).isRequired, - onFilterSelect: PropTypes.func.isRequired -}; - -export default InteractiveSearchFilterMenu; diff --git a/frontend/src/InteractiveSearch/InteractiveSearchFilterMenuConnector.js b/frontend/src/InteractiveSearch/InteractiveSearchFilterMenuConnector.js deleted file mode 100644 index d3156aabc..000000000 --- a/frontend/src/InteractiveSearch/InteractiveSearchFilterMenuConnector.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { setReleasesFilter } from 'Store/Actions/releaseActions'; -import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; -import InteractiveSearchFilterMenu from './InteractiveSearchFilterMenu'; - -function createMapStateToProps(appState) { - return createSelector( - createClientSideCollectionSelector('releases'), - (releases) => { - return { - ...releases - }; - } - ); -} - -function createMapDispatchToProps(dispatch, props) { - return { - onFilterSelect(selectedFilterKey) { - dispatch(setReleasesFilter({ selectedFilterKey })); - } - }; -} - -class InteractiveSearchFilterMenuConnector extends Component { - - // - // Render - - render() { - const { - ...otherProps - } = this.props; - - return ( - - - ); - } -} - -export default connect(createMapStateToProps, createMapDispatchToProps)(InteractiveSearchFilterMenuConnector); diff --git a/frontend/src/Movie/Details/MovieDetails.js b/frontend/src/Movie/Details/MovieDetails.js index aa0134ad3..e609a0b4f 100644 --- a/frontend/src/Movie/Details/MovieDetails.js +++ b/frontend/src/Movie/Details/MovieDetails.js @@ -27,7 +27,7 @@ import EditMovieModalConnector from 'Movie/Edit/EditMovieModalConnector'; import getMovieStatusDetails from 'Movie/getMovieStatusDetails'; import MovieHistoryModal from 'Movie/History/MovieHistoryModal'; import MoviePoster from 'Movie/MoviePoster'; -import MovieInteractiveSearchModalConnector from 'Movie/Search/MovieInteractiveSearchModalConnector'; +import MovieInteractiveSearchModal from 'Movie/Search/MovieInteractiveSearchModal'; import MovieFileEditorTable from 'MovieFile/Editor/MovieFileEditorTable'; import ExtraFileTable from 'MovieFile/Extras/ExtraFileTable'; import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector'; @@ -740,7 +740,7 @@ class MovieDetails extends Component { onModalClose={this.onInteractiveImportModalClose} /> - { - this.setState({ isInteractiveSearchModalOpen: true }); - }; - - onInteractiveSearchModalClose = () => { - this.setState({ isInteractiveSearchModalOpen: false }); - }; - - // - // Render - - render() { - const { - movieId, - movieTitle, - isSearching, - onSearchPress, - ...otherProps - } = this.props; - - return ( - - - - - - - - ); - } -} - -MovieSearchCell.propTypes = { - movieId: PropTypes.number.isRequired, - movieTitle: PropTypes.string.isRequired, - isSearching: PropTypes.bool.isRequired, - onSearchPress: PropTypes.func.isRequired -}; - -export default MovieSearchCell; diff --git a/frontend/src/Movie/MovieSearchCell.tsx b/frontend/src/Movie/MovieSearchCell.tsx new file mode 100644 index 000000000..cc7f07152 --- /dev/null +++ b/frontend/src/Movie/MovieSearchCell.tsx @@ -0,0 +1,71 @@ +import React, { useCallback } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { MOVIE_SEARCH } from 'Commands/commandNames'; +import IconButton from 'Components/Link/IconButton'; +import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import useModalOpenState from 'Helpers/Hooks/useModalOpenState'; +import { icons } from 'Helpers/Props'; +import { executeCommand } from 'Store/Actions/commandActions'; +import createExecutingCommandsSelector from 'Store/Selectors/createExecutingCommandsSelector'; +import translate from 'Utilities/String/translate'; +import MovieInteractiveSearchModal from './Search/MovieInteractiveSearchModal'; +import styles from './MovieSearchCell.css'; + +interface MovieSearchCellProps { + movieId: number; + movieTitle: string; +} + +function MovieSearchCell(props: MovieSearchCellProps) { + const { movieId, movieTitle } = props; + + const executingCommands = useSelector(createExecutingCommandsSelector()); + const isSearching = executingCommands.some(({ name, body }) => { + const { movieIds = [] } = body; + return name === MOVIE_SEARCH && movieIds.indexOf(movieId) > -1; + }); + + const dispatch = useDispatch(); + + const [ + isInteractiveSearchModalOpen, + setInteractiveSearchModalOpen, + setInteractiveSearchModalClosed, + ] = useModalOpenState(false); + + const handleSearchPress = useCallback(() => { + dispatch( + executeCommand({ + name: MOVIE_SEARCH, + movieIds: [movieId], + }) + ); + }, [movieId, dispatch]); + + return ( + + + + + + + + ); +} + +export default MovieSearchCell; diff --git a/frontend/src/Movie/MovieSearchCellConnector.js b/frontend/src/Movie/MovieSearchCellConnector.js deleted file mode 100644 index 0d8bc34e4..000000000 --- a/frontend/src/Movie/MovieSearchCellConnector.js +++ /dev/null @@ -1,48 +0,0 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import * as commandNames from 'Commands/commandNames'; -import MovieSearchCell from 'Movie/MovieSearchCell'; -import { executeCommand } from 'Store/Actions/commandActions'; -import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; -import createMovieSelector from 'Store/Selectors/createMovieSelector'; -import { isCommandExecuting } from 'Utilities/Command'; - -function createMapStateToProps() { - return createSelector( - (state, { movieId }) => movieId, - createMovieSelector(), - createCommandsSelector(), - (movieId, movie, commands) => { - const isSearching = commands.some((command) => { - const movieSearch = command.name === commandNames.MOVIE_SEARCH; - - if (!movieSearch) { - return false; - } - - return ( - isCommandExecuting(command) && - command.body.movieIds.indexOf(movieId) > -1 - ); - }); - - return { - movieMonitored: movie.monitored, - isSearching - }; - } - ); -} - -function createMapDispatchToProps(dispatch, props) { - return { - onSearchPress(name, path) { - dispatch(executeCommand({ - name: commandNames.MOVIE_SEARCH, - movieIds: [props.movieId] - })); - } - }; -} - -export default connect(createMapStateToProps, createMapDispatchToProps)(MovieSearchCell); diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModal.js b/frontend/src/Movie/Search/MovieInteractiveSearchModal.js deleted file mode 100644 index b381ac563..000000000 --- a/frontend/src/Movie/Search/MovieInteractiveSearchModal.js +++ /dev/null @@ -1,38 +0,0 @@ -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, - movieTitle, - onModalClose - } = props; - - return ( - - - - ); -} - -MovieInteractiveSearchModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - movieId: PropTypes.number.isRequired, - movieTitle: PropTypes.string, - onModalClose: PropTypes.func.isRequired -}; - -export default MovieInteractiveSearchModal; diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModal.tsx b/frontend/src/Movie/Search/MovieInteractiveSearchModal.tsx new file mode 100644 index 000000000..5a4fb3a09 --- /dev/null +++ b/frontend/src/Movie/Search/MovieInteractiveSearchModal.tsx @@ -0,0 +1,46 @@ +import React, { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import Modal from 'Components/Modal/Modal'; +import { sizes } from 'Helpers/Props'; +import { + cancelFetchReleases, + clearReleases, +} from 'Store/Actions/releaseActions'; +import MovieInteractiveSearchModalContent from './MovieInteractiveSearchModalContent'; + +interface MovieInteractiveSearchModalProps { + isOpen: boolean; + movieId: number; + movieTitle?: string; + onModalClose(): void; +} + +function MovieInteractiveSearchModal(props: MovieInteractiveSearchModalProps) { + const { isOpen, movieId, movieTitle, onModalClose } = props; + + const dispatch = useDispatch(); + + const handleModalClose = useCallback(() => { + dispatch(cancelFetchReleases()); + dispatch(clearReleases()); + + onModalClose(); + }, [dispatch, onModalClose]); + + return ( + + + + ); +} + +export default MovieInteractiveSearchModal; diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModalConnector.js b/frontend/src/Movie/Search/MovieInteractiveSearchModalConnector.js deleted file mode 100644 index 9f53b712f..000000000 --- a/frontend/src/Movie/Search/MovieInteractiveSearchModalConnector.js +++ /dev/null @@ -1,59 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions'; -import MovieInteractiveSearchModal from './MovieInteractiveSearchModal'; - -function createMapDispatchToProps(dispatch, props) { - return { - dispatchCancelFetchReleases() { - dispatch(cancelFetchReleases()); - }, - - dispatchClearReleases() { - dispatch(clearReleases()); - }, - - onModalClose() { - dispatch(cancelFetchReleases()); - dispatch(clearReleases()); - props.onModalClose(); - } - }; -} - -class MovieInteractiveSearchModalConnector extends Component { - - // - // Lifecycle - - componentWillUnmount() { - this.props.dispatchCancelFetchReleases(); - this.props.dispatchClearReleases(); - } - - // - // Render - - render() { - const { - dispatchCancelFetchReleases, - dispatchClearReleases, - ...otherProps - } = this.props; - - return ( - - ); - } -} - -MovieInteractiveSearchModalConnector.propTypes = { - ...MovieInteractiveSearchModal.propTypes, - dispatchCancelFetchReleases: PropTypes.func.isRequired, - dispatchClearReleases: PropTypes.func.isRequired -}; - -export default connect(null, createMapDispatchToProps)(MovieInteractiveSearchModalConnector); diff --git a/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.tsx b/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.tsx index 4fbca8b47..a5a9db2e2 100644 --- a/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.tsx +++ b/frontend/src/Movie/Search/MovieInteractiveSearchModalContent.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; import Button from 'Components/Link/Button'; import ModalBody from 'Components/Modal/ModalBody'; import ModalContent from 'Components/Modal/ModalContent'; @@ -6,6 +7,10 @@ import ModalFooter from 'Components/Modal/ModalFooter'; import ModalHeader from 'Components/Modal/ModalHeader'; import { scrollDirections } from 'Helpers/Props'; import InteractiveSearchConnector from 'InteractiveSearch/InteractiveSearchConnector'; +import { + cancelFetchReleases, + clearReleases, +} from 'Store/Actions/releaseActions'; import translate from 'Utilities/String/translate'; interface MovieInteractiveSearchModalContentProps { @@ -19,6 +24,15 @@ function MovieInteractiveSearchModalContent( ) { const { movieId, movieTitle, onModalClose } = props; + const dispatch = useDispatch(); + + useEffect(() => { + return () => { + dispatch(cancelFetchReleases()); + dispatch(clearReleases()); + }; + }, [dispatch]); + return ( diff --git a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js index 31a4c50fb..e8e073a41 100644 --- a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js +++ b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js @@ -5,7 +5,7 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableRow from 'Components/Table/TableRow'; import movieEntities from 'Movie/movieEntities'; -import MovieSearchCellConnector from 'Movie/MovieSearchCellConnector'; +import MovieSearchCell from 'Movie/MovieSearchCell'; import MovieStatusConnector from 'Movie/MovieStatusConnector'; import MovieTitleLink from 'Movie/MovieTitleLink'; import MovieFileLanguageConnector from 'MovieFile/MovieFileLanguageConnector'; @@ -124,7 +124,7 @@ function CutoffUnmetRow(props) { if (name === 'actions') { return ( -