parent
e85c010bf2
commit
2d96c308f0
@ -1,31 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import DeleteMovieModalContentConnector from './DeleteMovieModalContentConnector';
|
|
||||||
|
|
||||||
function DeleteMovieModal(props) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<DeleteMovieModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteMovieModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeleteMovieModal;
|
|
@ -1,13 +0,0 @@
|
|||||||
.message {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pathContainer {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.path {
|
|
||||||
margin-left: 5px;
|
|
||||||
color: var(--dangerColor);
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'message': string;
|
|
||||||
'path': string;
|
|
||||||
'pathContainer': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
@ -1,145 +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 Button from 'Components/Link/Button';
|
|
||||||
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, kinds } from 'Helpers/Props';
|
|
||||||
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,
|
|
||||||
addImportExclusion: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onDeleteFilesChange = ({ value }) => {
|
|
||||||
this.setState({ deleteFiles: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onAddImportExclusionChange = ({ value }) => {
|
|
||||||
this.setState({ addImportExclusion: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteMovieConfirmed = () => {
|
|
||||||
const deleteFiles = this.state.deleteFiles;
|
|
||||||
const addImportExclusion = this.state.addImportExclusion;
|
|
||||||
|
|
||||||
this.setState({ deleteFiles: false, addImportExclusion: false });
|
|
||||||
this.props.onDeleteSelectedPress(deleteFiles, addImportExclusion);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
movies,
|
|
||||||
onModalClose
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const deleteFiles = this.state.deleteFiles;
|
|
||||||
const addImportExclusion = this.state.addImportExclusion;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('DeleteSelectedMovie')}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{`Delete Movie Folder${movies.length > 1 ? 's' : ''}`}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="deleteFiles"
|
|
||||||
value={deleteFiles}
|
|
||||||
helpText={`Delete Movie Folder${movies.length > 1 ? 's' : ''} and all contents`}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onChange={this.onDeleteFilesChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('AddListExclusion')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="addImportExclusion"
|
|
||||||
value={addImportExclusion}
|
|
||||||
helpText={translate('AddImportExclusionHelpText')}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onChange={this.onAddImportExclusionChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.message}>
|
|
||||||
{`Are you sure you want to delete ${movies.length} selected movie(s)${deleteFiles ? ' and all contents' : ''}?`}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
movies.map((s) => {
|
|
||||||
return (
|
|
||||||
<li key={s.title}>
|
|
||||||
<span>{s.title}</span>
|
|
||||||
|
|
||||||
{
|
|
||||||
deleteFiles &&
|
|
||||||
<span className={styles.pathContainer}>
|
|
||||||
-
|
|
||||||
<span className={styles.path}>
|
|
||||||
{s.path}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={onModalClose}>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={this.onDeleteMovieConfirmed}
|
|
||||||
>
|
|
||||||
{translate('Delete')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteMovieModalContent.propTypes = {
|
|
||||||
movies: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onDeleteSelectedPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeleteMovieModalContent;
|
|
@ -1,46 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { bulkDeleteMovie } from 'Store/Actions/movieIndexActions';
|
|
||||||
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
|
|
||||||
import DeleteMovieModalContent from './DeleteMovieModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { movieIds }) => movieIds,
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
(movieIds, allMovies) => {
|
|
||||||
const selectedMovie = _.intersectionWith(allMovies, movieIds, (s, id) => {
|
|
||||||
return s.id === id;
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortedMovies = _.orderBy(selectedMovie, 'sortTitle');
|
|
||||||
const movies = _.map(sortedMovies, (s) => {
|
|
||||||
return {
|
|
||||||
title: s.title,
|
|
||||||
path: s.path
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
movies
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
|
||||||
return {
|
|
||||||
onDeleteSelectedPress(deleteFiles, addImportExclusion) {
|
|
||||||
dispatch(bulkDeleteMovie({
|
|
||||||
movieIds: props.movieIds,
|
|
||||||
deleteFiles,
|
|
||||||
addImportExclusion
|
|
||||||
}));
|
|
||||||
|
|
||||||
props.onModalClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(DeleteMovieModalContent);
|
|
@ -1,57 +0,0 @@
|
|||||||
.inputContainer {
|
|
||||||
margin-right: 20px;
|
|
||||||
min-width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainerContent {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.organizeSelectedButton,
|
|
||||||
.tagsButton {
|
|
||||||
composes: button from '~Components/Link/SpinnerButton.css';
|
|
||||||
|
|
||||||
margin-right: 10px;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deleteSelectedButton {
|
|
||||||
composes: button from '~Components/Link/SpinnerButton.css';
|
|
||||||
|
|
||||||
margin-left: 50px;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: $breakpointSmall) {
|
|
||||||
.inputContainer {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainerContent {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedMovieLabel {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'buttonContainer': string;
|
|
||||||
'buttonContainerContent': string;
|
|
||||||
'buttons': string;
|
|
||||||
'deleteSelectedButton': string;
|
|
||||||
'inputContainer': string;
|
|
||||||
'organizeSelectedButton': string;
|
|
||||||
'selectedMovieLabel': string;
|
|
||||||
'tagsButton': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
@ -1,303 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import AvailabilitySelectInput from 'Components/Form/AvailabilitySelectInput';
|
|
||||||
import QualityProfileSelectInputConnector from 'Components/Form/QualityProfileSelectInputConnector';
|
|
||||||
import RootFolderSelectInputConnector from 'Components/Form/RootFolderSelectInputConnector';
|
|
||||||
import SelectInput from 'Components/Form/SelectInput';
|
|
||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
|
||||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
import MoveMovieModal from 'Movie/MoveMovie/MoveMovieModal';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import DeleteMovieModal from './Delete/DeleteMovieModal';
|
|
||||||
import MovieEditorFooterLabel from './MovieEditorFooterLabel';
|
|
||||||
import TagsModal from './Tags/TagsModal';
|
|
||||||
import styles from './MovieEditorFooter.css';
|
|
||||||
|
|
||||||
const NO_CHANGE = 'noChange';
|
|
||||||
|
|
||||||
class MovieEditorFooter extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
monitored: NO_CHANGE,
|
|
||||||
qualityProfileId: NO_CHANGE,
|
|
||||||
minimumAvailability: NO_CHANGE,
|
|
||||||
rootFolderPath: NO_CHANGE,
|
|
||||||
savingTags: false,
|
|
||||||
isDeleteMovieModalOpen: false,
|
|
||||||
isTagsModalOpen: false,
|
|
||||||
isConfirmMoveModalOpen: false,
|
|
||||||
destinationRootFolder: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
const {
|
|
||||||
isSaving,
|
|
||||||
saveError
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (prevProps.isSaving && !isSaving && !saveError) {
|
|
||||||
this.setState({
|
|
||||||
monitored: NO_CHANGE,
|
|
||||||
qualityProfileId: NO_CHANGE,
|
|
||||||
minimumAvailability: NO_CHANGE,
|
|
||||||
rootFolderPath: NO_CHANGE,
|
|
||||||
savingTags: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.setState({ [name]: value });
|
|
||||||
|
|
||||||
if (value === NO_CHANGE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (name) {
|
|
||||||
case 'rootFolderPath':
|
|
||||||
this.setState({
|
|
||||||
isConfirmMoveModalOpen: true,
|
|
||||||
destinationRootFolder: value
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'monitored':
|
|
||||||
this.props.onSaveSelected({ [name]: value === 'monitored' });
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.props.onSaveSelected({ [name]: value });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onApplyTagsPress = (tags, applyTags) => {
|
|
||||||
this.setState({
|
|
||||||
savingTags: true,
|
|
||||||
isTagsModalOpen: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSaveSelected({
|
|
||||||
tags,
|
|
||||||
applyTags
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteSelectedPress = () => {
|
|
||||||
this.setState({ isDeleteMovieModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteMovieModalClose = () => {
|
|
||||||
this.setState({ isDeleteMovieModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTagsPress = () => {
|
|
||||||
this.setState({ isTagsModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTagsModalClose = () => {
|
|
||||||
this.setState({ isTagsModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSaveRootFolderPress = () => {
|
|
||||||
this.setState({
|
|
||||||
isConfirmMoveModalOpen: false,
|
|
||||||
destinationRootFolder: null
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSaveSelected({ rootFolderPath: this.state.destinationRootFolder });
|
|
||||||
};
|
|
||||||
|
|
||||||
onMoveMoviePress = () => {
|
|
||||||
this.setState({
|
|
||||||
isConfirmMoveModalOpen: false,
|
|
||||||
destinationRootFolder: null
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onSaveSelected({
|
|
||||||
rootFolderPath: this.state.destinationRootFolder,
|
|
||||||
moveFiles: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
movieIds,
|
|
||||||
selectedCount,
|
|
||||||
isSaving,
|
|
||||||
isDeleting,
|
|
||||||
isOrganizingMovie,
|
|
||||||
onOrganizeMoviePress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
monitored,
|
|
||||||
qualityProfileId,
|
|
||||||
minimumAvailability,
|
|
||||||
rootFolderPath,
|
|
||||||
savingTags,
|
|
||||||
isTagsModalOpen,
|
|
||||||
isDeleteMovieModalOpen,
|
|
||||||
isConfirmMoveModalOpen,
|
|
||||||
destinationRootFolder
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const monitoredOptions = [
|
|
||||||
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
|
||||||
{ key: 'monitored', value: translate('Monitored') },
|
|
||||||
{ key: 'unmonitored', value: translate('Unmonitored') }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageContentFooter>
|
|
||||||
<div className={styles.inputContainer}>
|
|
||||||
<MovieEditorFooterLabel
|
|
||||||
label={translate('MonitorMovie')}
|
|
||||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SelectInput
|
|
||||||
name="monitored"
|
|
||||||
value={monitored}
|
|
||||||
values={monitoredOptions}
|
|
||||||
isDisabled={!selectedCount}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
|
||||||
<MovieEditorFooterLabel
|
|
||||||
label={translate('QualityProfile')}
|
|
||||||
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<QualityProfileSelectInputConnector
|
|
||||||
name="qualityProfileId"
|
|
||||||
value={qualityProfileId}
|
|
||||||
includeNoChange={true}
|
|
||||||
isDisabled={!selectedCount}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
|
||||||
<MovieEditorFooterLabel
|
|
||||||
label={translate('MinimumAvailability')}
|
|
||||||
isSaving={isSaving && minimumAvailability !== NO_CHANGE}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AvailabilitySelectInput
|
|
||||||
name="minimumAvailability"
|
|
||||||
value={minimumAvailability}
|
|
||||||
includeNoChange={true}
|
|
||||||
isDisabled={!selectedCount}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
|
||||||
<MovieEditorFooterLabel
|
|
||||||
label={translate('RootFolder')}
|
|
||||||
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<RootFolderSelectInputConnector
|
|
||||||
name="rootFolderPath"
|
|
||||||
value={rootFolderPath}
|
|
||||||
includeNoChange={true}
|
|
||||||
isDisabled={!selectedCount}
|
|
||||||
selectedValueOptions={{ includeFreeSpace: false }}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.buttonContainer}>
|
|
||||||
<div className={styles.buttonContainerContent}>
|
|
||||||
<MovieEditorFooterLabel
|
|
||||||
label={translate('MoviesSelectedInterp', [selectedCount])}
|
|
||||||
isSaving={false}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.buttons}>
|
|
||||||
<div>
|
|
||||||
<SpinnerButton
|
|
||||||
className={styles.organizeSelectedButton}
|
|
||||||
kind={kinds.WARNING}
|
|
||||||
isSpinning={isOrganizingMovie}
|
|
||||||
isDisabled={!selectedCount || isOrganizingMovie}
|
|
||||||
onPress={onOrganizeMoviePress}
|
|
||||||
>
|
|
||||||
{translate('RenameFiles')}
|
|
||||||
</SpinnerButton>
|
|
||||||
|
|
||||||
<SpinnerButton
|
|
||||||
className={styles.tagsButton}
|
|
||||||
isSpinning={isSaving && savingTags}
|
|
||||||
isDisabled={!selectedCount || isOrganizingMovie}
|
|
||||||
onPress={this.onTagsPress}
|
|
||||||
>
|
|
||||||
{translate('SetTags')}
|
|
||||||
</SpinnerButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SpinnerButton
|
|
||||||
className={styles.deleteSelectedButton}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
isSpinning={isDeleting}
|
|
||||||
isDisabled={!selectedCount || isDeleting}
|
|
||||||
onPress={this.onDeleteSelectedPress}
|
|
||||||
>
|
|
||||||
{translate('Delete')}
|
|
||||||
</SpinnerButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagsModal
|
|
||||||
isOpen={isTagsModalOpen}
|
|
||||||
movieIds={movieIds}
|
|
||||||
onApplyTagsPress={this.onApplyTagsPress}
|
|
||||||
onModalClose={this.onTagsModalClose}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DeleteMovieModal
|
|
||||||
isOpen={isDeleteMovieModalOpen}
|
|
||||||
movieIds={movieIds}
|
|
||||||
onModalClose={this.onDeleteMovieModalClose}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MoveMovieModal
|
|
||||||
destinationRootFolder={destinationRootFolder}
|
|
||||||
isOpen={isConfirmMoveModalOpen}
|
|
||||||
onSavePress={this.onSaveRootFolderPress}
|
|
||||||
onMoveMoviePress={this.onMoveMoviePress}
|
|
||||||
/>
|
|
||||||
</PageContentFooter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieEditorFooter.propTypes = {
|
|
||||||
movieIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
selectedCount: PropTypes.number.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
|
||||||
deleteError: PropTypes.object,
|
|
||||||
isOrganizingMovie: PropTypes.bool.isRequired,
|
|
||||||
onSaveSelected: PropTypes.func.isRequired,
|
|
||||||
onOrganizeMoviePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieEditorFooter;
|
|
@ -1,8 +0,0 @@
|
|||||||
.label {
|
|
||||||
margin-bottom: 3px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.savingIcon {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'label': string;
|
|
||||||
'savingIcon': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
@ -1,40 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import SpinnerIcon from 'Components/SpinnerIcon';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import styles from './MovieEditorFooterLabel.css';
|
|
||||||
|
|
||||||
function MovieEditorFooterLabel(props) {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
label,
|
|
||||||
isSaving
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
{label}
|
|
||||||
|
|
||||||
{
|
|
||||||
isSaving &&
|
|
||||||
<SpinnerIcon
|
|
||||||
className={styles.savingIcon}
|
|
||||||
name={icons.SPINNER}
|
|
||||||
isSpinning={true}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieEditorFooterLabel.propTypes = {
|
|
||||||
className: PropTypes.string.isRequired,
|
|
||||||
label: PropTypes.string.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
MovieEditorFooterLabel.defaultProps = {
|
|
||||||
className: styles.label
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MovieEditorFooterLabel;
|
|
@ -1,31 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import OrganizeMovieModalContentConnector from './OrganizeMovieModalContentConnector';
|
|
||||||
|
|
||||||
function OrganizeMovieModal(props) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<OrganizeMovieModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizeMovieModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizeMovieModal;
|
|
@ -1,8 +0,0 @@
|
|||||||
.renameIcon {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'message': string;
|
|
||||||
'renameIcon': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
@ -1,75 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
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, kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './OrganizeMovieModalContent.css';
|
|
||||||
|
|
||||||
function OrganizeMovieModalContent(props) {
|
|
||||||
const {
|
|
||||||
movieTitles,
|
|
||||||
onModalClose,
|
|
||||||
onOrganizeMoviePress
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('OrganizeSelectedMovies')}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<Alert>
|
|
||||||
{translate('PreviewRenameHelpText')}
|
|
||||||
<Icon
|
|
||||||
className={styles.renameIcon}
|
|
||||||
name={icons.ORGANIZE}
|
|
||||||
/>
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<div className={styles.message}>
|
|
||||||
{translate('OrganizeConfirm', [movieTitles.length])}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
movieTitles.map((title) => {
|
|
||||||
return (
|
|
||||||
<li key={title}>
|
|
||||||
{title}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={onModalClose}>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={onOrganizeMoviePress}
|
|
||||||
>
|
|
||||||
{translate('Organize')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizeMovieModalContent.propTypes = {
|
|
||||||
movieTitles: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onOrganizeMoviePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizeMovieModalContent;
|
|
@ -1,67 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
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 { executeCommand } from 'Store/Actions/commandActions';
|
|
||||||
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
|
|
||||||
import OrganizeMovieModalContent from './OrganizeMovieModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { movieIds }) => movieIds,
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
(movieIds, allMovies) => {
|
|
||||||
const movies = _.intersectionWith(allMovies, movieIds, (s, id) => {
|
|
||||||
return s.id === id;
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortedMovies = _.orderBy(movies, 'sortTitle');
|
|
||||||
const movieTitles = _.map(sortedMovies, 'title');
|
|
||||||
|
|
||||||
return {
|
|
||||||
movieTitles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
executeCommand
|
|
||||||
};
|
|
||||||
|
|
||||||
class OrganizeMovieModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onOrganizeMoviePress = () => {
|
|
||||||
this.props.executeCommand({
|
|
||||||
name: commandNames.RENAME_MOVIE,
|
|
||||||
movieIds: this.props.movieIds
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onModalClose(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render(props) {
|
|
||||||
return (
|
|
||||||
<OrganizeMovieModalContent
|
|
||||||
{...this.props}
|
|
||||||
onOrganizeMoviePress={this.onOrganizeMoviePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizeMovieModalContentConnector.propTypes = {
|
|
||||||
movieIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
executeCommand: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizeMovieModalContentConnector);
|
|
@ -1,31 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import TagsModalContentConnector from './TagsModalContentConnector';
|
|
||||||
|
|
||||||
function TagsModal(props) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<TagsModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TagsModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TagsModal;
|
|
@ -1,12 +0,0 @@
|
|||||||
.renameIcon {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
padding-top: 4px;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'message': string;
|
|
||||||
'renameIcon': string;
|
|
||||||
'result': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
@ -1,188 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
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 Label from 'Components/Label';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
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, kinds, sizes } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './TagsModalContent.css';
|
|
||||||
|
|
||||||
class TagsModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
tags: [],
|
|
||||||
applyTags: 'add'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.setState({ [name]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onApplyTagsPress = () => {
|
|
||||||
const {
|
|
||||||
tags,
|
|
||||||
applyTags
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
this.props.onApplyTagsPress(tags, applyTags);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
movieTags,
|
|
||||||
tagList,
|
|
||||||
onModalClose
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
tags,
|
|
||||||
applyTags
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const applyTagsOptions = [
|
|
||||||
{ key: 'add', value: translate('Add') },
|
|
||||||
{ key: 'remove', value: translate('Remove') },
|
|
||||||
{ key: 'replace', value: translate('Replace') }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
Tags
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<Form>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Tags')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TAG}
|
|
||||||
name="tags"
|
|
||||||
value={tags}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('ApplyTags')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="applyTags"
|
|
||||||
value={applyTags}
|
|
||||||
values={applyTagsOptions}
|
|
||||||
helpTexts={[
|
|
||||||
translate('ApplyTagsHelpTexts1'),
|
|
||||||
translate('ApplyTagsHelpTexts2'),
|
|
||||||
translate('ApplyTagsHelpTexts3'),
|
|
||||||
translate('ApplyTagsHelpTexts4')
|
|
||||||
]}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Result')}</FormLabel>
|
|
||||||
|
|
||||||
<div className={styles.result}>
|
|
||||||
{
|
|
||||||
movieTags.map((t) => {
|
|
||||||
const tag = _.find(tagList, { id: t });
|
|
||||||
|
|
||||||
if (!tag) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeTag = (applyTags === 'remove' && tags.indexOf(t) > -1) ||
|
|
||||||
(applyTags === 'replace' && tags.indexOf(t) === -1);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={tag.id}
|
|
||||||
title={removeTag ? translate('RemovingTag') : translate('ExistingTag')}
|
|
||||||
kind={removeTag ? kinds.INVERSE : kinds.INFO}
|
|
||||||
size={sizes.LARGE}
|
|
||||||
>
|
|
||||||
{tag.label}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
(applyTags === 'add' || applyTags === 'replace') &&
|
|
||||||
tags.map((t) => {
|
|
||||||
const tag = _.find(tagList, { id: t });
|
|
||||||
|
|
||||||
if (!tag) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (movieTags.indexOf(t) > -1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={tag.id}
|
|
||||||
title={translate('AddingTag')}
|
|
||||||
kind={kinds.SUCCESS}
|
|
||||||
size={sizes.LARGE}
|
|
||||||
>
|
|
||||||
{tag.label}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={onModalClose}>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.PRIMARY}
|
|
||||||
onPress={this.onApplyTagsPress}
|
|
||||||
>
|
|
||||||
{translate('Apply')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TagsModalContent.propTypes = {
|
|
||||||
movieTags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onApplyTagsPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TagsModalContent;
|
|
@ -1,36 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
|
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
|
||||||
import TagsModalContent from './TagsModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { movieIds }) => movieIds,
|
|
||||||
createAllMoviesSelector(),
|
|
||||||
createTagsSelector(),
|
|
||||||
(movieIds, allMovies, tagList) => {
|
|
||||||
const movies = _.intersectionWith(allMovies, movieIds, (s, id) => {
|
|
||||||
return s.id === id;
|
|
||||||
});
|
|
||||||
|
|
||||||
const movieTags = _.uniq(_.concat(..._.map(movies, 'tags')));
|
|
||||||
|
|
||||||
return {
|
|
||||||
movieTags,
|
|
||||||
tagList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
|
||||||
return {
|
|
||||||
onAction() {
|
|
||||||
// Do something
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(TagsModalContent);
|
|
Loading…
Reference in new issue