From 653b358fd3baa72954d6a7df3f5f71ef4668f386 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sat, 8 Mar 2025 16:36:12 +0200 Subject: [PATCH] Convert Delete Movie Modal to TypeScript Co-authored-by: Mark McDowall --- frontend/src/Movie/Delete/DeleteMovieModal.js | 37 ---- .../src/Movie/Delete/DeleteMovieModal.tsx | 24 +++ .../Movie/Delete/DeleteMovieModalContent.js | 163 ------------------ .../Movie/Delete/DeleteMovieModalContent.tsx | 149 ++++++++++++++++ .../DeleteMovieModalContentConnector.js | 45 ----- frontend/src/Movie/Details/MovieDetails.js | 1 - frontend/src/Movie/Movie.ts | 3 +- 7 files changed, 175 insertions(+), 247 deletions(-) delete mode 100644 frontend/src/Movie/Delete/DeleteMovieModal.js create mode 100644 frontend/src/Movie/Delete/DeleteMovieModal.tsx delete mode 100644 frontend/src/Movie/Delete/DeleteMovieModalContent.js create mode 100644 frontend/src/Movie/Delete/DeleteMovieModalContent.tsx delete mode 100644 frontend/src/Movie/Delete/DeleteMovieModalContentConnector.js diff --git a/frontend/src/Movie/Delete/DeleteMovieModal.js b/frontend/src/Movie/Delete/DeleteMovieModal.js deleted file mode 100644 index 5497e39527..0000000000 --- a/frontend/src/Movie/Delete/DeleteMovieModal.js +++ /dev/null @@ -1,37 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Modal from 'Components/Modal/Modal'; -import { sizes } from 'Helpers/Props'; -import DeleteMovieModalContentConnector from './DeleteMovieModalContentConnector'; - -function DeleteMovieModal(props) { - const { - isOpen, - onModalClose, - previousMovie, - ...otherProps - } = props; - - return ( - - - - ); -} - -DeleteMovieModal.propTypes = { - ...DeleteMovieModalContentConnector.propTypes, - isOpen: PropTypes.bool.isRequired, - onModalClose: PropTypes.func.isRequired, - previousMovie: PropTypes.string -}; - -export default DeleteMovieModal; diff --git a/frontend/src/Movie/Delete/DeleteMovieModal.tsx b/frontend/src/Movie/Delete/DeleteMovieModal.tsx new file mode 100644 index 0000000000..6daffefb49 --- /dev/null +++ b/frontend/src/Movie/Delete/DeleteMovieModal.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import Modal from 'Components/Modal/Modal'; +import { sizes } from 'Helpers/Props'; +import DeleteMovieModalContent, { + DeleteMovieModalContentProps, +} from './DeleteMovieModalContent'; + +interface DeleteMovieModalProps extends DeleteMovieModalContentProps { + isOpen: boolean; +} + +function DeleteMovieModal({ + isOpen, + onModalClose, + ...otherProps +}: DeleteMovieModalProps) { + return ( + + + + ); +} + +export default DeleteMovieModal; diff --git a/frontend/src/Movie/Delete/DeleteMovieModalContent.js b/frontend/src/Movie/Delete/DeleteMovieModalContent.js deleted file mode 100644 index 68ee5d00bd..0000000000 --- a/frontend/src/Movie/Delete/DeleteMovieModalContent.js +++ /dev/null @@ -1,163 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import FormGroup from 'Components/Form/FormGroup'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import FormLabel from 'Components/Form/FormLabel'; -import Icon from 'Components/Icon'; -import Button from 'Components/Link/Button'; -import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; -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 { icons, inputTypes, kinds } from 'Helpers/Props'; -import formatBytes from 'Utilities/Number/formatBytes'; -import translate from 'Utilities/String/translate'; -import styles from './DeleteMovieModalContent.css'; - -class DeleteMovieModalContent extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - deleteFiles: false - }; - } - - // - // Listeners - - onDeleteFilesChange = ({ value }) => { - this.setState({ deleteFiles: value }); - }; - - onDeleteMovieConfirmed = () => { - const deleteFiles = this.state.deleteFiles; - const addImportExclusion = this.props.deleteOptions.addImportExclusion; - - this.setState({ deleteFiles: false }); - this.props.onDeletePress(deleteFiles, addImportExclusion); - }; - - // - // Render - - render() { - const { - title, - path, - statistics = {}, - deleteOptions, - onModalClose, - onDeleteOptionChange - } = this.props; - - const { - movieFileCount = 0, - sizeOnDisk = 0 - } = statistics; - - const deleteFiles = this.state.deleteFiles; - const addImportExclusion = deleteOptions.addImportExclusion; - - return ( - - - {translate('DeleteHeader', { title })} - - - -
- - - {path} -
- - - - {translate('AddListExclusion')} - - - - - - - {movieFileCount === 0 ? translate('DeleteMovieFolder') : translate('DeleteMovieFiles', { movieFileCount })} - - - - - { - deleteFiles ? -
-
- - { - movieFileCount ? -
- {translate('DeleteMovieFolderMovieCount', { movieFileCount, size: formatBytes(sizeOnDisk) })} -
: - null - } -
: - null - } - -
- - - - - - -
- ); - } -} - -DeleteMovieModalContent.propTypes = { - title: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - statistics: PropTypes.object.isRequired, - hasFile: PropTypes.bool.isRequired, - deleteOptions: PropTypes.object.isRequired, - onDeleteOptionChange: PropTypes.func.isRequired, - onDeletePress: PropTypes.func.isRequired, - onModalClose: PropTypes.func.isRequired -}; - -DeleteMovieModalContent.defaultProps = { - statistics: {} -}; - -export default DeleteMovieModalContent; diff --git a/frontend/src/Movie/Delete/DeleteMovieModalContent.tsx b/frontend/src/Movie/Delete/DeleteMovieModalContent.tsx new file mode 100644 index 0000000000..ebd850d8aa --- /dev/null +++ b/frontend/src/Movie/Delete/DeleteMovieModalContent.tsx @@ -0,0 +1,149 @@ +import React, { useCallback, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState from 'App/State/AppState'; +import FormGroup from 'Components/Form/FormGroup'; +import FormInputGroup from 'Components/Form/FormInputGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import Icon from 'Components/Icon'; +import Button from 'Components/Link/Button'; +import InlineMarkdown from 'Components/Markdown/InlineMarkdown'; +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 { icons, inputTypes, kinds } from 'Helpers/Props'; +import { Statistics } from 'Movie/Movie'; +import useMovie from 'Movie/useMovie'; +import { deleteMovie, setDeleteOption } from 'Store/Actions/movieActions'; +import { CheckInputChanged } from 'typings/inputs'; +import formatBytes from 'Utilities/Number/formatBytes'; +import translate from 'Utilities/String/translate'; +import styles from './DeleteMovieModalContent.css'; + +export interface DeleteMovieModalContentProps { + movieId: number; + onModalClose: () => void; +} + +function DeleteMovieModalContent({ + movieId, + onModalClose, +}: DeleteMovieModalContentProps) { + const dispatch = useDispatch(); + const { + title, + path, + collection, + statistics = {} as Statistics, + } = useMovie(movieId)!; + const { addImportExclusion } = useSelector( + (state: AppState) => state.movies.deleteOptions + ); + + const { movieFileCount = 0, sizeOnDisk = 0 } = statistics; + + const [deleteFiles, setDeleteFiles] = useState(false); + + const handleDeleteFilesChange = useCallback( + ({ value }: CheckInputChanged) => { + setDeleteFiles(value); + }, + [] + ); + + const handleDeleteMovieConfirmed = useCallback(() => { + dispatch( + deleteMovie({ + id: movieId, + collectionTmdbId: collection?.tmdbId, + deleteFiles, + addImportExclusion, + }) + ); + }, [movieId, collection, addImportExclusion, deleteFiles, dispatch]); + + const handleDeleteOptionChange = useCallback( + ({ name, value }: CheckInputChanged) => { + dispatch(setDeleteOption({ [name]: value })); + }, + [dispatch] + ); + + return ( + + {translate('DeleteHeader', { title })} + + +
+ + + {path} +
+ + + {translate('AddListExclusion')} + + + + + + + {movieFileCount === 0 + ? translate('DeleteMovieFolder') + : translate('DeleteMovieFiles', { movieFileCount })} + + + + + + {deleteFiles ? ( +
+
+ +
+ + {movieFileCount ? ( +
+ {translate('DeleteMovieFolderMovieCount', { + movieFileCount, + size: formatBytes(sizeOnDisk), + })} +
+ ) : null} +
+ ) : null} +
+ + + + + + +
+ ); +} + +export default DeleteMovieModalContent; diff --git a/frontend/src/Movie/Delete/DeleteMovieModalContentConnector.js b/frontend/src/Movie/Delete/DeleteMovieModalContentConnector.js deleted file mode 100644 index 93cc3928ca..0000000000 --- a/frontend/src/Movie/Delete/DeleteMovieModalContentConnector.js +++ /dev/null @@ -1,45 +0,0 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { deleteMovie, setDeleteOption } from 'Store/Actions/movieActions'; -import createMovieSelector from 'Store/Selectors/createMovieSelector'; -import DeleteMovieModalContent from './DeleteMovieModalContent'; - -function createMapStateToProps() { - return createSelector( - (state) => state.movies.deleteOptions, - createMovieSelector(), - (deleteOptions, movie) => { - return { - ...movie, - deleteOptions - }; - } - ); -} - -function createMapDispatchToProps(dispatch, props) { - return { - onDeleteOptionChange(option) { - dispatch( - setDeleteOption({ - [option.name]: option.value - }) - ); - }, - - onDeletePress(deleteFiles, addImportExclusion) { - dispatch( - deleteMovie({ - id: props.movieId, - collectionTmdbId: this.collection?.tmdbId, - deleteFiles, - addImportExclusion - }) - ); - - props.onModalClose(true); - } - }; -} - -export default connect(createMapStateToProps, createMapDispatchToProps)(DeleteMovieModalContent); diff --git a/frontend/src/Movie/Details/MovieDetails.js b/frontend/src/Movie/Details/MovieDetails.js index 6b23dee7e3..1b01f93d93 100644 --- a/frontend/src/Movie/Details/MovieDetails.js +++ b/frontend/src/Movie/Details/MovieDetails.js @@ -747,7 +747,6 @@ class MovieDetails extends Component { isOpen={isDeleteMovieModalOpen} movieId={id} onModalClose={this.onDeleteMovieModalClose} - nextMovieRelativePath={`/movie/${nextMovie.titleSlug}`} />