New: Optionally remove from queue by changing category to 'Post-Import Category' when configured

(cherry picked from commit 345854d0fe9b65a561fdab12aac688782a420aa5)

Closes #4510
This commit is contained in:
Mark McDowall 2024-01-22 20:56:35 -08:00 committed by Bogdan
commit 6517485e0f
31 changed files with 319 additions and 212 deletions

View file

@ -25,7 +25,7 @@ import toggleSelected from 'Utilities/Table/toggleSelected';
import QueueFilterModal from './QueueFilterModal';
import QueueOptionsConnector from './QueueOptionsConnector';
import QueueRowConnector from './QueueRowConnector';
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
import RemoveQueueItemModal from './RemoveQueueItemModal';
class Queue extends Component {
@ -309,9 +309,16 @@ class Queue extends Component {
}
</PageContentBody>
<RemoveQueueItemsModal
<RemoveQueueItemModal
isOpen={isConfirmRemoveModalOpen}
selectedCount={selectedCount}
canChangeCategory={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);
return !!(item && item.downloadClientHasPostImportCategory);
})
)}
canIgnore={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);
@ -319,7 +326,7 @@ class Queue extends Component {
return !!(item && item.artistId && item.albumId);
})
)}
allPending={isConfirmRemoveModalOpen && (
pending={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);

View file

@ -98,6 +98,7 @@ class QueueRow extends Component {
indexer,
outputPath,
downloadClient,
downloadClientHasPostImportCategory,
downloadForced,
estimatedCompletionTime,
added,
@ -403,6 +404,7 @@ class QueueRow extends Component {
<RemoveQueueItemModal
isOpen={isRemoveQueueItemModalOpen}
sourceTitle={title}
canChangeCategory={!!downloadClientHasPostImportCategory}
canIgnore={!!artist}
isPending={isPending}
onRemovePress={this.onRemoveQueueItemModalConfirmed}
@ -432,6 +434,7 @@ QueueRow.propTypes = {
indexer: PropTypes.string,
outputPath: PropTypes.string,
downloadClient: PropTypes.string,
downloadClientHasPostImportCategory: PropTypes.bool,
downloadForced: PropTypes.bool.isRequired,
estimatedCompletionTime: PropTypes.string,
added: PropTypes.string,

View file

@ -1,175 +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 Modal from 'Components/Modal/Modal';
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';
class RemoveQueueItemModal extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
removeFromClient: true,
blocklist: false,
skipRedownload: false
};
}
//
// Control
resetState = function() {
this.setState({
removeFromClient: true,
blocklist: false,
skipRedownload: false
});
};
//
// Listeners
onRemoveFromClientChange = ({ value }) => {
this.setState({ removeFromClient: value });
};
onBlocklistChange = ({ value }) => {
this.setState({ blocklist: value });
};
onSkipRedownloadChange = ({ value }) => {
this.setState({ skipRedownload: value });
};
onRemoveConfirmed = () => {
const state = this.state;
this.resetState();
this.props.onRemovePress(state);
};
onModalClose = () => {
this.resetState();
this.props.onModalClose();
};
//
// Render
render() {
const {
isOpen,
sourceTitle,
canIgnore,
isPending
} = this.props;
const { removeFromClient, blocklist, skipRedownload } = this.state;
return (
<Modal
isOpen={isOpen}
size={sizes.MEDIUM}
onModalClose={this.onModalClose}
>
<ModalContent
onModalClose={this.onModalClose}
>
<ModalHeader>
Remove - {sourceTitle}
</ModalHeader>
<ModalBody>
<div>
Are you sure you want to remove '{sourceTitle}' from the queue?
</div>
{
isPending ?
null :
<FormGroup>
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="removeFromClient"
value={removeFromClient}
helpTextWarning={translate('RemoveFromDownloadClientHelpTextWarning')}
isDisabled={!canIgnore}
onChange={this.onRemoveFromClientChange}
/>
</FormGroup>
}
<FormGroup>
<FormLabel>
{translate('BlocklistRelease')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blocklist"
value={blocklist}
helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange}
/>
</FormGroup>
{
blocklist &&
<FormGroup>
<FormLabel>
{translate('SkipRedownload')}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipRedownload"
value={skipRedownload}
helpText={translate('SkipRedownloadHelpText')}
onChange={this.onSkipRedownloadChange}
/>
</FormGroup>
}
</ModalBody>
<ModalFooter>
<Button onPress={this.onModalClose}>
Close
</Button>
<Button
kind={kinds.DANGER}
onPress={this.onRemoveConfirmed}
>
Remove
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
}
RemoveQueueItemModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired,
canIgnore: PropTypes.bool.isRequired,
isPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default RemoveQueueItemModal;

View file

@ -0,0 +1,230 @@
import React, { useCallback, useMemo, useState } 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 Modal from 'Components/Modal/Modal';
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 './RemoveQueueItemModal.css';
interface RemovePressProps {
remove: boolean;
changeCategory: boolean;
blocklist: boolean;
skipRedownload: boolean;
}
interface RemoveQueueItemModalProps {
isOpen: boolean;
sourceTitle: string;
canChangeCategory: boolean;
canIgnore: boolean;
isPending: boolean;
selectedCount?: number;
onRemovePress(props: RemovePressProps): void;
onModalClose: () => void;
}
type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore';
type BlocklistMethod =
| 'doNotBlocklist'
| 'blocklistAndSearch'
| 'blocklistOnly';
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
const {
isOpen,
sourceTitle,
canIgnore,
canChangeCategory,
isPending,
selectedCount,
onRemovePress,
onModalClose,
} = props;
const multipleSelected = selectedCount && selectedCount > 1;
const [removalMethod, setRemovalMethod] =
useState<RemovalMethod>('removeFromClient');
const [blocklistMethod, setBlocklistMethod] =
useState<BlocklistMethod>('doNotBlocklist');
const { title, message } = useMemo(() => {
if (!selectedCount) {
return {
title: translate('RemoveQueueItem', { sourceTitle }),
message: translate('RemoveQueueItemConfirmation', { sourceTitle }),
};
}
if (selectedCount === 1) {
return {
title: translate('RemoveSelectedItem'),
message: translate('RemoveSelectedItemQueueMessageText'),
};
}
return {
title: translate('RemoveSelectedItems'),
message: translate('RemoveSelectedItemsQueueMessageText', {
selectedCount,
}),
};
}, [sourceTitle, selectedCount]);
const removalMethodOptions = useMemo(() => {
return [
{
key: 'removeFromClient',
value: translate('RemoveFromDownloadClient'),
hint: multipleSelected
? translate('RemoveMultipleFromDownloadClientHint')
: translate('RemoveFromDownloadClientHint'),
},
{
key: 'changeCategory',
value: translate('ChangeCategory'),
isDisabled: !canChangeCategory,
hint: multipleSelected
? translate('ChangeCategoryMultipleHint')
: translate('ChangeCategoryHint'),
},
{
key: 'ignore',
value: multipleSelected
? translate('IgnoreDownloads')
: translate('IgnoreDownload'),
isDisabled: !canIgnore,
hint: multipleSelected
? translate('IgnoreDownloadsHint')
: translate('IgnoreDownloadHint'),
},
];
}, [canChangeCategory, canIgnore, multipleSelected]);
const blocklistMethodOptions = useMemo(() => {
return [
{
key: 'doNotBlocklist',
value: translate('DoNotBlocklist'),
hint: translate('DoNotBlocklistHint'),
},
{
key: 'blocklistAndSearch',
value: translate('BlocklistAndSearch'),
hint: multipleSelected
? translate('BlocklistAndSearchMultipleHint')
: translate('BlocklistAndSearchHint'),
},
{
key: 'blocklistOnly',
value: translate('BlocklistOnly'),
hint: multipleSelected
? translate('BlocklistMultipleOnlyHint')
: translate('BlocklistOnlyHint'),
},
];
}, [multipleSelected]);
const handleRemovalMethodChange = useCallback(
({ value }: { value: RemovalMethod }) => {
setRemovalMethod(value);
},
[setRemovalMethod]
);
const handleBlocklistMethodChange = useCallback(
({ value }: { value: BlocklistMethod }) => {
setBlocklistMethod(value);
},
[setBlocklistMethod]
);
const handleConfirmRemove = useCallback(() => {
onRemovePress({
remove: removalMethod === 'removeFromClient',
changeCategory: removalMethod === 'changeCategory',
blocklist: blocklistMethod !== 'doNotBlocklist',
skipRedownload: blocklistMethod === 'blocklistOnly',
});
setRemovalMethod('removeFromClient');
setBlocklistMethod('doNotBlocklist');
}, [
removalMethod,
blocklistMethod,
setRemovalMethod,
setBlocklistMethod,
onRemovePress,
]);
const handleModalClose = useCallback(() => {
setRemovalMethod('removeFromClient');
setBlocklistMethod('doNotBlocklist');
onModalClose();
}, [setRemovalMethod, setBlocklistMethod, onModalClose]);
return (
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={handleModalClose}>
<ModalContent onModalClose={handleModalClose}>
<ModalHeader>{title}</ModalHeader>
<ModalBody>
<div className={styles.message}>{message}</div>
{isPending ? null : (
<FormGroup>
<FormLabel>{translate('RemoveQueueItemRemovalMethod')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="removalMethod"
value={removalMethod}
values={removalMethodOptions}
isDisabled={!canChangeCategory && !canIgnore}
helpTextWarning={translate(
'RemoveQueueItemRemovalMethodHelpTextWarning'
)}
onChange={handleRemovalMethodChange}
/>
</FormGroup>
)}
<FormGroup>
<FormLabel>
{multipleSelected
? translate('BlocklistReleases')
: translate('BlocklistRelease')}
</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="blocklistMethod"
value={blocklistMethod}
values={blocklistMethodOptions}
helpText={translate('BlocklistReleaseHelpText')}
onChange={handleBlocklistMethodChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button onPress={handleModalClose}>{translate('Close')}</Button>
<Button kind={kinds.DANGER} onPress={handleConfirmRemove}>
{translate('Remove')}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
export default RemoveQueueItemModal;