mirror of
https://github.com/Radarr/Radarr.git
synced 2025-04-19 12:14:45 -04: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>,
|
||||
AppSectionSaveState {
|
||||
itemMap: Record<number, number>;
|
||||
|
||||
pendingChanges: Partial<MovieCollection>;
|
||||
}
|
||||
|
||||
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 { Navigation } from 'swiper';
|
||||
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 Icon from 'Components/Icon';
|
||||
import Label from 'Components/Label';
|
||||
|
@ -311,7 +311,7 @@ class CollectionOverview extends Component {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<EditCollectionModalConnector
|
||||
<EditMovieCollectionModal
|
||||
isOpen={isEditCollectionModalOpen}
|
||||
collectionId={id}
|
||||
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'
|
||||
| 'deleted';
|
||||
|
||||
export type MovieAvailability = 'announced' | 'inCinemas' | 'released';
|
||||
|
||||
export type CoverType = 'poster' | 'fanart' | 'headshot';
|
||||
|
||||
export interface Image {
|
||||
|
@ -70,7 +72,7 @@ interface Movie extends ModelBase {
|
|||
releaseDate?: string;
|
||||
rootFolderPath: string;
|
||||
runtime: number;
|
||||
minimumAvailability: string;
|
||||
minimumAvailability: MovieAvailability;
|
||||
path: string;
|
||||
genres: string[];
|
||||
ratings: Ratings;
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import ModelBase from 'App/ModelBase';
|
||||
import Movie from 'Movie/Movie';
|
||||
import Movie, { Image, MovieAvailability } from 'Movie/Movie';
|
||||
|
||||
interface MovieCollection extends ModelBase {
|
||||
title: string;
|
||||
sortTitle: string;
|
||||
tmdbId: number;
|
||||
sortTitle: string;
|
||||
title: string;
|
||||
overview: string;
|
||||
monitored: boolean;
|
||||
rootFolderPath: string;
|
||||
minimumAvailability: MovieAvailability;
|
||||
qualityProfileId: number;
|
||||
rootFolderPath: string;
|
||||
searchOnAdd: boolean;
|
||||
images: Image[];
|
||||
movies: Movie[];
|
||||
missingMovies: number;
|
||||
tags: number[];
|
||||
|
|
|
@ -593,6 +593,7 @@
|
|||
"EditIndexerImplementation": "Edit Indexer - {implementationName}",
|
||||
"EditMetadata": "Edit {metadataType} Metadata",
|
||||
"EditMovie": "Edit Movie",
|
||||
"EditMovieCollectionModalHeader": "Edit - {title}",
|
||||
"EditMovieFile": "Edit Movie File",
|
||||
"EditMovieModalHeader": "Edit - {title}",
|
||||
"EditMovies": "Edit Movies",
|
||||
|
|
Loading…
Add table
Reference in a new issue