parent
32ce09648c
commit
a2fd23c84d
@ -0,0 +1,15 @@
|
|||||||
|
import ModelBase from 'App/ModelBase';
|
||||||
|
import AppSectionState from 'App/State/AppSectionState';
|
||||||
|
|
||||||
|
export interface OrganizePreviewModel extends ModelBase {
|
||||||
|
seriesId: number;
|
||||||
|
seasonNumber: number;
|
||||||
|
episodeNumbers: number[];
|
||||||
|
episodeFileId: number;
|
||||||
|
existingPath: string;
|
||||||
|
newPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrganizePreviewAppState = AppSectionState<OrganizePreviewModel>;
|
||||||
|
|
||||||
|
export default OrganizePreviewAppState;
|
@ -1,34 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import OrganizePreviewModalContentConnector from './OrganizePreviewModalContentConnector';
|
|
||||||
|
|
||||||
function OrganizePreviewModal(props) {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
onModalClose,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
isOpen &&
|
|
||||||
<OrganizePreviewModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizePreviewModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizePreviewModal;
|
|
@ -0,0 +1,37 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import { clearOrganizePreview } from 'Store/Actions/organizePreviewActions';
|
||||||
|
import OrganizePreviewModalContent, {
|
||||||
|
OrganizePreviewModalContentProps,
|
||||||
|
} from './OrganizePreviewModalContent';
|
||||||
|
|
||||||
|
interface OrganizePreviewModalProps extends OrganizePreviewModalContentProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrganizePreviewModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: OrganizePreviewModalProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleOnModalClose = useCallback(() => {
|
||||||
|
dispatch(clearOrganizePreview());
|
||||||
|
onModalClose();
|
||||||
|
}, [dispatch, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onModalClose={handleOnModalClose}>
|
||||||
|
{isOpen ? (
|
||||||
|
<OrganizePreviewModalContent
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={handleOnModalClose}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default OrganizePreviewModal;
|
@ -1,39 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearOrganizePreview } from 'Store/Actions/organizePreviewActions';
|
|
||||||
import OrganizePreviewModal from './OrganizePreviewModal';
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
clearOrganizePreview
|
|
||||||
};
|
|
||||||
|
|
||||||
class OrganizePreviewModalConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.clearOrganizePreview();
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<OrganizePreviewModal
|
|
||||||
{...this.props}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizePreviewModalConnector.propTypes = {
|
|
||||||
clearOrganizePreview: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(undefined, mapDispatchToProps)(OrganizePreviewModalConnector);
|
|
@ -1,202 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
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 { kinds } from 'Helpers/Props';
|
|
||||||
import formatSeason from 'Season/formatSeason';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
|
||||||
import selectAll from 'Utilities/Table/selectAll';
|
|
||||||
import toggleSelected from 'Utilities/Table/toggleSelected';
|
|
||||||
import OrganizePreviewRow from './OrganizePreviewRow';
|
|
||||||
import styles from './OrganizePreviewModalContent.css';
|
|
||||||
|
|
||||||
function getValue(allSelected, allUnselected) {
|
|
||||||
if (allSelected) {
|
|
||||||
return true;
|
|
||||||
} else if (allUnselected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
class OrganizePreviewModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
allSelected: false,
|
|
||||||
allUnselected: false,
|
|
||||||
lastToggled: null,
|
|
||||||
selectedState: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
getSelectedIds = () => {
|
|
||||||
return getSelectedIds(this.state.selectedState);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSelectAllChange = ({ value }) => {
|
|
||||||
this.setState(selectAll(this.state.selectedState, value));
|
|
||||||
};
|
|
||||||
|
|
||||||
onSelectedChange = ({ id, value, shiftKey = false }) => {
|
|
||||||
this.setState((state) => {
|
|
||||||
return toggleSelected(state, this.props.items, id, value, shiftKey);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onOrganizePress = () => {
|
|
||||||
this.props.onOrganizePress(this.getSelectedIds());
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
error,
|
|
||||||
items,
|
|
||||||
seasonNumber,
|
|
||||||
renameEpisodes,
|
|
||||||
episodeFormat,
|
|
||||||
path,
|
|
||||||
onModalClose
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
allSelected,
|
|
||||||
allUnselected,
|
|
||||||
selectedState
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const selectAllValue = getValue(allSelected, allUnselected);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{ seasonNumber == null ?
|
|
||||||
translate('OrganizeModalHeader') :
|
|
||||||
translate('OrganizeModalHeaderSeason', { season: formatSeason(seasonNumber) })
|
|
||||||
}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
{
|
|
||||||
isFetching &&
|
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && error &&
|
|
||||||
<Alert kind={kinds.DANGER}>{translate('OrganizeLoadError')}</Alert>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && isPopulated && !items.length &&
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
renameEpisodes ?
|
|
||||||
<div>{translate('OrganizeNothingToRename')}</div> :
|
|
||||||
<div>{translate('OrganizeRenamingDisabled')}</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && isPopulated && !!items.length &&
|
|
||||||
<div>
|
|
||||||
<Alert>
|
|
||||||
<div>
|
|
||||||
<InlineMarkdown data={translate('OrganizeRelativePaths', { path })} blockClassName={styles.path} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<InlineMarkdown data={translate('OrganizeNamingPattern', { episodeFormat })} blockClassName={styles.episodeFormat} />
|
|
||||||
</div>
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<div className={styles.previews}>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<OrganizePreviewRow
|
|
||||||
key={item.episodeFileId}
|
|
||||||
id={item.episodeFileId}
|
|
||||||
existingPath={item.existingPath}
|
|
||||||
newPath={item.newPath}
|
|
||||||
isSelected={selectedState[item.episodeFileId]}
|
|
||||||
onSelectedChange={this.onSelectedChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
{
|
|
||||||
isPopulated && !!items.length &&
|
|
||||||
<CheckInput
|
|
||||||
className={styles.selectAllInput}
|
|
||||||
containerClassName={styles.selectAllInputContainer}
|
|
||||||
name="selectAll"
|
|
||||||
value={selectAllValue}
|
|
||||||
onChange={this.onSelectAllChange}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.PRIMARY}
|
|
||||||
onPress={this.onOrganizePress}
|
|
||||||
>
|
|
||||||
{translate('Organize')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizePreviewModalContent.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
seasonNumber: PropTypes.number,
|
|
||||||
path: PropTypes.string.isRequired,
|
|
||||||
renameEpisodes: PropTypes.bool,
|
|
||||||
episodeFormat: PropTypes.string,
|
|
||||||
onOrganizePress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizePreviewModalContent;
|
|
@ -0,0 +1,201 @@
|
|||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import * as commandNames from 'Commands/commandNames';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
|
import CheckInput from 'Components/Form/CheckInput';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
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 useSelectState from 'Helpers/Hooks/useSelectState';
|
||||||
|
import { kinds } from 'Helpers/Props';
|
||||||
|
import formatSeason from 'Season/formatSeason';
|
||||||
|
import useSeries from 'Series/useSeries';
|
||||||
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
|
import { fetchOrganizePreview } from 'Store/Actions/organizePreviewActions';
|
||||||
|
import { fetchNamingSettings } from 'Store/Actions/settingsActions';
|
||||||
|
import { CheckInputChanged } from 'typings/inputs';
|
||||||
|
import { SelectStateInputProps } from 'typings/props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||||
|
import OrganizePreviewRow from './OrganizePreviewRow';
|
||||||
|
import styles from './OrganizePreviewModalContent.css';
|
||||||
|
|
||||||
|
function getValue(allSelected: boolean, allUnselected: boolean) {
|
||||||
|
if (allSelected) {
|
||||||
|
return true;
|
||||||
|
} else if (allUnselected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrganizePreviewModalContentProps {
|
||||||
|
seriesId: number;
|
||||||
|
seasonNumber?: number;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrganizePreviewModalContent({
|
||||||
|
seriesId,
|
||||||
|
seasonNumber,
|
||||||
|
onModalClose,
|
||||||
|
}: OrganizePreviewModalContentProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
isFetching: isPreviewFetching,
|
||||||
|
isPopulated: isPreviewPopulated,
|
||||||
|
error: previewError,
|
||||||
|
} = useSelector((state: AppState) => state.organizePreview);
|
||||||
|
|
||||||
|
const {
|
||||||
|
isFetching: isNamingFetching,
|
||||||
|
isPopulated: isNamingPopulated,
|
||||||
|
error: namingError,
|
||||||
|
item: naming,
|
||||||
|
} = useSelector((state: AppState) => state.settings.naming);
|
||||||
|
|
||||||
|
const series = useSeries(seriesId)!;
|
||||||
|
const [selectState, setSelectState] = useSelectState();
|
||||||
|
|
||||||
|
const { allSelected, allUnselected, selectedState } = selectState;
|
||||||
|
const isFetching = isPreviewFetching || isNamingFetching;
|
||||||
|
const isPopulated = isPreviewPopulated && isNamingPopulated;
|
||||||
|
const error = previewError || namingError;
|
||||||
|
const { renameEpisodes } = naming;
|
||||||
|
const episodeFormat = naming[`${series.seriesType}EpisodeFormat`];
|
||||||
|
|
||||||
|
const selectAllValue = getValue(allSelected, allUnselected);
|
||||||
|
|
||||||
|
const handleSelectAllChange = useCallback(
|
||||||
|
({ value }: CheckInputChanged) => {
|
||||||
|
setSelectState({ type: value ? 'selectAll' : 'unselectAll', items });
|
||||||
|
},
|
||||||
|
[items, setSelectState]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelectedChange = useCallback(
|
||||||
|
({ id, value, shiftKey = false }: SelectStateInputProps) => {
|
||||||
|
setSelectState({
|
||||||
|
type: 'toggleSelected',
|
||||||
|
items,
|
||||||
|
id,
|
||||||
|
isSelected: value,
|
||||||
|
shiftKey,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[items, setSelectState]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOrganizePress = useCallback(() => {
|
||||||
|
const files = getSelectedIds(selectedState);
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
executeCommand({
|
||||||
|
name: commandNames.RENAME_FILES,
|
||||||
|
files,
|
||||||
|
seriesId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
onModalClose();
|
||||||
|
}, [seriesId, selectedState, dispatch, onModalClose]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchOrganizePreview({ seriesId, seasonNumber }));
|
||||||
|
dispatch(fetchNamingSettings());
|
||||||
|
}, [seriesId, seasonNumber, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{seasonNumber == null
|
||||||
|
? translate('OrganizeModalHeader')
|
||||||
|
: translate('OrganizeModalHeaderSeason', {
|
||||||
|
season: formatSeason(seasonNumber) ?? '',
|
||||||
|
})}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
{isFetching ? <LoadingIndicator /> : null}
|
||||||
|
|
||||||
|
{!isFetching && error ? (
|
||||||
|
<Alert kind={kinds.DANGER}>{translate('OrganizeLoadError')}</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!isFetching && isPopulated && !items.length ? (
|
||||||
|
<div>
|
||||||
|
{renameEpisodes ? (
|
||||||
|
<div>{translate('OrganizeNothingToRename')}</div>
|
||||||
|
) : (
|
||||||
|
<div>{translate('OrganizeRenamingDisabled')}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!isFetching && isPopulated && items.length ? (
|
||||||
|
<div>
|
||||||
|
<Alert>
|
||||||
|
<div>
|
||||||
|
<InlineMarkdown
|
||||||
|
data={translate('OrganizeRelativePaths', {
|
||||||
|
path: series.path,
|
||||||
|
})}
|
||||||
|
blockClassName={styles.path}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<InlineMarkdown
|
||||||
|
data={translate('OrganizeNamingPattern', { episodeFormat })}
|
||||||
|
blockClassName={styles.episodeFormat}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<div className={styles.previews}>
|
||||||
|
{items.map((item) => {
|
||||||
|
return (
|
||||||
|
<OrganizePreviewRow
|
||||||
|
key={item.episodeFileId}
|
||||||
|
id={item.episodeFileId}
|
||||||
|
existingPath={item.existingPath}
|
||||||
|
newPath={item.newPath}
|
||||||
|
isSelected={selectedState[item.episodeFileId]}
|
||||||
|
onSelectedChange={handleSelectedChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
{isPopulated && items.length ? (
|
||||||
|
<CheckInput
|
||||||
|
className={styles.selectAllInput}
|
||||||
|
containerClassName={styles.selectAllInputContainer}
|
||||||
|
name="selectAll"
|
||||||
|
value={selectAllValue}
|
||||||
|
onChange={handleSelectAllChange}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||||
|
|
||||||
|
<Button kind={kinds.PRIMARY} onPress={handleOrganizePress}>
|
||||||
|
{translate('Organize')}
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrganizePreviewModalContent;
|
@ -1,91 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import * as commandNames from 'Commands/commandNames';
|
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
|
||||||
import { fetchOrganizePreview } from 'Store/Actions/organizePreviewActions';
|
|
||||||
import { fetchNamingSettings } from 'Store/Actions/settingsActions';
|
|
||||||
import createSeriesSelector from 'Store/Selectors/createSeriesSelector';
|
|
||||||
import OrganizePreviewModalContent from './OrganizePreviewModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.organizePreview,
|
|
||||||
(state) => state.settings.naming,
|
|
||||||
createSeriesSelector(),
|
|
||||||
(organizePreview, naming, series) => {
|
|
||||||
const props = { ...organizePreview };
|
|
||||||
props.isFetching = organizePreview.isFetching || naming.isFetching;
|
|
||||||
props.isPopulated = organizePreview.isPopulated && naming.isPopulated;
|
|
||||||
props.error = organizePreview.error || naming.error;
|
|
||||||
props.renameEpisodes = naming.item.renameEpisodes;
|
|
||||||
props.episodeFormat = naming.item[`${series.seriesType}EpisodeFormat`];
|
|
||||||
props.path = series.path;
|
|
||||||
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchOrganizePreview,
|
|
||||||
fetchNamingSettings,
|
|
||||||
executeCommand
|
|
||||||
};
|
|
||||||
|
|
||||||
class OrganizePreviewModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const {
|
|
||||||
seriesId,
|
|
||||||
seasonNumber
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.props.fetchOrganizePreview({
|
|
||||||
seriesId,
|
|
||||||
seasonNumber
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.fetchNamingSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onOrganizePress = (files) => {
|
|
||||||
this.props.executeCommand({
|
|
||||||
name: commandNames.RENAME_FILES,
|
|
||||||
seriesId: this.props.seriesId,
|
|
||||||
files
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<OrganizePreviewModalContent
|
|
||||||
{...this.props}
|
|
||||||
onOrganizePress={this.onOrganizePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizePreviewModalContentConnector.propTypes = {
|
|
||||||
seriesId: PropTypes.number.isRequired,
|
|
||||||
seasonNumber: PropTypes.number,
|
|
||||||
fetchOrganizePreview: PropTypes.func.isRequired,
|
|
||||||
fetchNamingSettings: PropTypes.func.isRequired,
|
|
||||||
executeCommand: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizePreviewModalContentConnector);
|
|
@ -1,90 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
|
||||||
import styles from './OrganizePreviewRow.css';
|
|
||||||
|
|
||||||
class OrganizePreviewRow extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
onSelectedChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onSelectedChange({ id, value: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onSelectedChange = ({ value, shiftKey }) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
onSelectedChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onSelectedChange({ id, value, shiftKey });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
existingPath,
|
|
||||||
newPath,
|
|
||||||
isSelected
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.row}>
|
|
||||||
<CheckInput
|
|
||||||
containerClassName={styles.selectedContainer}
|
|
||||||
name={id.toString()}
|
|
||||||
value={isSelected}
|
|
||||||
onChange={this.onSelectedChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<Icon
|
|
||||||
name={icons.SUBTRACT}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={styles.path}>
|
|
||||||
{existingPath}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Icon
|
|
||||||
name={icons.ADD}
|
|
||||||
kind={kinds.SUCCESS}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={styles.path}>
|
|
||||||
{newPath}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OrganizePreviewRow.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
existingPath: PropTypes.string.isRequired,
|
|
||||||
newPath: PropTypes.string.isRequired,
|
|
||||||
isSelected: PropTypes.bool,
|
|
||||||
onSelectedChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizePreviewRow;
|
|
@ -0,0 +1,61 @@
|
|||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import CheckInput from 'Components/Form/CheckInput';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
|
import { CheckInputChanged } from 'typings/inputs';
|
||||||
|
import { SelectStateInputProps } from 'typings/props';
|
||||||
|
import styles from './OrganizePreviewRow.css';
|
||||||
|
|
||||||
|
interface OrganizePreviewRowProps {
|
||||||
|
id: number;
|
||||||
|
existingPath: string;
|
||||||
|
newPath: string;
|
||||||
|
isSelected?: boolean;
|
||||||
|
onSelectedChange: (props: SelectStateInputProps) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrganizePreviewRow({
|
||||||
|
id,
|
||||||
|
existingPath,
|
||||||
|
newPath,
|
||||||
|
isSelected,
|
||||||
|
onSelectedChange,
|
||||||
|
}: OrganizePreviewRowProps) {
|
||||||
|
const handleSelectedChange = useCallback(
|
||||||
|
({ value, shiftKey }: CheckInputChanged) => {
|
||||||
|
onSelectedChange({ id, value, shiftKey });
|
||||||
|
},
|
||||||
|
[id, onSelectedChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onSelectedChange({ id, value: true, shiftKey: false });
|
||||||
|
}, [id, onSelectedChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.row}>
|
||||||
|
<CheckInput
|
||||||
|
containerClassName={styles.selectedContainer}
|
||||||
|
name={id.toString()}
|
||||||
|
value={isSelected}
|
||||||
|
onChange={handleSelectedChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Icon name={icons.SUBTRACT} kind={kinds.DANGER} />
|
||||||
|
|
||||||
|
<span className={styles.path}>{existingPath}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Icon name={icons.ADD} kind={kinds.SUCCESS} />
|
||||||
|
|
||||||
|
<span className={styles.path}>{newPath}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrganizePreviewRow;
|
Loading…
Reference in new issue