mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-10-29 23:12:39 +01:00
Convert Release Profiles to TypeScript
This commit is contained in:
parent
6660db22ec
commit
e6e1078c15
@ -16,6 +16,7 @@ import IndexerFlag from 'typings/IndexerFlag';
|
|||||||
import Notification from 'typings/Notification';
|
import Notification from 'typings/Notification';
|
||||||
import QualityProfile from 'typings/QualityProfile';
|
import QualityProfile from 'typings/QualityProfile';
|
||||||
import General from 'typings/Settings/General';
|
import General from 'typings/Settings/General';
|
||||||
|
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||||
import UiSettings from 'typings/Settings/UiSettings';
|
import UiSettings from 'typings/Settings/UiSettings';
|
||||||
|
|
||||||
export interface DownloadClientAppState
|
export interface DownloadClientAppState
|
||||||
@ -49,6 +50,12 @@ export interface QualityProfilesAppState
|
|||||||
extends AppSectionState<QualityProfile>,
|
extends AppSectionState<QualityProfile>,
|
||||||
AppSectionItemSchemaState<QualityProfile> {}
|
AppSectionItemSchemaState<QualityProfile> {}
|
||||||
|
|
||||||
|
export interface ReleaseProfilesAppState
|
||||||
|
extends AppSectionState<ReleaseProfile>,
|
||||||
|
AppSectionSaveState {
|
||||||
|
pendingChanges: Partial<ReleaseProfile>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CustomFormatAppState
|
export interface CustomFormatAppState
|
||||||
extends AppSectionState<CustomFormat>,
|
extends AppSectionState<CustomFormat>,
|
||||||
AppSectionDeleteState,
|
AppSectionDeleteState,
|
||||||
@ -83,6 +90,7 @@ interface SettingsAppState {
|
|||||||
languages: LanguageSettingsAppState;
|
languages: LanguageSettingsAppState;
|
||||||
notifications: NotificationAppState;
|
notifications: NotificationAppState;
|
||||||
qualityProfiles: QualityProfilesAppState;
|
qualityProfiles: QualityProfilesAppState;
|
||||||
|
releaseProfiles: ReleaseProfilesAppState;
|
||||||
ui: UiSettingsAppState;
|
ui: UiSettingsAppState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,6 +272,8 @@ FormInputGroup.propTypes = {
|
|||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
values: PropTypes.arrayOf(PropTypes.any),
|
values: PropTypes.arrayOf(PropTypes.any),
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
delimiters: PropTypes.arrayOf(PropTypes.string),
|
||||||
isDisabled: PropTypes.bool,
|
isDisabled: PropTypes.bool,
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
kind: PropTypes.oneOf(kinds.all),
|
kind: PropTypes.oneOf(kinds.all),
|
||||||
@ -284,8 +286,10 @@ FormInputGroup.propTypes = {
|
|||||||
helpTextWarning: PropTypes.string,
|
helpTextWarning: PropTypes.string,
|
||||||
helpLink: PropTypes.string,
|
helpLink: PropTypes.string,
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus: PropTypes.bool,
|
||||||
|
canEdit: PropTypes.bool,
|
||||||
includeNoChange: PropTypes.bool,
|
includeNoChange: PropTypes.bool,
|
||||||
includeNoChangeDisabled: PropTypes.bool,
|
includeNoChangeDisabled: PropTypes.bool,
|
||||||
|
includeAny: PropTypes.bool,
|
||||||
selectedValueOptions: PropTypes.object,
|
selectedValueOptions: PropTypes.object,
|
||||||
indexerFlags: PropTypes.number,
|
indexerFlags: PropTypes.number,
|
||||||
pending: PropTypes.bool,
|
pending: PropTypes.bool,
|
||||||
|
@ -19,7 +19,7 @@ function EditImportListExclusionModal(
|
|||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const onModalClosePress = useCallback(() => {
|
const handleModalClose = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
clearPendingChanges({
|
clearPendingChanges({
|
||||||
section: 'settings.importListExclusions',
|
section: 'settings.importListExclusions',
|
||||||
@ -29,10 +29,10 @@ function EditImportListExclusionModal(
|
|||||||
}, [dispatch, onModalClose]);
|
}, [dispatch, onModalClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal size={sizes.MEDIUM} isOpen={isOpen} onModalClose={onModalClosePress}>
|
<Modal size={sizes.MEDIUM} isOpen={isOpen} onModalClose={handleModalClose}>
|
||||||
<EditImportListExclusionModalContent
|
<EditImportListExclusionModalContent
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onModalClose={onModalClosePress}
|
onModalClose={handleModalClose}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@ -31,12 +31,6 @@ const newImportListExclusion = {
|
|||||||
tvdbId: 0,
|
tvdbId: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface EditImportListExclusionModalContentProps {
|
|
||||||
id?: number;
|
|
||||||
onModalClose: () => void;
|
|
||||||
onDeleteImportListExclusionPress?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createImportListExclusionSelector(id?: number) {
|
function createImportListExclusionSelector(id?: number) {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state: AppState) => state.settings.importListExclusions,
|
(state: AppState) => state.settings.importListExclusions,
|
||||||
@ -62,12 +56,24 @@ function createImportListExclusionSelector(id?: number) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditImportListExclusionModalContent(
|
interface EditImportListExclusionModalContentProps {
|
||||||
props: EditImportListExclusionModalContentProps
|
id?: number;
|
||||||
) {
|
onModalClose: () => void;
|
||||||
const { id, onModalClose, onDeleteImportListExclusionPress } = props;
|
onDeleteImportListExclusionPress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditImportListExclusionModalContent({
|
||||||
|
id,
|
||||||
|
onModalClose,
|
||||||
|
onDeleteImportListExclusionPress,
|
||||||
|
}: EditImportListExclusionModalContentProps) {
|
||||||
|
const { isFetching, isSaving, item, error, saveError, ...otherProps } =
|
||||||
|
useSelector(createImportListExclusionSelector(id));
|
||||||
|
|
||||||
|
const { title, tvdbId } = item;
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const previousIsSaving = usePrevious(isSaving);
|
||||||
|
|
||||||
const dispatchSetImportListExclusionValue = (payload: {
|
const dispatchSetImportListExclusionValue = (payload: {
|
||||||
name: string;
|
name: string;
|
||||||
@ -77,20 +83,10 @@ function EditImportListExclusionModalContent(
|
|||||||
dispatch(setImportListExclusionValue(payload));
|
dispatch(setImportListExclusionValue(payload));
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isFetching, isSaving, item, error, saveError, ...otherProps } =
|
|
||||||
useSelector(createImportListExclusionSelector(props.id));
|
|
||||||
const previousIsSaving = usePrevious(isSaving);
|
|
||||||
|
|
||||||
const { title, tvdbId } = item;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
Object.keys(newImportListExclusion).forEach((name) => {
|
Object.entries(newImportListExclusion).forEach(([name, value]) => {
|
||||||
dispatchSetImportListExclusionValue({
|
dispatchSetImportListExclusionValue({ name, value });
|
||||||
name,
|
|
||||||
value:
|
|
||||||
newImportListExclusion[name as keyof typeof newImportListExclusion],
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -100,7 +96,7 @@ function EditImportListExclusionModalContent(
|
|||||||
if (previousIsSaving && !isSaving && !saveError) {
|
if (previousIsSaving && !isSaving && !saveError) {
|
||||||
onModalClose();
|
onModalClose();
|
||||||
}
|
}
|
||||||
});
|
}, [previousIsSaving, isSaving, saveError, onModalClose]);
|
||||||
|
|
||||||
const onSavePress = useCallback(() => {
|
const onSavePress = useCallback(() => {
|
||||||
dispatch(saveImportListExclusion({ id }));
|
dispatch(saveImportListExclusion({ id }));
|
||||||
|
@ -7,7 +7,7 @@ import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import DelayProfilesConnector from './Delay/DelayProfilesConnector';
|
import DelayProfilesConnector from './Delay/DelayProfilesConnector';
|
||||||
import QualityProfilesConnector from './Quality/QualityProfilesConnector';
|
import QualityProfilesConnector from './Quality/QualityProfilesConnector';
|
||||||
import ReleaseProfilesConnector from './Release/ReleaseProfilesConnector';
|
import ReleaseProfiles from './Release/ReleaseProfiles';
|
||||||
|
|
||||||
// Only a single DragDrop Context can exist so it's done here to allow editing
|
// Only a single DragDrop Context can exist so it's done here to allow editing
|
||||||
// quality profiles and reordering delay profiles to work.
|
// quality profiles and reordering delay profiles to work.
|
||||||
@ -26,7 +26,7 @@ class Profiles extends Component {
|
|||||||
<DndProvider options={HTML5toTouch}>
|
<DndProvider options={HTML5toTouch}>
|
||||||
<QualityProfilesConnector />
|
<QualityProfilesConnector />
|
||||||
<DelayProfilesConnector />
|
<DelayProfilesConnector />
|
||||||
<ReleaseProfilesConnector />
|
<ReleaseProfiles />
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import EditReleaseProfileModalContentConnector from './EditReleaseProfileModalContentConnector';
|
|
||||||
|
|
||||||
function EditReleaseProfileModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<EditReleaseProfileModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditReleaseProfileModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditReleaseProfileModal;
|
|
@ -0,0 +1,41 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import { sizes } from 'Helpers/Props';
|
||||||
|
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||||
|
import EditReleaseProfileModalContent from './EditReleaseProfileModalContent';
|
||||||
|
|
||||||
|
interface EditReleaseProfileModalProps {
|
||||||
|
id?: number;
|
||||||
|
isOpen: boolean;
|
||||||
|
onModalClose: () => void;
|
||||||
|
onDeleteReleaseProfilePress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditReleaseProfileModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: EditReleaseProfileModalProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleModalClose = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
clearPendingChanges({
|
||||||
|
section: 'settings.releaseProfiles',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
onModalClose();
|
||||||
|
}, [dispatch, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal size={sizes.MEDIUM} isOpen={isOpen} onModalClose={handleModalClose}>
|
||||||
|
<EditReleaseProfileModalContent
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={handleModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditReleaseProfileModal;
|
@ -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 EditReleaseProfileModal from './EditReleaseProfileModal';
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
clearPendingChanges
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditReleaseProfileModalConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.clearPendingChanges({ section: 'settings.releaseProfiles' });
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditReleaseProfileModal
|
|
||||||
{...this.props}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditReleaseProfileModalConnector.propTypes = {
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
clearPendingChanges: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(EditReleaseProfileModalConnector);
|
|
@ -1,5 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import React from 'react';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
import Form from 'Components/Form/Form';
|
import Form from 'Components/Form/Form';
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
@ -10,33 +12,97 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||||||
import ModalContent from 'Components/Modal/ModalContent';
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import usePrevious from 'Helpers/Hooks/usePrevious';
|
||||||
import { inputTypes, kinds } from 'Helpers/Props';
|
import { inputTypes, kinds } from 'Helpers/Props';
|
||||||
|
import {
|
||||||
|
saveReleaseProfile,
|
||||||
|
setReleaseProfileValue,
|
||||||
|
} from 'Store/Actions/Settings/releaseProfiles';
|
||||||
|
import selectSettings from 'Store/Selectors/selectSettings';
|
||||||
|
import { PendingSection } from 'typings/pending';
|
||||||
|
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './EditReleaseProfileModalContent.css';
|
import styles from './EditReleaseProfileModalContent.css';
|
||||||
|
|
||||||
const tagInputDelimiters = ['Tab', 'Enter'];
|
const tagInputDelimiters = ['Tab', 'Enter'];
|
||||||
|
|
||||||
function EditReleaseProfileModalContent(props) {
|
const newReleaseProfile = {
|
||||||
const {
|
enabled: true,
|
||||||
isSaving,
|
required: [],
|
||||||
saveError,
|
ignored: [],
|
||||||
item,
|
tags: [],
|
||||||
onInputChange,
|
indexerId: 0,
|
||||||
onModalClose,
|
};
|
||||||
onSavePress,
|
|
||||||
onDeleteReleaseProfilePress,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const {
|
function createReleaseProfileSelector(id?: number) {
|
||||||
id,
|
return createSelector(
|
||||||
name,
|
(state: AppState) => state.settings.releaseProfiles,
|
||||||
enabled,
|
(releaseProfiles) => {
|
||||||
required,
|
const { items, isFetching, error, isSaving, saveError, pendingChanges } =
|
||||||
ignored,
|
releaseProfiles;
|
||||||
tags,
|
|
||||||
indexerId
|
const mapping = id ? items.find((i) => i.id === id) : newReleaseProfile;
|
||||||
} = item;
|
const settings = selectSettings(mapping, pendingChanges, saveError);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
isFetching,
|
||||||
|
error,
|
||||||
|
isSaving,
|
||||||
|
saveError,
|
||||||
|
item: settings.settings as PendingSection<ReleaseProfile>,
|
||||||
|
...settings,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditReleaseProfileModalContentProps {
|
||||||
|
id?: number;
|
||||||
|
onModalClose: () => void;
|
||||||
|
onDeleteReleaseProfilePress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditReleaseProfileModalContent({
|
||||||
|
id,
|
||||||
|
onModalClose,
|
||||||
|
onDeleteReleaseProfilePress,
|
||||||
|
}: EditReleaseProfileModalContentProps) {
|
||||||
|
const { item, isFetching, isSaving, error, saveError, ...otherProps } =
|
||||||
|
useSelector(createReleaseProfileSelector(id));
|
||||||
|
|
||||||
|
const { name, enabled, required, ignored, tags, indexerId } = item;
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const previousIsSaving = usePrevious(isSaving);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!id) {
|
||||||
|
Object.entries(newReleaseProfile).forEach(([name, value]) => {
|
||||||
|
// @ts-expect-error 'setReleaseProfileValue' isn't typed yet
|
||||||
|
dispatch(setReleaseProfileValue({ name, value }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (previousIsSaving && !isSaving && !saveError) {
|
||||||
|
onModalClose();
|
||||||
|
}
|
||||||
|
}, [previousIsSaving, isSaving, saveError, onModalClose]);
|
||||||
|
|
||||||
|
const handleSavePress = useCallback(() => {
|
||||||
|
dispatch(saveReleaseProfile({ id }));
|
||||||
|
}, [dispatch, id]);
|
||||||
|
|
||||||
|
const handleInputChange = useCallback(
|
||||||
|
(payload: { name: string; value: string | number }) => {
|
||||||
|
// @ts-expect-error 'setReleaseProfileValue' isn't typed yet
|
||||||
|
dispatch(setReleaseProfileValue(payload));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
@ -46,7 +112,6 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Form {...otherProps}>
|
<Form {...otherProps}>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{translate('Name')}</FormLabel>
|
<FormLabel>{translate('Name')}</FormLabel>
|
||||||
|
|
||||||
@ -56,7 +121,7 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
{...name}
|
{...name}
|
||||||
placeholder={translate('OptionalName')}
|
placeholder={translate('OptionalName')}
|
||||||
canEdit={true}
|
canEdit={true}
|
||||||
onChange={onInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@ -68,7 +133,7 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
name="enabled"
|
name="enabled"
|
||||||
helpText={translate('EnableProfileHelpText')}
|
helpText={translate('EnableProfileHelpText')}
|
||||||
{...enabled}
|
{...enabled}
|
||||||
onChange={onInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@ -85,7 +150,7 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
placeholder={translate('AddNewRestriction')}
|
placeholder={translate('AddNewRestriction')}
|
||||||
delimiters={tagInputDelimiters}
|
delimiters={tagInputDelimiters}
|
||||||
canEdit={true}
|
canEdit={true}
|
||||||
onChange={onInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@ -102,7 +167,7 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
placeholder={translate('AddNewRestriction')}
|
placeholder={translate('AddNewRestriction')}
|
||||||
delimiters={tagInputDelimiters}
|
delimiters={tagInputDelimiters}
|
||||||
canEdit={true}
|
canEdit={true}
|
||||||
onChange={onInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@ -113,10 +178,12 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
type={inputTypes.INDEXER_SELECT}
|
type={inputTypes.INDEXER_SELECT}
|
||||||
name="indexerId"
|
name="indexerId"
|
||||||
helpText={translate('ReleaseProfileIndexerHelpText')}
|
helpText={translate('ReleaseProfileIndexerHelpText')}
|
||||||
helpTextWarning={translate('ReleaseProfileIndexerHelpTextWarning')}
|
helpTextWarning={translate(
|
||||||
|
'ReleaseProfileIndexerHelpTextWarning'
|
||||||
|
)}
|
||||||
{...indexerId}
|
{...indexerId}
|
||||||
includeAny={true}
|
includeAny={true}
|
||||||
onChange={onInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
@ -128,33 +195,28 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
name="tags"
|
name="tags"
|
||||||
helpText={translate('ReleaseProfileTagSeriesHelpText')}
|
helpText={translate('ReleaseProfileTagSeriesHelpText')}
|
||||||
{...tags}
|
{...tags}
|
||||||
onChange={onInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
{
|
{id ? (
|
||||||
id &&
|
<Button
|
||||||
<Button
|
className={styles.deleteButton}
|
||||||
className={styles.deleteButton}
|
kind={kinds.DANGER}
|
||||||
kind={kinds.DANGER}
|
onPress={onDeleteReleaseProfilePress}
|
||||||
onPress={onDeleteReleaseProfilePress}
|
>
|
||||||
>
|
{translate('Delete')}
|
||||||
{translate('Delete')}
|
</Button>
|
||||||
</Button>
|
) : null}
|
||||||
}
|
|
||||||
|
|
||||||
<Button
|
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<SpinnerErrorButton
|
<SpinnerErrorButton
|
||||||
isSpinning={isSaving}
|
isSpinning={isSaving}
|
||||||
error={saveError}
|
error={saveError}
|
||||||
onPress={onSavePress}
|
onPress={handleSavePress}
|
||||||
>
|
>
|
||||||
{translate('Save')}
|
{translate('Save')}
|
||||||
</SpinnerErrorButton>
|
</SpinnerErrorButton>
|
||||||
@ -163,14 +225,4 @@ function EditReleaseProfileModalContent(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EditReleaseProfileModalContent.propTypes = {
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
onInputChange: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onSavePress: PropTypes.func.isRequired,
|
|
||||||
onDeleteReleaseProfilePress: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditReleaseProfileModalContent;
|
export default EditReleaseProfileModalContent;
|
@ -1,112 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { saveReleaseProfile, setReleaseProfileValue } from 'Store/Actions/settingsActions';
|
|
||||||
import selectSettings from 'Store/Selectors/selectSettings';
|
|
||||||
import EditReleaseProfileModalContent from './EditReleaseProfileModalContent';
|
|
||||||
|
|
||||||
const newReleaseProfile = {
|
|
||||||
enabled: true,
|
|
||||||
required: [],
|
|
||||||
ignored: [],
|
|
||||||
tags: [],
|
|
||||||
indexerId: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state, { id }) => id,
|
|
||||||
(state) => state.settings.releaseProfiles,
|
|
||||||
(id, releaseProfiles) => {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
error,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
pendingChanges,
|
|
||||||
items
|
|
||||||
} = releaseProfiles;
|
|
||||||
|
|
||||||
const profile = id ? items.find((i) => i.id === id) : newReleaseProfile;
|
|
||||||
const settings = selectSettings(profile, pendingChanges, saveError);
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
isFetching,
|
|
||||||
error,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
item: settings.settings,
|
|
||||||
...settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
setReleaseProfileValue,
|
|
||||||
saveReleaseProfile
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditReleaseProfileModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (!this.props.id) {
|
|
||||||
Object.keys(newReleaseProfile).forEach((name) => {
|
|
||||||
this.props.setReleaseProfileValue({
|
|
||||||
name,
|
|
||||||
value: newReleaseProfile[name]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.props.setReleaseProfileValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
this.props.saveReleaseProfile({ id: this.props.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditReleaseProfileModalContent
|
|
||||||
{...this.props}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
onTestPress={this.onTestPress}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onFieldChange={this.onFieldChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditReleaseProfileModalContentConnector.propTypes = {
|
|
||||||
id: PropTypes.number,
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
setReleaseProfileValue: PropTypes.func.isRequired,
|
|
||||||
saveReleaseProfile: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditReleaseProfileModalContentConnector);
|
|
@ -1,206 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import MiddleTruncate from 'react-middle-truncate';
|
|
||||||
import Card from 'Components/Card';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import TagList from 'Components/TagList';
|
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import EditReleaseProfileModalConnector from './EditReleaseProfileModalConnector';
|
|
||||||
import styles from './ReleaseProfile.css';
|
|
||||||
|
|
||||||
class ReleaseProfile extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isEditReleaseProfileModalOpen: false,
|
|
||||||
isDeleteReleaseProfileModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onEditReleaseProfilePress = () => {
|
|
||||||
this.setState({ isEditReleaseProfileModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onEditReleaseProfileModalClose = () => {
|
|
||||||
this.setState({ isEditReleaseProfileModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteReleaseProfilePress = () => {
|
|
||||||
this.setState({
|
|
||||||
isEditReleaseProfileModalOpen: false,
|
|
||||||
isDeleteReleaseProfileModalOpen: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteReleaseProfileModalClose = () => {
|
|
||||||
this.setState({ isDeleteReleaseProfileModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onConfirmDeleteReleaseProfile = () => {
|
|
||||||
this.props.onConfirmDeleteReleaseProfile(this.props.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
enabled,
|
|
||||||
required,
|
|
||||||
ignored,
|
|
||||||
tags,
|
|
||||||
indexerId,
|
|
||||||
tagList,
|
|
||||||
indexerList
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isEditReleaseProfileModalOpen,
|
|
||||||
isDeleteReleaseProfileModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const indexer = indexerId !== 0 && indexerList.find((i) => i.id === indexerId);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={styles.releaseProfile}
|
|
||||||
overlayContent={true}
|
|
||||||
onPress={this.onEditReleaseProfilePress}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
name ?
|
|
||||||
<div className={styles.name}>
|
|
||||||
{name}
|
|
||||||
</div> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
required.map((item) => {
|
|
||||||
if (!item) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
className={styles.label}
|
|
||||||
key={item}
|
|
||||||
kind={kinds.SUCCESS}
|
|
||||||
>
|
|
||||||
<MiddleTruncate
|
|
||||||
text={item}
|
|
||||||
start={10}
|
|
||||||
end={10}
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
ignored.map((item) => {
|
|
||||||
if (!item) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
className={styles.label}
|
|
||||||
key={item}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
>
|
|
||||||
<MiddleTruncate
|
|
||||||
text={item}
|
|
||||||
start={10}
|
|
||||||
end={10}
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagList
|
|
||||||
tags={tags}
|
|
||||||
tagList={tagList}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
!enabled &&
|
|
||||||
<Label
|
|
||||||
kind={kinds.DISABLED}
|
|
||||||
outline={true}
|
|
||||||
>
|
|
||||||
{translate('Disabled')}
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
indexer &&
|
|
||||||
<Label
|
|
||||||
kind={kinds.INFO}
|
|
||||||
outline={true}
|
|
||||||
>
|
|
||||||
{indexer.name}
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditReleaseProfileModalConnector
|
|
||||||
id={id}
|
|
||||||
isOpen={isEditReleaseProfileModalOpen}
|
|
||||||
onModalClose={this.onEditReleaseProfileModalClose}
|
|
||||||
onDeleteReleaseProfilePress={this.onDeleteReleaseProfilePress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isDeleteReleaseProfileModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('DeleteReleaseProfile')}
|
|
||||||
message={translate('DeleteReleaseProfileMessageText', { name })}
|
|
||||||
confirmLabel={translate('Delete')}
|
|
||||||
onConfirm={this.onConfirmDeleteReleaseProfile}
|
|
||||||
onCancel={this.onDeleteReleaseProfileModalClose}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseProfile.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string,
|
|
||||||
enabled: PropTypes.bool.isRequired,
|
|
||||||
required: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
ignored: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
indexerId: PropTypes.number.isRequired,
|
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
indexerList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onConfirmDeleteReleaseProfile: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
ReleaseProfile.defaultProps = {
|
|
||||||
enabled: true,
|
|
||||||
required: [],
|
|
||||||
ignored: [],
|
|
||||||
indexerId: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReleaseProfile;
|
|
132
frontend/src/Settings/Profiles/Release/ReleaseProfileRow.tsx
Normal file
132
frontend/src/Settings/Profiles/Release/ReleaseProfileRow.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
// @ts-expect-error 'MiddleTruncate' isn't typed
|
||||||
|
import MiddleTruncate from 'react-middle-truncate';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { Tag } from 'App/State/TagsAppState';
|
||||||
|
import Card from 'Components/Card';
|
||||||
|
import Label from 'Components/Label';
|
||||||
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
|
import TagList from 'Components/TagList';
|
||||||
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
|
import { kinds } from 'Helpers/Props';
|
||||||
|
import { deleteReleaseProfile } from 'Store/Actions/Settings/releaseProfiles';
|
||||||
|
import Indexer from 'typings/Indexer';
|
||||||
|
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import EditReleaseProfileModal from './EditReleaseProfileModal';
|
||||||
|
import styles from './ReleaseProfileRow.css';
|
||||||
|
|
||||||
|
interface ReleaseProfileProps extends ReleaseProfile {
|
||||||
|
tagList: Tag[];
|
||||||
|
indexerList: Indexer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReleaseProfileRow(props: ReleaseProfileProps) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
enabled = true,
|
||||||
|
required = [],
|
||||||
|
ignored = [],
|
||||||
|
tags,
|
||||||
|
indexerId = 0,
|
||||||
|
tagList,
|
||||||
|
indexerList,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const [
|
||||||
|
isEditReleaseProfileModalOpen,
|
||||||
|
setEditReleaseProfileModalOpen,
|
||||||
|
setEditReleaseProfileModalClosed,
|
||||||
|
] = useModalOpenState(false);
|
||||||
|
|
||||||
|
const [
|
||||||
|
isDeleteReleaseProfileModalOpen,
|
||||||
|
setDeleteReleaseProfileModalOpen,
|
||||||
|
setDeleteReleaseProfileModalClosed,
|
||||||
|
] = useModalOpenState(false);
|
||||||
|
|
||||||
|
const handleDeletePress = useCallback(() => {
|
||||||
|
dispatch(deleteReleaseProfile({ id }));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
const indexer =
|
||||||
|
indexerId !== 0 && indexerList.find((i) => i.id === indexerId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className={styles.releaseProfile}
|
||||||
|
overlayContent={true}
|
||||||
|
onPress={setEditReleaseProfileModalOpen}
|
||||||
|
>
|
||||||
|
{name ? <div className={styles.name}>{name}</div> : null}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{required.map((item) => {
|
||||||
|
if (!item) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label key={item} className={styles.label} kind={kinds.SUCCESS}>
|
||||||
|
<MiddleTruncate text={item} start={10} end={10} />
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{ignored.map((item) => {
|
||||||
|
if (!item) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label key={item} className={styles.label} kind={kinds.DANGER}>
|
||||||
|
<MiddleTruncate text={item} start={10} end={10} />
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TagList tags={tags} tagList={tagList} />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{enabled ? null : (
|
||||||
|
<Label kind={kinds.DISABLED} outline={true}>
|
||||||
|
{translate('Disabled')}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{indexer ? (
|
||||||
|
<Label kind={kinds.INFO} outline={true}>
|
||||||
|
{indexer.name}
|
||||||
|
</Label>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditReleaseProfileModal
|
||||||
|
id={id}
|
||||||
|
isOpen={isEditReleaseProfileModalOpen}
|
||||||
|
onModalClose={setEditReleaseProfileModalClosed}
|
||||||
|
onDeleteReleaseProfilePress={setDeleteReleaseProfileModalOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
isOpen={isDeleteReleaseProfileModalOpen}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('DeleteReleaseProfile')}
|
||||||
|
message={translate('DeleteReleaseProfileMessageText', {
|
||||||
|
name: name ?? id,
|
||||||
|
})}
|
||||||
|
confirmLabel={translate('Delete')}
|
||||||
|
onConfirm={handleDeletePress}
|
||||||
|
onCancel={setDeleteReleaseProfileModalClosed}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReleaseProfileRow;
|
@ -4,7 +4,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.addReleaseProfile {
|
.addReleaseProfile {
|
||||||
composes: releaseProfile from '~./ReleaseProfile.css';
|
composes: releaseProfile from '~./ReleaseProfileRow.css';
|
||||||
|
|
||||||
background-color: var(--cardAlternateBackgroundColor);
|
background-color: var(--cardAlternateBackgroundColor);
|
||||||
color: var(--gray);
|
color: var(--gray);
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Card from 'Components/Card';
|
|
||||||
import FieldSet from 'Components/FieldSet';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import EditReleaseProfileModalConnector from './EditReleaseProfileModalConnector';
|
|
||||||
import ReleaseProfile from './ReleaseProfile';
|
|
||||||
import styles from './ReleaseProfiles.css';
|
|
||||||
|
|
||||||
class ReleaseProfiles extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isAddReleaseProfileModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onAddReleaseProfilePress = () => {
|
|
||||||
this.setState({ isAddReleaseProfileModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onAddReleaseProfileModalClose = () => {
|
|
||||||
this.setState({ isAddReleaseProfileModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
items,
|
|
||||||
tagList,
|
|
||||||
indexerList,
|
|
||||||
onConfirmDeleteReleaseProfile,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend={translate('ReleaseProfiles')}>
|
|
||||||
<PageSectionContent
|
|
||||||
errorMessage={translate('ReleaseProfilesLoadError')}
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<div className={styles.releaseProfiles}>
|
|
||||||
<Card
|
|
||||||
className={styles.addReleaseProfile}
|
|
||||||
onPress={this.onAddReleaseProfilePress}
|
|
||||||
>
|
|
||||||
<div className={styles.center}>
|
|
||||||
<Icon
|
|
||||||
name={icons.ADD}
|
|
||||||
size={45}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<ReleaseProfile
|
|
||||||
key={item.id}
|
|
||||||
tagList={tagList}
|
|
||||||
indexerList={indexerList}
|
|
||||||
{...item}
|
|
||||||
onConfirmDeleteReleaseProfile={onConfirmDeleteReleaseProfile}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditReleaseProfileModalConnector
|
|
||||||
isOpen={this.state.isAddReleaseProfileModalOpen}
|
|
||||||
onModalClose={this.onAddReleaseProfileModalClose}
|
|
||||||
/>
|
|
||||||
</PageSectionContent>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseProfiles.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
indexerList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onConfirmDeleteReleaseProfile: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReleaseProfiles;
|
|
81
frontend/src/Settings/Profiles/Release/ReleaseProfiles.tsx
Normal file
81
frontend/src/Settings/Profiles/Release/ReleaseProfiles.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import { ReleaseProfilesAppState } from 'App/State/SettingsAppState';
|
||||||
|
import Card from 'Components/Card';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||||
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import { fetchIndexers } from 'Store/Actions/Settings/indexers';
|
||||||
|
import { fetchReleaseProfiles } from 'Store/Actions/Settings/releaseProfiles';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
|
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import EditReleaseProfileModal from './EditReleaseProfileModal';
|
||||||
|
import ReleaseProfileRow from './ReleaseProfileRow';
|
||||||
|
import styles from './ReleaseProfiles.css';
|
||||||
|
|
||||||
|
function ReleaseProfiles() {
|
||||||
|
const { items, isFetching, isPopulated, error }: ReleaseProfilesAppState =
|
||||||
|
useSelector(createClientSideCollectionSelector('settings.releaseProfiles'));
|
||||||
|
|
||||||
|
const tagList = useSelector(createTagsSelector());
|
||||||
|
const indexerList = useSelector(
|
||||||
|
(state: AppState) => state.settings.indexers.items
|
||||||
|
);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const [
|
||||||
|
isAddReleaseProfileModalOpen,
|
||||||
|
setAddReleaseProfileModalOpen,
|
||||||
|
setAddReleaseProfileModalClosed,
|
||||||
|
] = useModalOpenState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchReleaseProfiles());
|
||||||
|
dispatch(fetchIndexers());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet legend={translate('ReleaseProfiles')}>
|
||||||
|
<PageSectionContent
|
||||||
|
errorMessage={translate('ReleaseProfilesLoadError')}
|
||||||
|
isFetching={isFetching}
|
||||||
|
isPopulated={isPopulated}
|
||||||
|
error={error}
|
||||||
|
>
|
||||||
|
<div className={styles.releaseProfiles}>
|
||||||
|
<Card
|
||||||
|
className={styles.addReleaseProfile}
|
||||||
|
onPress={setAddReleaseProfileModalOpen}
|
||||||
|
>
|
||||||
|
<div className={styles.center}>
|
||||||
|
<Icon name={icons.ADD} size={45} />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{items.map((item) => {
|
||||||
|
return (
|
||||||
|
<ReleaseProfileRow
|
||||||
|
key={item.id}
|
||||||
|
tagList={tagList}
|
||||||
|
indexerList={indexerList}
|
||||||
|
{...item}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditReleaseProfileModal
|
||||||
|
isOpen={isAddReleaseProfileModalOpen}
|
||||||
|
onModalClose={setAddReleaseProfileModalClosed}
|
||||||
|
/>
|
||||||
|
</PageSectionContent>
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReleaseProfiles;
|
@ -1,70 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { deleteReleaseProfile, fetchIndexers, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
|
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
|
||||||
import ReleaseProfiles from './ReleaseProfiles';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.releaseProfiles,
|
|
||||||
(state) => state.settings.indexers,
|
|
||||||
createTagsSelector(),
|
|
||||||
(releaseProfiles, indexers, tagList) => {
|
|
||||||
return {
|
|
||||||
...releaseProfiles,
|
|
||||||
tagList,
|
|
||||||
isIndexersPopulated: indexers.isPopulated,
|
|
||||||
indexerList: indexers.items
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchIndexers,
|
|
||||||
fetchReleaseProfiles,
|
|
||||||
deleteReleaseProfile
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReleaseProfilesConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchReleaseProfiles();
|
|
||||||
if (!this.props.isIndexersPopulated) {
|
|
||||||
this.props.fetchIndexers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onConfirmDeleteReleaseProfile = (id) => {
|
|
||||||
this.props.deleteReleaseProfile({ id });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ReleaseProfiles
|
|
||||||
{...this.props}
|
|
||||||
onConfirmDeleteReleaseProfile={this.onConfirmDeleteReleaseProfile}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseProfilesConnector.propTypes = {
|
|
||||||
isIndexersPopulated: PropTypes.bool.isRequired,
|
|
||||||
fetchReleaseProfiles: PropTypes.func.isRequired,
|
|
||||||
deleteReleaseProfile: PropTypes.func.isRequired,
|
|
||||||
fetchIndexers: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(ReleaseProfilesConnector);
|
|
12
frontend/src/typings/Settings/ReleaseProfile.ts
Normal file
12
frontend/src/typings/Settings/ReleaseProfile.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import ModelBase from 'App/ModelBase';
|
||||||
|
|
||||||
|
interface ReleaseProfile extends ModelBase {
|
||||||
|
name: string;
|
||||||
|
enabled: boolean;
|
||||||
|
required: string[];
|
||||||
|
ignored: string[];
|
||||||
|
indexerId: number;
|
||||||
|
tags: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReleaseProfile;
|
@ -361,7 +361,7 @@
|
|||||||
"DeleteQualityProfile": "Delete Quality Profile",
|
"DeleteQualityProfile": "Delete Quality Profile",
|
||||||
"DeleteQualityProfileMessageText": "Are you sure you want to delete the quality profile '{name}'?",
|
"DeleteQualityProfileMessageText": "Are you sure you want to delete the quality profile '{name}'?",
|
||||||
"DeleteReleaseProfile": "Delete Release Profile",
|
"DeleteReleaseProfile": "Delete Release Profile",
|
||||||
"DeleteReleaseProfileMessageText": "Are you sure you want to delete this release profile '{name}'?",
|
"DeleteReleaseProfileMessageText": "Are you sure you want to delete the release profile '{name}'?",
|
||||||
"DeleteRemotePathMapping": "Delete Remote Path Mapping",
|
"DeleteRemotePathMapping": "Delete Remote Path Mapping",
|
||||||
"DeleteRemotePathMappingMessageText": "Are you sure you want to delete this remote path mapping?",
|
"DeleteRemotePathMappingMessageText": "Are you sure you want to delete this remote path mapping?",
|
||||||
"DeleteRootFolder": "Delete Root Folder",
|
"DeleteRootFolder": "Delete Root Folder",
|
||||||
|
Loading…
Reference in New Issue
Block a user