mirror of
https://github.com/Radarr/Radarr
synced 2025-12-06 16:32:36 +01:00
Convert Edit Movie Collection modal to TypeScript
This commit is contained in:
parent
1d1aca1a04
commit
ae5450f75d
14 changed files with 286 additions and 381 deletions
|
|
@ -7,6 +7,8 @@ interface MovieCollectionAppState
|
||||||
extends AppSectionState<MovieCollection>,
|
extends AppSectionState<MovieCollection>,
|
||||||
AppSectionSaveState {
|
AppSectionSaveState {
|
||||||
itemMap: Record<number, number>;
|
itemMap: Record<number, number>;
|
||||||
|
|
||||||
|
pendingChanges: Partial<MovieCollection>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MovieCollectionAppState;
|
export default MovieCollectionAppState;
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import EditCollectionModalContentConnector from './EditCollectionModalContentConnector';
|
|
||||||
|
|
||||||
function EditCollectionModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<EditCollectionModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditCollectionModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditCollectionModal;
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|
||||||
import EditCollectionModal from './EditCollectionModal';
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
clearPendingChanges
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditCollectionModalConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.clearPendingChanges({ section: 'movieCollections' });
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditCollectionModal
|
|
||||||
{...this.props}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditCollectionModalConnector.propTypes = {
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
clearPendingChanges: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(undefined, mapDispatchToProps)(EditCollectionModalConnector);
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Form from 'Components/Form/Form';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
|
||||||
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 { inputTypes } from 'Helpers/Props';
|
|
||||||
import MoviePoster from 'Movie/MoviePoster';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './EditCollectionModalContent.css';
|
|
||||||
|
|
||||||
class EditCollectionModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
const {
|
|
||||||
onSavePress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onSavePress(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
images,
|
|
||||||
overview,
|
|
||||||
item,
|
|
||||||
isSaving,
|
|
||||||
onInputChange,
|
|
||||||
onModalClose,
|
|
||||||
isSmallScreen,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
monitored,
|
|
||||||
qualityProfileId,
|
|
||||||
minimumAvailability,
|
|
||||||
// Id,
|
|
||||||
rootFolderPath,
|
|
||||||
tags,
|
|
||||||
searchOnAdd
|
|
||||||
} = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('Edit')} - {title}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div className={styles.container}>
|
|
||||||
{
|
|
||||||
!isSmallScreen &&
|
|
||||||
<div className={styles.poster}>
|
|
||||||
<MoviePoster
|
|
||||||
className={styles.poster}
|
|
||||||
images={images}
|
|
||||||
size={250}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className={styles.info}>
|
|
||||||
<div className={styles.overview}>
|
|
||||||
{overview}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Form
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Monitored')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="monitored"
|
|
||||||
helpText={translate('MonitoredCollectionHelpText')}
|
|
||||||
{...monitored}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('MinimumAvailability')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.AVAILABILITY_SELECT}
|
|
||||||
name="minimumAvailability"
|
|
||||||
{...minimumAvailability}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
|
||||||
name="qualityProfileId"
|
|
||||||
{...qualityProfileId}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RootFolder')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
|
||||||
name="rootFolderPath"
|
|
||||||
{...rootFolderPath}
|
|
||||||
includeMissingValue={true}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Tags')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TAG}
|
|
||||||
name="tags"
|
|
||||||
onChange={onInputChange}
|
|
||||||
{...tags}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('SearchOnAdd')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="searchOnAdd"
|
|
||||||
helpText={translate('SearchOnAddCollectionHelpText')}
|
|
||||||
{...searchOnAdd}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<SpinnerButton
|
|
||||||
isSpinning={isSaving}
|
|
||||||
onPress={this.onSavePress}
|
|
||||||
>
|
|
||||||
{translate('Save')}
|
|
||||||
</SpinnerButton>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditCollectionModalContent.propTypes = {
|
|
||||||
collectionId: PropTypes.number.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
overview: PropTypes.string.isRequired,
|
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
isPathChanging: PropTypes.bool.isRequired,
|
|
||||||
isSmallScreen: PropTypes.bool.isRequired,
|
|
||||||
onInputChange: PropTypes.func.isRequired,
|
|
||||||
onSavePress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditCollectionModalContent;
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { saveMovieCollection, setMovieCollectionValue } from 'Store/Actions/movieCollectionActions';
|
|
||||||
import createCollectionSelector from 'Store/Selectors/createCollectionSelector';
|
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
|
||||||
import selectSettings from 'Store/Selectors/selectSettings';
|
|
||||||
import EditCollectionModalContent from './EditCollectionModalContent';
|
|
||||||
|
|
||||||
function createIsPathChangingSelector() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.movieCollections.pendingChanges,
|
|
||||||
createCollectionSelector(),
|
|
||||||
(pendingChanges, collection) => {
|
|
||||||
const rootFolderPath = pendingChanges.rootFolderPath;
|
|
||||||
|
|
||||||
if (rootFolderPath == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return collection.rootFolderPath !== rootFolderPath;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.movieCollections,
|
|
||||||
createCollectionSelector(),
|
|
||||||
createIsPathChangingSelector(),
|
|
||||||
createDimensionsSelector(),
|
|
||||||
(moviesState, collection, isPathChanging, dimensions) => {
|
|
||||||
const {
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
pendingChanges
|
|
||||||
} = moviesState;
|
|
||||||
|
|
||||||
const movieSettings = {
|
|
||||||
monitored: collection.monitored,
|
|
||||||
qualityProfileId: collection.qualityProfileId,
|
|
||||||
minimumAvailability: collection.minimumAvailability,
|
|
||||||
rootFolderPath: collection.rootFolderPath,
|
|
||||||
tags: collection.tags,
|
|
||||||
searchOnAdd: collection.searchOnAdd
|
|
||||||
};
|
|
||||||
|
|
||||||
const settings = selectSettings(movieSettings, pendingChanges, saveError);
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: collection.title,
|
|
||||||
images: collection.images,
|
|
||||||
overview: collection.overview,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
isPathChanging,
|
|
||||||
originalPath: collection.path,
|
|
||||||
item: settings.settings,
|
|
||||||
isSmallScreen: dimensions.isSmallScreen,
|
|
||||||
...settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchSetMovieCollectionValue: setMovieCollectionValue,
|
|
||||||
dispatchSaveMovieCollection: saveMovieCollection
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditCollectionModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.props.dispatchSetMovieCollectionValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
this.props.dispatchSaveMovieCollection({
|
|
||||||
id: this.props.collectionId
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditCollectionModalContent
|
|
||||||
{...this.props}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
onMoveMoviePress={this.onMoveMoviePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditCollectionModalContentConnector.propTypes = {
|
|
||||||
collectionId: PropTypes.number,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
dispatchSetMovieCollectionValue: PropTypes.func.isRequired,
|
|
||||||
dispatchSaveMovieCollection: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditCollectionModalContentConnector);
|
|
||||||
36
frontend/src/Collection/Edit/EditMovieCollectionModal.tsx
Normal file
36
frontend/src/Collection/Edit/EditMovieCollectionModal.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||||
|
import EditMovieCollectionModalContent, {
|
||||||
|
EditMovieCollectionModalContentProps,
|
||||||
|
} from './EditMovieCollectionModalContent';
|
||||||
|
|
||||||
|
interface EditMovieCollectionModalProps
|
||||||
|
extends EditMovieCollectionModalContentProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditMovieCollectionModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: EditMovieCollectionModalProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleModalClose = useCallback(() => {
|
||||||
|
dispatch(clearPendingChanges({ section: 'movieCollections' }));
|
||||||
|
onModalClose();
|
||||||
|
}, [dispatch, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onModalClose={handleModalClose}>
|
||||||
|
<EditMovieCollectionModalContent
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={handleModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditMovieCollectionModal;
|
||||||
214
frontend/src/Collection/Edit/EditMovieCollectionModalContent.tsx
Normal file
214
frontend/src/Collection/Edit/EditMovieCollectionModalContent.tsx
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import useMovieCollection from 'Collection/useMovieCollection';
|
||||||
|
import Form from 'Components/Form/Form';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
|
||||||
|
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 usePrevious from 'Helpers/Hooks/usePrevious';
|
||||||
|
import { inputTypes } from 'Helpers/Props';
|
||||||
|
import MoviePoster from 'Movie/MoviePoster';
|
||||||
|
import {
|
||||||
|
saveMovieCollection,
|
||||||
|
setMovieCollectionValue,
|
||||||
|
} from 'Store/Actions/movieCollectionActions';
|
||||||
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
|
import selectSettings from 'Store/Selectors/selectSettings';
|
||||||
|
import { InputChanged } from 'typings/inputs';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './EditMovieCollectionModalContent.css';
|
||||||
|
|
||||||
|
export interface EditMovieCollectionModalContentProps {
|
||||||
|
collectionId: number;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditMovieCollectionModalContent({
|
||||||
|
collectionId,
|
||||||
|
onModalClose,
|
||||||
|
}: EditMovieCollectionModalContentProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
overview,
|
||||||
|
monitored,
|
||||||
|
qualityProfileId,
|
||||||
|
minimumAvailability,
|
||||||
|
rootFolderPath,
|
||||||
|
searchOnAdd,
|
||||||
|
images,
|
||||||
|
tags,
|
||||||
|
} = useMovieCollection(collectionId)!;
|
||||||
|
|
||||||
|
const { isSaving, saveError, pendingChanges } = useSelector(
|
||||||
|
(state: AppState) => state.movieCollections
|
||||||
|
);
|
||||||
|
const { isSmallScreen } = useSelector(createDimensionsSelector());
|
||||||
|
|
||||||
|
const wasSaving = usePrevious(isSaving);
|
||||||
|
|
||||||
|
const { settings, ...otherSettings } = useMemo(() => {
|
||||||
|
return selectSettings(
|
||||||
|
{
|
||||||
|
monitored,
|
||||||
|
minimumAvailability,
|
||||||
|
qualityProfileId,
|
||||||
|
rootFolderPath,
|
||||||
|
searchOnAdd,
|
||||||
|
tags,
|
||||||
|
},
|
||||||
|
pendingChanges,
|
||||||
|
saveError
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
monitored,
|
||||||
|
minimumAvailability,
|
||||||
|
qualityProfileId,
|
||||||
|
rootFolderPath,
|
||||||
|
searchOnAdd,
|
||||||
|
tags,
|
||||||
|
pendingChanges,
|
||||||
|
saveError,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleInputChange = useCallback(
|
||||||
|
({ name, value }: InputChanged) => {
|
||||||
|
// @ts-expect-error actions aren't typed
|
||||||
|
dispatch(setMovieCollectionValue({ name, value }));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSavePress = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
saveMovieCollection({
|
||||||
|
id: collectionId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [collectionId, dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isSaving && wasSaving && !saveError) {
|
||||||
|
onModalClose();
|
||||||
|
}
|
||||||
|
}, [isSaving, wasSaving, saveError, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{translate('EditMovieCollectionModalHeader', { title })}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div className={styles.container}>
|
||||||
|
{isSmallScreen ? null : (
|
||||||
|
<div className={styles.poster}>
|
||||||
|
<MoviePoster
|
||||||
|
className={styles.poster}
|
||||||
|
images={images}
|
||||||
|
size={250}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={styles.info}>
|
||||||
|
<div className={styles.overview}>{overview}</div>
|
||||||
|
|
||||||
|
<Form {...otherSettings}>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('Monitored')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="monitored"
|
||||||
|
helpText={translate('MonitoredCollectionHelpText')}
|
||||||
|
{...settings.monitored}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('MinimumAvailability')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.AVAILABILITY_SELECT}
|
||||||
|
name="minimumAvailability"
|
||||||
|
{...settings.minimumAvailability}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||||
|
name="qualityProfileId"
|
||||||
|
{...settings.qualityProfileId}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RootFolder')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||||
|
name="rootFolderPath"
|
||||||
|
{...settings.rootFolderPath}
|
||||||
|
includeMissingValue={true}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('Tags')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.TAG}
|
||||||
|
name="tags"
|
||||||
|
{...settings.tags}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('SearchOnAdd')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="searchOnAdd"
|
||||||
|
helpText={translate('SearchOnAddCollectionHelpText')}
|
||||||
|
{...settings.searchOnAdd}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||||
|
|
||||||
|
<SpinnerErrorButton
|
||||||
|
error={saveError}
|
||||||
|
isSpinning={isSaving}
|
||||||
|
onPress={handleSavePress}
|
||||||
|
>
|
||||||
|
{translate('Save')}
|
||||||
|
</SpinnerErrorButton>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditMovieCollectionModalContent;
|
||||||
|
|
@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
||||||
import TextTruncate from 'react-text-truncate';
|
import TextTruncate from 'react-text-truncate';
|
||||||
import { Navigation } from 'swiper';
|
import { Navigation } from 'swiper';
|
||||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||||
import EditCollectionModalConnector from 'Collection/Edit/EditCollectionModalConnector';
|
import EditMovieCollectionModal from 'Collection/Edit/EditMovieCollectionModal';
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
import CheckInput from 'Components/Form/CheckInput';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
|
|
@ -311,7 +311,7 @@ class CollectionOverview extends Component {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EditCollectionModalConnector
|
<EditMovieCollectionModal
|
||||||
isOpen={isEditCollectionModalOpen}
|
isOpen={isEditCollectionModalOpen}
|
||||||
collectionId={id}
|
collectionId={id}
|
||||||
onModalClose={this.onEditCollectionModalClose}
|
onModalClose={this.onEditCollectionModalClose}
|
||||||
|
|
|
||||||
21
frontend/src/Collection/useMovieCollection.ts
Normal file
21
frontend/src/Collection/useMovieCollection.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
|
||||||
|
export function createMovieCollectionSelector(collectionId?: number) {
|
||||||
|
return createSelector(
|
||||||
|
(state: AppState) => state.movieCollections.itemMap,
|
||||||
|
(state: AppState) => state.movieCollections.items,
|
||||||
|
(itemMap, allMovieCollections) => {
|
||||||
|
return collectionId
|
||||||
|
? allMovieCollections[itemMap[collectionId]]
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useMovieCollection(collectionId: number | undefined) {
|
||||||
|
return useSelector(createMovieCollectionSelector(collectionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useMovieCollection;
|
||||||
|
|
@ -9,6 +9,8 @@ export type MovieStatus =
|
||||||
| 'released'
|
| 'released'
|
||||||
| 'deleted';
|
| 'deleted';
|
||||||
|
|
||||||
|
export type MovieAvailability = 'announced' | 'inCinemas' | 'released';
|
||||||
|
|
||||||
export type CoverType = 'poster' | 'fanart' | 'headshot';
|
export type CoverType = 'poster' | 'fanart' | 'headshot';
|
||||||
|
|
||||||
export interface Image {
|
export interface Image {
|
||||||
|
|
@ -70,7 +72,7 @@ interface Movie extends ModelBase {
|
||||||
releaseDate?: string;
|
releaseDate?: string;
|
||||||
rootFolderPath: string;
|
rootFolderPath: string;
|
||||||
runtime: number;
|
runtime: number;
|
||||||
minimumAvailability: string;
|
minimumAvailability: MovieAvailability;
|
||||||
path: string;
|
path: string;
|
||||||
genres: string[];
|
genres: string[];
|
||||||
ratings: Ratings;
|
ratings: Ratings;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import ModelBase from 'App/ModelBase';
|
import ModelBase from 'App/ModelBase';
|
||||||
import Movie from 'Movie/Movie';
|
import Movie, { Image, MovieAvailability } from 'Movie/Movie';
|
||||||
|
|
||||||
interface MovieCollection extends ModelBase {
|
interface MovieCollection extends ModelBase {
|
||||||
title: string;
|
|
||||||
sortTitle: string;
|
|
||||||
tmdbId: number;
|
tmdbId: number;
|
||||||
|
sortTitle: string;
|
||||||
|
title: string;
|
||||||
overview: string;
|
overview: string;
|
||||||
monitored: boolean;
|
monitored: boolean;
|
||||||
rootFolderPath: string;
|
minimumAvailability: MovieAvailability;
|
||||||
qualityProfileId: number;
|
qualityProfileId: number;
|
||||||
|
rootFolderPath: string;
|
||||||
|
searchOnAdd: boolean;
|
||||||
|
images: Image[];
|
||||||
movies: Movie[];
|
movies: Movie[];
|
||||||
missingMovies: number;
|
missingMovies: number;
|
||||||
tags: number[];
|
tags: number[];
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,7 @@
|
||||||
"EditIndexerImplementation": "Edit Indexer - {implementationName}",
|
"EditIndexerImplementation": "Edit Indexer - {implementationName}",
|
||||||
"EditMetadata": "Edit {metadataType} Metadata",
|
"EditMetadata": "Edit {metadataType} Metadata",
|
||||||
"EditMovie": "Edit Movie",
|
"EditMovie": "Edit Movie",
|
||||||
|
"EditMovieCollectionModalHeader": "Edit - {title}",
|
||||||
"EditMovieFile": "Edit Movie File",
|
"EditMovieFile": "Edit Movie File",
|
||||||
"EditMovieModalHeader": "Edit - {title}",
|
"EditMovieModalHeader": "Edit - {title}",
|
||||||
"EditMovies": "Edit Movies",
|
"EditMovies": "Edit Movies",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue