mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-27 22:57:09 -04:00
e4e96fc7f9
(cherry picked from commit a2fd23c84d0a9d01864119d2e643970845c9e49e)
194 lines
5.9 KiB
TypeScript
194 lines
5.9 KiB
TypeScript
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 useMovie from 'Movie/useMovie';
|
|
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 {
|
|
movieId: number;
|
|
onModalClose: () => void;
|
|
}
|
|
|
|
function OrganizePreviewModalContent({
|
|
movieId,
|
|
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 movie = useMovie(movieId)!;
|
|
const [selectState, setSelectState] = useSelectState();
|
|
|
|
const { allSelected, allUnselected, selectedState } = selectState;
|
|
const isFetching = isPreviewFetching || isNamingFetching;
|
|
const isPopulated = isPreviewPopulated && isNamingPopulated;
|
|
const error = previewError || namingError;
|
|
const { renameMovies, standardMovieFormat } = naming;
|
|
|
|
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,
|
|
movieId,
|
|
})
|
|
);
|
|
|
|
onModalClose();
|
|
}, [movieId, selectedState, dispatch, onModalClose]);
|
|
|
|
useEffect(() => {
|
|
dispatch(fetchOrganizePreview({ movieId }));
|
|
dispatch(fetchNamingSettings());
|
|
}, [movieId, dispatch]);
|
|
|
|
return (
|
|
<ModalContent onModalClose={onModalClose}>
|
|
<ModalHeader>{translate('OrganizeModalHeader')}</ModalHeader>
|
|
|
|
<ModalBody>
|
|
{isFetching ? <LoadingIndicator /> : null}
|
|
|
|
{!isFetching && error ? (
|
|
<Alert kind={kinds.DANGER}>{translate('OrganizeLoadError')}</Alert>
|
|
) : null}
|
|
|
|
{!isFetching && isPopulated && !items.length ? (
|
|
<div>
|
|
{renameMovies ? (
|
|
<div>{translate('OrganizeNothingToRename')}</div>
|
|
) : (
|
|
<div>{translate('OrganizeRenamingDisabled')}</div>
|
|
)}
|
|
</div>
|
|
) : null}
|
|
|
|
{!isFetching && isPopulated && items.length ? (
|
|
<div>
|
|
<Alert>
|
|
<div>
|
|
<InlineMarkdown
|
|
data={translate('OrganizeRelativePaths', {
|
|
path: movie.path,
|
|
})}
|
|
blockClassName={styles.path}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<InlineMarkdown
|
|
data={translate('OrganizeNamingPattern', {
|
|
standardMovieFormat,
|
|
})}
|
|
blockClassName={styles.standardMovieFormat}
|
|
/>
|
|
</div>
|
|
</Alert>
|
|
|
|
<div className={styles.previews}>
|
|
{items.map((item) => {
|
|
return (
|
|
<OrganizePreviewRow
|
|
key={item.movieFileId}
|
|
id={item.movieFileId}
|
|
existingPath={item.existingPath}
|
|
newPath={item.newPath}
|
|
isSelected={selectedState[item.movieFileId]}
|
|
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;
|