From 03b8c4c28e1d0a5d5caa4c6f4dd04a7edf5c4a17 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 7 Dec 2024 19:28:58 -0800 Subject: [PATCH] Convert EpisodeSearch to TypeScript --- .../Episode/EpisodeDetailsModalContent.tsx | 4 +- frontend/src/Episode/Search/EpisodeSearch.js | 56 ----------- frontend/src/Episode/Search/EpisodeSearch.tsx | 80 ++++++++++++++++ .../Episode/Search/EpisodeSearchConnector.js | 93 ------------------- 4 files changed, 82 insertions(+), 151 deletions(-) delete mode 100644 frontend/src/Episode/Search/EpisodeSearch.js create mode 100644 frontend/src/Episode/Search/EpisodeSearch.tsx delete mode 100644 frontend/src/Episode/Search/EpisodeSearchConnector.js diff --git a/frontend/src/Episode/EpisodeDetailsModalContent.tsx b/frontend/src/Episode/EpisodeDetailsModalContent.tsx index 05a08f16f..75c8bef73 100644 --- a/frontend/src/Episode/EpisodeDetailsModalContent.tsx +++ b/frontend/src/Episode/EpisodeDetailsModalContent.tsx @@ -20,7 +20,7 @@ import { } from 'Store/Actions/releaseActions'; import translate from 'Utilities/String/translate'; import EpisodeHistoryConnector from './History/EpisodeHistoryConnector'; -import EpisodeSearchConnector from './Search/EpisodeSearchConnector'; +import EpisodeSearch from './Search/EpisodeSearch'; import SeasonEpisodeNumber from './SeasonEpisodeNumber'; import EpisodeSummary from './Summary/EpisodeSummary'; import styles from './EpisodeDetailsModalContent.css'; @@ -174,7 +174,7 @@ function EpisodeDetailsModalContent(props: EpisodeDetailsModalContentProps) { {/* Don't wrap in tabContent so we not have a top margin */} - -
- -
- -
- -
- - ); -} - -EpisodeSearch.propTypes = { - onQuickSearchPress: PropTypes.func.isRequired, - onInteractiveSearchPress: PropTypes.func.isRequired -}; - -export default EpisodeSearch; diff --git a/frontend/src/Episode/Search/EpisodeSearch.tsx b/frontend/src/Episode/Search/EpisodeSearch.tsx new file mode 100644 index 000000000..818bb5d54 --- /dev/null +++ b/frontend/src/Episode/Search/EpisodeSearch.tsx @@ -0,0 +1,80 @@ +import React, { useCallback, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState from 'App/State/AppState'; +import * as commandNames from 'Commands/commandNames'; +import Icon from 'Components/Icon'; +import Button from 'Components/Link/Button'; +import { icons, kinds, sizes } from 'Helpers/Props'; +import InteractiveSearch from 'InteractiveSearch/InteractiveSearch'; +import { executeCommand } from 'Store/Actions/commandActions'; +import translate from 'Utilities/String/translate'; +import styles from './EpisodeSearch.css'; + +interface EpisodeSearchProps { + episodeId: number; + startInteractiveSearch: boolean; + onModalClose: () => void; +} + +function EpisodeSearch({ + episodeId, + startInteractiveSearch, + onModalClose, +}: EpisodeSearchProps) { + const dispatch = useDispatch(); + const { isPopulated } = useSelector((state: AppState) => state.releases); + + const [isInteractiveSearchOpen, setIsInteractiveSearchOpen] = useState( + startInteractiveSearch || isPopulated + ); + + const handleQuickSearchPress = useCallback(() => { + dispatch( + executeCommand({ + name: commandNames.EPISODE_SEARCH, + episodeIds: [episodeId], + }) + ); + + onModalClose(); + }, [episodeId, dispatch, onModalClose]); + + const handleInteractiveSearchPress = useCallback(() => { + setIsInteractiveSearchOpen(true); + }, []); + + if (isInteractiveSearchOpen) { + return ; + } + + return ( +
+
+ +
+ +
+ +
+
+ ); +} + +export default EpisodeSearch; diff --git a/frontend/src/Episode/Search/EpisodeSearchConnector.js b/frontend/src/Episode/Search/EpisodeSearchConnector.js deleted file mode 100644 index 9b41dd9c4..000000000 --- a/frontend/src/Episode/Search/EpisodeSearchConnector.js +++ /dev/null @@ -1,93 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import * as commandNames from 'Commands/commandNames'; -import InteractiveSearch from 'InteractiveSearch/InteractiveSearch'; -import { executeCommand } from 'Store/Actions/commandActions'; -import EpisodeSearch from './EpisodeSearch'; - -function createMapStateToProps() { - return createSelector( - (state) => state.releases, - (releases) => { - return { - isPopulated: releases.isPopulated - }; - } - ); -} - -const mapDispatchToProps = { - executeCommand -}; - -class EpisodeSearchConnector extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - isInteractiveSearchOpen: props.startInteractiveSearch - }; - } - - componentDidMount() { - if (this.props.isPopulated) { - this.setState({ isInteractiveSearchOpen: true }); - } - } - - // - // Listeners - - onQuickSearchPress = () => { - this.props.executeCommand({ - name: commandNames.EPISODE_SEARCH, - episodeIds: [this.props.episodeId] - }); - - this.props.onModalClose(); - }; - - onInteractiveSearchPress = () => { - this.setState({ isInteractiveSearchOpen: true }); - }; - - // - // Render - - render() { - const { episodeId } = this.props; - - if (this.state.isInteractiveSearchOpen) { - return ( - - ); - } - - return ( - - ); - } -} - -EpisodeSearchConnector.propTypes = { - episodeId: PropTypes.number.isRequired, - isPopulated: PropTypes.bool.isRequired, - startInteractiveSearch: PropTypes.bool.isRequired, - onModalClose: PropTypes.func.isRequired, - executeCommand: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(EpisodeSearchConnector);