mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-19 21:46:50 -04:00
Convert ImportLists to TypeScript
(cherry picked from commit 10e3a237ef972540abcf4348bb56973d7ee19bd7)
This commit is contained in:
@@ -15,7 +15,7 @@ import MovieIndex from 'Movie/Index/MovieIndex';
|
|||||||
import CustomFormatSettingsPage from 'Settings/CustomFormats/CustomFormatSettingsPage';
|
import CustomFormatSettingsPage from 'Settings/CustomFormats/CustomFormatSettingsPage';
|
||||||
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
|
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
|
||||||
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
|
||||||
import ImportListSettingsConnector from 'Settings/ImportLists/ImportListSettingsConnector';
|
import ImportListSettings from 'Settings/ImportLists/ImportListSettings';
|
||||||
import IndexerSettings from 'Settings/Indexers/IndexerSettings';
|
import IndexerSettings from 'Settings/Indexers/IndexerSettings';
|
||||||
import MediaManagement from 'Settings/MediaManagement/MediaManagement';
|
import MediaManagement from 'Settings/MediaManagement/MediaManagement';
|
||||||
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
|
import MetadataSettings from 'Settings/Metadata/MetadataSettings';
|
||||||
@@ -117,10 +117,7 @@ function AppRoutes() {
|
|||||||
component={DownloadClientSettingsConnector}
|
component={DownloadClientSettingsConnector}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route path="/settings/importlists" component={ImportListSettings} />
|
||||||
path="/settings/importlists"
|
|
||||||
component={ImportListSettingsConnector}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route path="/settings/connect" component={NotificationSettings} />
|
<Route path="/settings/connect" component={NotificationSettings} />
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ export type NamingExamplesAppState = AppSectionItemState<NamingExample>;
|
|||||||
export interface ImportListAppState
|
export interface ImportListAppState
|
||||||
extends AppSectionState<ImportList>,
|
extends AppSectionState<ImportList>,
|
||||||
AppSectionDeleteState,
|
AppSectionDeleteState,
|
||||||
AppSectionSaveState {}
|
AppSectionSaveState,
|
||||||
|
AppSectionSchemaState<Presets<ImportList>> {
|
||||||
|
isTestingAll: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IndexerOptionsAppState
|
export interface IndexerOptionsAppState
|
||||||
extends AppSectionItemState<IndexerOptions>,
|
extends AppSectionItemState<IndexerOptions>,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Popover from 'Components/Tooltip/Popover';
|
|||||||
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
import { icons, kinds, sizes } from 'Helpers/Props';
|
import { icons, kinds, sizes } from 'Helpers/Props';
|
||||||
import MovieHeadshot from 'Movie/MovieHeadshot';
|
import MovieHeadshot from 'Movie/MovieHeadshot';
|
||||||
import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
|
import EditImportListModal from 'Settings/ImportLists/ImportLists/EditImportListModal';
|
||||||
import { deleteImportList } from 'Store/Actions/Settings/importLists';
|
import { deleteImportList } from 'Store/Actions/Settings/importLists';
|
||||||
import ImportList from 'typings/ImportList';
|
import ImportList from 'typings/ImportList';
|
||||||
import MovieCredit from 'typings/MovieCredit';
|
import MovieCredit from 'typings/MovieCredit';
|
||||||
@@ -154,7 +154,7 @@ function MovieCastPoster(props: MovieCastPosterProps) {
|
|||||||
{character}
|
{character}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EditImportListModalConnector
|
<EditImportListModal
|
||||||
id={importListId}
|
id={importListId}
|
||||||
isOpen={isEditImportListModalOpen}
|
isOpen={isEditImportListModalOpen}
|
||||||
onModalClose={setEditImportListModalClosed}
|
onModalClose={setEditImportListModalClosed}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Popover from 'Components/Tooltip/Popover';
|
|||||||
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
import { icons, kinds, sizes } from 'Helpers/Props';
|
import { icons, kinds, sizes } from 'Helpers/Props';
|
||||||
import MovieHeadshot from 'Movie/MovieHeadshot';
|
import MovieHeadshot from 'Movie/MovieHeadshot';
|
||||||
import EditImportListModalConnector from 'Settings/ImportLists/ImportLists/EditImportListModalConnector';
|
import EditImportListModal from 'Settings/ImportLists/ImportLists/EditImportListModal';
|
||||||
import { deleteImportList } from 'Store/Actions/Settings/importLists';
|
import { deleteImportList } from 'Store/Actions/Settings/importLists';
|
||||||
import ImportList from 'typings/ImportList';
|
import ImportList from 'typings/ImportList';
|
||||||
import MovieCredit from 'typings/MovieCredit';
|
import MovieCredit from 'typings/MovieCredit';
|
||||||
@@ -152,7 +152,7 @@ function MovieCrewPoster(props: MovieCrewPosterProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className={classNames(styles.title, 'swiper-no-swiping')}>{job}</div>
|
<div className={classNames(styles.title, 'swiper-no-swiping')}>{job}</div>
|
||||||
|
|
||||||
<EditImportListModalConnector
|
<EditImportListModal
|
||||||
id={importListId}
|
id={importListId}
|
||||||
isOpen={isEditImportListModalOpen}
|
isOpen={isEditImportListModalOpen}
|
||||||
onModalClose={setEditImportListModalClosed}
|
onModalClose={setEditImportListModalClosed}
|
||||||
|
|||||||
+2
-2
@@ -170,7 +170,7 @@ function EditImportListExclusionModalContent({
|
|||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
{id && (
|
{id ? (
|
||||||
<Button
|
<Button
|
||||||
className={styles.deleteButton}
|
className={styles.deleteButton}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
@@ -178,7 +178,7 @@ function EditImportListExclusionModalContent({
|
|||||||
>
|
>
|
||||||
{translate('Delete')}
|
{translate('Delete')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||||
|
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component, Fragment } from 'react';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
|
||||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
|
||||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import ImportListExclusions from './ImportListExclusions/ImportListExclusions';
|
|
||||||
import ImportListsConnector from './ImportLists/ImportListsConnector';
|
|
||||||
import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal';
|
|
||||||
import ImportListOptions from './Options/ImportListOptions';
|
|
||||||
|
|
||||||
class ImportListSettings extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this._saveCallback = null;
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isSaving: false,
|
|
||||||
hasPendingChanges: false,
|
|
||||||
isManageImportListsOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
setChildSave = (saveCallback) => {
|
|
||||||
this._saveCallback = saveCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
onChildStateChange = (payload) => {
|
|
||||||
this.setState(payload);
|
|
||||||
};
|
|
||||||
|
|
||||||
onManageImportListsPress = () => {
|
|
||||||
this.setState({ isManageImportListsOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onManageImportListsModalClose = () => {
|
|
||||||
this.setState({ isManageImportListsOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
if (this._saveCallback) {
|
|
||||||
this._saveCallback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isTestingAll,
|
|
||||||
dispatchTestAllImportLists
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isSaving,
|
|
||||||
hasPendingChanges,
|
|
||||||
isManageImportListsOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageContent title={translate('ImportListSettings')}>
|
|
||||||
<SettingsToolbar
|
|
||||||
isSaving={isSaving}
|
|
||||||
hasPendingChanges={hasPendingChanges}
|
|
||||||
additionalButtons={
|
|
||||||
<Fragment>
|
|
||||||
<PageToolbarSeparator />
|
|
||||||
|
|
||||||
<PageToolbarButton
|
|
||||||
label={translate('TestAllLists')}
|
|
||||||
iconName={icons.TEST}
|
|
||||||
isSpinning={isTestingAll}
|
|
||||||
onPress={dispatchTestAllImportLists}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PageToolbarButton
|
|
||||||
label={translate('ManageLists')}
|
|
||||||
iconName={icons.MANAGE}
|
|
||||||
onPress={this.onManageImportListsPress}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PageContentBody>
|
|
||||||
<ImportListsConnector />
|
|
||||||
|
|
||||||
<ImportListOptions
|
|
||||||
setChildSave={this.setChildSave}
|
|
||||||
onChildStateChange={this.onChildStateChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImportListExclusions />
|
|
||||||
|
|
||||||
<ManageImportListsModal
|
|
||||||
isOpen={isManageImportListsOpen}
|
|
||||||
onModalClose={this.onManageImportListsModalClose}
|
|
||||||
/>
|
|
||||||
</PageContentBody>
|
|
||||||
</PageContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportListSettings.propTypes = {
|
|
||||||
isTestingAll: PropTypes.bool.isRequired,
|
|
||||||
dispatchTestAllImportLists: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImportListSettings;
|
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import PageContent from 'Components/Page/PageContent';
|
||||||
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
|
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||||
|
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||||
|
import { testAllImportLists } from 'Store/Actions/settingsActions';
|
||||||
|
import {
|
||||||
|
SaveCallback,
|
||||||
|
SettingsStateChange,
|
||||||
|
} from 'typings/Settings/SettingsState';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import ImportListExclusions from './ImportListExclusions/ImportListExclusions';
|
||||||
|
import ImportLists from './ImportLists/ImportLists';
|
||||||
|
import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal';
|
||||||
|
import ImportListOptions from './Options/ImportListOptions';
|
||||||
|
|
||||||
|
function ImportListSettings() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const isTestingAll = useSelector(
|
||||||
|
(state: AppState) => state.settings.importLists.isTestingAll
|
||||||
|
);
|
||||||
|
|
||||||
|
const saveOptions = useRef<() => void>();
|
||||||
|
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
const [hasPendingChanges, setHasPendingChanges] = useState(false);
|
||||||
|
const [isManageImportListsModalOpen, setIsManageImportListsModalOpen] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
|
const handleSetChildSave = useCallback((saveCallback: SaveCallback) => {
|
||||||
|
saveOptions.current = saveCallback;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleChildStateChange = useCallback(
|
||||||
|
({ isSaving, hasPendingChanges }: SettingsStateChange) => {
|
||||||
|
setIsSaving(isSaving);
|
||||||
|
setHasPendingChanges(hasPendingChanges);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleManageImportListsPress = useCallback(() => {
|
||||||
|
setIsManageImportListsModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleManageImportListsModalClose = useCallback(() => {
|
||||||
|
setIsManageImportListsModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSavePress = useCallback(() => {
|
||||||
|
saveOptions.current?.();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleTestAllIndexersPress = useCallback(() => {
|
||||||
|
dispatch(testAllImportLists());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContent title={translate('ImportListSettings')}>
|
||||||
|
<SettingsToolbar
|
||||||
|
isSaving={isSaving}
|
||||||
|
hasPendingChanges={hasPendingChanges}
|
||||||
|
additionalButtons={
|
||||||
|
<>
|
||||||
|
<PageToolbarSeparator />
|
||||||
|
|
||||||
|
<PageToolbarButton
|
||||||
|
label={translate('TestAllLists')}
|
||||||
|
iconName={icons.TEST}
|
||||||
|
isSpinning={isTestingAll}
|
||||||
|
onPress={handleTestAllIndexersPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PageToolbarButton
|
||||||
|
label={translate('ManageLists')}
|
||||||
|
iconName={icons.MANAGE}
|
||||||
|
onPress={handleManageImportListsPress}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
onSavePress={handleSavePress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PageContentBody>
|
||||||
|
<ImportLists />
|
||||||
|
|
||||||
|
<ImportListOptions
|
||||||
|
setChildSave={handleSetChildSave}
|
||||||
|
onChildStateChange={handleChildStateChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImportListExclusions />
|
||||||
|
|
||||||
|
<ManageImportListsModal
|
||||||
|
isOpen={isManageImportListsModalOpen}
|
||||||
|
onModalClose={handleManageImportListsModalClose}
|
||||||
|
/>
|
||||||
|
</PageContentBody>
|
||||||
|
</PageContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImportListSettings;
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { testAllImportLists } from 'Store/Actions/settingsActions';
|
|
||||||
import ImportListSettings from './ImportListSettings';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.importLists.isTestingAll,
|
|
||||||
(isTestingAll) => {
|
|
||||||
return {
|
|
||||||
isTestingAll
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchTestAllImportLists: testAllImportLists
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(ImportListSettings);
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import Link from 'Components/Link/Link';
|
|
||||||
import Menu from 'Components/Menu/Menu';
|
|
||||||
import MenuContent from 'Components/Menu/MenuContent';
|
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import AddImportListPresetMenuItem from './AddImportListPresetMenuItem';
|
|
||||||
import styles from './AddImportListItem.css';
|
|
||||||
|
|
||||||
class AddImportListItem extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onImportListSelect = () => {
|
|
||||||
const {
|
|
||||||
implementation,
|
|
||||||
implementationName,
|
|
||||||
minRefreshInterval
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.props.onImportListSelect({ implementation, implementationName, minRefreshInterval });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
implementation,
|
|
||||||
implementationName,
|
|
||||||
minRefreshInterval,
|
|
||||||
infoLink,
|
|
||||||
presets,
|
|
||||||
onImportListSelect
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const hasPresets = !!presets && !!presets.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={styles.list}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
className={styles.underlay}
|
|
||||||
onPress={this.onImportListSelect}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.overlay}>
|
|
||||||
<div className={styles.name}>
|
|
||||||
{implementationName}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.actions}>
|
|
||||||
{
|
|
||||||
hasPresets &&
|
|
||||||
<span>
|
|
||||||
<Button
|
|
||||||
size={sizes.SMALL}
|
|
||||||
onPress={this.onImportListSelect}
|
|
||||||
>
|
|
||||||
{translate('Custom')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Menu className={styles.presetsMenu}>
|
|
||||||
<Button
|
|
||||||
className={styles.presetsMenuButton}
|
|
||||||
size={sizes.SMALL}
|
|
||||||
>
|
|
||||||
{translate('Presets')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<MenuContent>
|
|
||||||
{
|
|
||||||
presets.map((preset) => {
|
|
||||||
return (
|
|
||||||
<AddImportListPresetMenuItem
|
|
||||||
key={preset.name}
|
|
||||||
name={preset.name}
|
|
||||||
implementation={implementation}
|
|
||||||
implementationName={implementationName}
|
|
||||||
minRefreshInterval={minRefreshInterval}
|
|
||||||
onPress={onImportListSelect}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</MenuContent>
|
|
||||||
</Menu>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
to={infoLink}
|
|
||||||
size={sizes.SMALL}
|
|
||||||
>
|
|
||||||
{translate('MoreInfo')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddImportListItem.propTypes = {
|
|
||||||
implementation: PropTypes.string.isRequired,
|
|
||||||
implementationName: PropTypes.string.isRequired,
|
|
||||||
minRefreshInterval: PropTypes.string.isRequired,
|
|
||||||
infoLink: PropTypes.string.isRequired,
|
|
||||||
presets: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
onImportListSelect: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddImportListItem;
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Link from 'Components/Link/Link';
|
||||||
|
import Menu from 'Components/Menu/Menu';
|
||||||
|
import MenuContent from 'Components/Menu/MenuContent';
|
||||||
|
import { sizes } from 'Helpers/Props';
|
||||||
|
import { selectImportListSchema } from 'Store/Actions/settingsActions';
|
||||||
|
import ImportList from 'typings/ImportList';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import AddImportListPresetMenuItem from './AddImportListPresetMenuItem';
|
||||||
|
import styles from './AddImportListItem.css';
|
||||||
|
|
||||||
|
interface AddImportListItemProps {
|
||||||
|
implementation: string;
|
||||||
|
implementationName: string;
|
||||||
|
minRefreshInterval: string;
|
||||||
|
infoLink: string;
|
||||||
|
presets?: ImportList[];
|
||||||
|
onImportListSelect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddImportListItem({
|
||||||
|
implementation,
|
||||||
|
implementationName,
|
||||||
|
minRefreshInterval,
|
||||||
|
infoLink,
|
||||||
|
presets,
|
||||||
|
onImportListSelect,
|
||||||
|
}: AddImportListItemProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const hasPresets = !!(presets && presets.length);
|
||||||
|
|
||||||
|
const handleImportListSelect = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
selectImportListSchema({
|
||||||
|
implementation,
|
||||||
|
implementationName,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
onImportListSelect();
|
||||||
|
}, [implementation, implementationName, dispatch, onImportListSelect]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.list}>
|
||||||
|
<Link className={styles.underlay} onPress={handleImportListSelect} />
|
||||||
|
|
||||||
|
<div className={styles.overlay}>
|
||||||
|
<div className={styles.name}>{implementationName}</div>
|
||||||
|
|
||||||
|
<div className={styles.actions}>
|
||||||
|
{hasPresets && (
|
||||||
|
<span>
|
||||||
|
<Button size={sizes.SMALL} onPress={handleImportListSelect}>
|
||||||
|
{translate('Custom')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Menu className={styles.presetsMenu}>
|
||||||
|
<Button className={styles.presetsMenuButton} size={sizes.SMALL}>
|
||||||
|
{translate('Presets')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<MenuContent>
|
||||||
|
{presets.map((preset) => {
|
||||||
|
return (
|
||||||
|
<AddImportListPresetMenuItem
|
||||||
|
key={preset.name}
|
||||||
|
name={preset.name}
|
||||||
|
implementation={implementation}
|
||||||
|
implementationName={implementationName}
|
||||||
|
minRefreshInterval={minRefreshInterval}
|
||||||
|
onPress={onImportListSelect}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MenuContent>
|
||||||
|
</Menu>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button to={infoLink} size={sizes.SMALL}>
|
||||||
|
{translate('MoreInfo')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddImportListItem;
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import AddImportListModalContentConnector from './AddImportListModalContentConnector';
|
|
||||||
|
|
||||||
function AddImportListModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<AddImportListModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddImportListModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddImportListModal;
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import AddImportListModalContent, {
|
||||||
|
AddImportListModalContentProps,
|
||||||
|
} from './AddImportListModalContent';
|
||||||
|
|
||||||
|
interface AddImportListModalProps extends AddImportListModalContentProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddImportListModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: AddImportListModalProps) {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
||||||
|
<AddImportListModalContent {...otherProps} onModalClose={onModalClose} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddImportListModal;
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
import FieldSet from 'Components/FieldSet';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
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 titleCase from 'Utilities/String/titleCase';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import AddImportListItem from './AddImportListItem';
|
|
||||||
import styles from './AddImportListModalContent.css';
|
|
||||||
|
|
||||||
class AddImportListModalContent extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isSchemaFetching,
|
|
||||||
isSchemaPopulated,
|
|
||||||
schemaError,
|
|
||||||
listGroups,
|
|
||||||
onImportListSelect,
|
|
||||||
onModalClose
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('AddImportList')}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
{
|
|
||||||
isSchemaFetching ?
|
|
||||||
<LoadingIndicator /> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isSchemaFetching && !!schemaError ?
|
|
||||||
<Alert kind={kinds.DANGER}>
|
|
||||||
{translate('AddListError')}
|
|
||||||
</Alert> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isSchemaPopulated && !schemaError ?
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<Alert kind={kinds.INFO}>
|
|
||||||
<div>
|
|
||||||
{translate('SupportedListsMovie')}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{translate('SupportedListsMoreInfo')}
|
|
||||||
</div>
|
|
||||||
</Alert>
|
|
||||||
{
|
|
||||||
Object.keys(listGroups).map((key) => {
|
|
||||||
return (
|
|
||||||
<FieldSet key={key} legend={translate('TypeOfList', {
|
|
||||||
typeOfList: titleCase(key)
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div className={styles.lists}>
|
|
||||||
{
|
|
||||||
listGroups[key].map((list) => {
|
|
||||||
return (
|
|
||||||
<AddImportListItem
|
|
||||||
key={list.implementation}
|
|
||||||
implementation={list.implementation}
|
|
||||||
{...list}
|
|
||||||
onImportListSelect={onImportListSelect}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Close')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddImportListModalContent.propTypes = {
|
|
||||||
isSchemaFetching: PropTypes.bool.isRequired,
|
|
||||||
isSchemaPopulated: PropTypes.bool.isRequired,
|
|
||||||
schemaError: PropTypes.object,
|
|
||||||
listGroups: PropTypes.object.isRequired,
|
|
||||||
onImportListSelect: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddImportListModalContent;
|
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import React, { useEffect, useMemo } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
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 { fetchImportListSchema } from 'Store/Actions/settingsActions';
|
||||||
|
import ImportList from 'typings/ImportList';
|
||||||
|
import titleCase from 'Utilities/String/titleCase';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import AddImportListItem from './AddImportListItem';
|
||||||
|
import styles from './AddImportListModalContent.css';
|
||||||
|
|
||||||
|
export interface AddImportListModalContentProps {
|
||||||
|
onImportListSelect: () => void;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddImportListModalContent({
|
||||||
|
onImportListSelect,
|
||||||
|
onModalClose,
|
||||||
|
}: AddImportListModalContentProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { isSchemaFetching, isSchemaPopulated, schemaError, schema } =
|
||||||
|
useSelector((state: AppState) => state.settings.importLists);
|
||||||
|
|
||||||
|
const listGroups = useMemo(() => {
|
||||||
|
const result = schema.reduce<Record<string, ImportList[]>>((acc, item) => {
|
||||||
|
if (!acc[item.listType]) {
|
||||||
|
acc[item.listType] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[item.listType].push(item);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Sort the lists by listOrder after grouping them
|
||||||
|
Object.keys(result).forEach((key) => {
|
||||||
|
result[key].sort((a, b) => {
|
||||||
|
return a.listOrder - b.listOrder;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [schema]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchImportListSchema());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>{translate('AddImportList')}</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
{isSchemaFetching ? <LoadingIndicator /> : null}
|
||||||
|
|
||||||
|
{!isSchemaFetching && !!schemaError ? (
|
||||||
|
<Alert kind={kinds.DANGER}>{translate('AddListError')}</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{isSchemaPopulated && !schemaError ? (
|
||||||
|
<div>
|
||||||
|
<Alert kind={kinds.INFO}>
|
||||||
|
<div>{translate('SupportedListsMovie')}</div>
|
||||||
|
<div>{translate('SupportedListsMoreInfo')}</div>
|
||||||
|
</Alert>
|
||||||
|
{Object.keys(listGroups).map((key) => {
|
||||||
|
return (
|
||||||
|
<FieldSet
|
||||||
|
key={key}
|
||||||
|
legend={translate('TypeOfList', {
|
||||||
|
typeOfList: titleCase(key),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className={styles.lists}>
|
||||||
|
{listGroups[key].map((list) => {
|
||||||
|
return (
|
||||||
|
<AddImportListItem
|
||||||
|
key={list.implementation}
|
||||||
|
{...list}
|
||||||
|
implementation={list.implementation}
|
||||||
|
onImportListSelect={onImportListSelect}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>{translate('Close')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddImportListModalContent;
|
||||||
@@ -1,76 +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 { fetchImportListSchema, selectImportListSchema } from 'Store/Actions/settingsActions';
|
|
||||||
import AddImportListModalContent from './AddImportListModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.importLists,
|
|
||||||
(importLists) => {
|
|
||||||
const {
|
|
||||||
isSchemaFetching,
|
|
||||||
isSchemaPopulated,
|
|
||||||
schemaError,
|
|
||||||
schema
|
|
||||||
} = importLists;
|
|
||||||
|
|
||||||
const listGroups = _.chain(schema)
|
|
||||||
.sortBy((o) => o.listOrder)
|
|
||||||
.groupBy('listType')
|
|
||||||
.value();
|
|
||||||
|
|
||||||
return {
|
|
||||||
isSchemaFetching,
|
|
||||||
isSchemaPopulated,
|
|
||||||
schemaError,
|
|
||||||
listGroups
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchImportListSchema,
|
|
||||||
selectImportListSchema
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddImportListModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchImportListSchema();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onImportListSelect = ({ implementation, implementationName, name, minRefreshInterval }) => {
|
|
||||||
this.props.selectImportListSchema({ implementation, implementationName, presetName: name, minRefreshInterval });
|
|
||||||
this.props.onModalClose({ listSelected: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<AddImportListModalContent
|
|
||||||
{...this.props}
|
|
||||||
onImportListSelect={this.onImportListSelect}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddImportListModalContentConnector.propTypes = {
|
|
||||||
fetchImportListSchema: PropTypes.func.isRequired,
|
|
||||||
selectImportListSchema: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(AddImportListModalContentConnector);
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import MenuItem from 'Components/Menu/MenuItem';
|
|
||||||
|
|
||||||
class AddImportListPresetMenuItem extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onPress = () => {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
implementation,
|
|
||||||
implementationName,
|
|
||||||
minRefreshInterval
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
this.props.onPress({
|
|
||||||
name,
|
|
||||||
implementation,
|
|
||||||
implementationName,
|
|
||||||
minRefreshInterval
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
implementation,
|
|
||||||
implementationName,
|
|
||||||
minRefreshInterval,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
{...otherProps}
|
|
||||||
onPress={this.onPress}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddImportListPresetMenuItem.propTypes = {
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
implementation: PropTypes.string.isRequired,
|
|
||||||
implementationName: PropTypes.string.isRequired,
|
|
||||||
minRefreshInterval: PropTypes.string.isRequired,
|
|
||||||
onPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddImportListPresetMenuItem;
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import MenuItem, { MenuItemProps } from 'Components/Menu/MenuItem';
|
||||||
|
import { selectImportListSchema } from 'Store/Actions/settingsActions';
|
||||||
|
|
||||||
|
interface AddImportListPresetMenuItemProps
|
||||||
|
extends Omit<MenuItemProps, 'children'> {
|
||||||
|
name: string;
|
||||||
|
implementation: string;
|
||||||
|
implementationName: string;
|
||||||
|
minRefreshInterval: string;
|
||||||
|
onPress: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddImportListPresetMenuItem({
|
||||||
|
name,
|
||||||
|
implementation,
|
||||||
|
implementationName,
|
||||||
|
minRefreshInterval,
|
||||||
|
onPress,
|
||||||
|
...otherProps
|
||||||
|
}: AddImportListPresetMenuItemProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handlePress = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
selectImportListSchema({
|
||||||
|
implementation,
|
||||||
|
implementationName,
|
||||||
|
presetName: name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
onPress();
|
||||||
|
}, [name, implementation, implementationName, dispatch, onPress]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem {...otherProps} onPress={handlePress}>
|
||||||
|
{name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddImportListPresetMenuItem;
|
||||||
@@ -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 EditImportListModalContentConnector from './EditImportListModalContentConnector';
|
|
||||||
|
|
||||||
function EditImportListModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<EditImportListModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditImportListModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditImportListModal;
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||||
|
import {
|
||||||
|
cancelSaveImportList,
|
||||||
|
cancelTestImportList,
|
||||||
|
} from 'Store/Actions/settingsActions';
|
||||||
|
import EditImportListModalContent, {
|
||||||
|
EditImportListModalContentProps,
|
||||||
|
} from './EditImportListModalContent';
|
||||||
|
|
||||||
|
const section = 'settings.importLists';
|
||||||
|
|
||||||
|
interface EditImportListModalProps extends EditImportListModalContentProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditImportListModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: EditImportListModalProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleModalClose = useCallback(() => {
|
||||||
|
dispatch(clearPendingChanges({ section }));
|
||||||
|
dispatch(cancelTestImportList({ section }));
|
||||||
|
dispatch(cancelSaveImportList({ section }));
|
||||||
|
|
||||||
|
onModalClose();
|
||||||
|
}, [dispatch, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onModalClose={handleModalClose}>
|
||||||
|
<EditImportListModalContent
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={handleModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditImportListModal;
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|
||||||
import { cancelSaveImportList, cancelTestImportList } from 'Store/Actions/settingsActions';
|
|
||||||
import EditImportListModal from './EditImportListModal';
|
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
|
||||||
const section = 'settings.importLists';
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchClearPendingChanges() {
|
|
||||||
dispatch(clearPendingChanges({ section }));
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchCancelTestImportList() {
|
|
||||||
dispatch(cancelTestImportList({ section }));
|
|
||||||
},
|
|
||||||
|
|
||||||
dispatchCancelSaveImportList() {
|
|
||||||
dispatch(cancelSaveImportList({ section }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditImportListModalConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.dispatchClearPendingChanges();
|
|
||||||
this.props.dispatchCancelTestImportList();
|
|
||||||
this.props.dispatchCancelSaveImportList();
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
dispatchClearPendingChanges,
|
|
||||||
dispatchCancelTestImportList,
|
|
||||||
dispatchCancelSaveImportList,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EditImportListModal
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditImportListModalConnector.propTypes = {
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
dispatchClearPendingChanges: PropTypes.func.isRequired,
|
|
||||||
dispatchCancelTestImportList: PropTypes.func.isRequired,
|
|
||||||
dispatchCancelSaveImportList: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(null, createMapDispatchToProps)(EditImportListModalConnector);
|
|
||||||
@@ -1,305 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import MovieMinimumAvailabilityPopoverContent from 'AddMovie/MovieMinimumAvailabilityPopoverContent';
|
|
||||||
import Alert from 'Components/Alert';
|
|
||||||
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 ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
|
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
|
||||||
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 Popover from 'Components/Tooltip/Popover';
|
|
||||||
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
|
|
||||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
|
||||||
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './EditImportListModalContent.css';
|
|
||||||
|
|
||||||
function EditImportListModalContent(props) {
|
|
||||||
const {
|
|
||||||
advancedSettings,
|
|
||||||
isFetching,
|
|
||||||
error,
|
|
||||||
isSaving,
|
|
||||||
isTesting,
|
|
||||||
saveError,
|
|
||||||
item,
|
|
||||||
onInputChange,
|
|
||||||
onFieldChange,
|
|
||||||
onModalClose,
|
|
||||||
onSavePress,
|
|
||||||
onTestPress,
|
|
||||||
onDeleteImportListPress,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
implementationName,
|
|
||||||
name,
|
|
||||||
enabled,
|
|
||||||
enableAuto,
|
|
||||||
minRefreshInterval,
|
|
||||||
monitor,
|
|
||||||
minimumAvailability,
|
|
||||||
qualityProfileId,
|
|
||||||
rootFolderPath,
|
|
||||||
searchOnAdd,
|
|
||||||
tags,
|
|
||||||
fields,
|
|
||||||
message
|
|
||||||
} = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{id ? translate('EditImportListImplementation', { implementationName }) : translate('AddImportListImplementation', { implementationName })}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
{
|
|
||||||
isFetching ?
|
|
||||||
<LoadingIndicator /> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !!error ?
|
|
||||||
<Alert kind={kinds.DANGER}>
|
|
||||||
{translate('AddListError')}
|
|
||||||
</Alert> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !error ?
|
|
||||||
<Form
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
!!message &&
|
|
||||||
<Alert
|
|
||||||
className={styles.message}
|
|
||||||
kind={message.value.type}
|
|
||||||
>
|
|
||||||
{message.value.message}
|
|
||||||
</Alert>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Alert
|
|
||||||
kind={kinds.INFO}
|
|
||||||
className={styles.message}
|
|
||||||
>
|
|
||||||
{translate('ListWillRefreshEveryInterval', {
|
|
||||||
refreshInterval: formatShortTimeSpan(minRefreshInterval.value)
|
|
||||||
})}
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Name')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TEXT}
|
|
||||||
name="name"
|
|
||||||
{...name}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Enable')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="enabled"
|
|
||||||
helpText={translate('ListEnabledHelpText')}
|
|
||||||
{...enabled}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('EnableAutomaticAdd')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="enableAuto"
|
|
||||||
helpText={translate('EnableAutomaticAddMovieHelpText')}
|
|
||||||
{...enableAuto}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Monitor')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.MONITOR_MOVIES_SELECT}
|
|
||||||
name="monitor"
|
|
||||||
helpText={translate('ListMonitorMovieHelpText')}
|
|
||||||
{...monitor}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('SearchOnAdd')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="searchOnAdd"
|
|
||||||
helpText={translate('ListSearchOnAddMovieHelpText')}
|
|
||||||
{...searchOnAdd}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>
|
|
||||||
{translate('MinimumAvailability')}
|
|
||||||
|
|
||||||
<Popover
|
|
||||||
anchor={
|
|
||||||
<Icon
|
|
||||||
className={styles.labelIcon}
|
|
||||||
name={icons.INFO}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
title={translate('MinimumAvailability')}
|
|
||||||
body={<MovieMinimumAvailabilityPopoverContent />}
|
|
||||||
position={tooltipPositions.RIGHT}
|
|
||||||
/>
|
|
||||||
</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.AVAILABILITY_SELECT}
|
|
||||||
name="minimumAvailability"
|
|
||||||
{...minimumAvailability}
|
|
||||||
onChange={onInputChange}
|
|
||||||
helpLink="https://wiki.servarr.com/radarr/faq#what-is-minimum-availability"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.QUALITY_PROFILE_SELECT}
|
|
||||||
name="qualityProfileId"
|
|
||||||
helpText={translate('ListQualityProfileHelpText')}
|
|
||||||
{...qualityProfileId}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RootFolder')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.ROOT_FOLDER_SELECT}
|
|
||||||
name="rootFolderPath"
|
|
||||||
helpText={translate('ListRootFolderHelpText')}
|
|
||||||
{...rootFolderPath}
|
|
||||||
includeMissingValue={true}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RadarrTags')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TAG}
|
|
||||||
name="tags"
|
|
||||||
helpText={translate('ListTagsHelpText')}
|
|
||||||
{...tags}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{
|
|
||||||
fields.map((field) => {
|
|
||||||
return (
|
|
||||||
<ProviderFieldFormGroup
|
|
||||||
key={field.name}
|
|
||||||
advancedSettings={advancedSettings}
|
|
||||||
provider="importList"
|
|
||||||
providerData={item}
|
|
||||||
{...field}
|
|
||||||
onChange={onFieldChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</Form> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
{
|
|
||||||
id &&
|
|
||||||
<Button
|
|
||||||
className={styles.deleteButton}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={onDeleteImportListPress}
|
|
||||||
>
|
|
||||||
{translate('Delete')}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
|
|
||||||
<AdvancedSettingsButton
|
|
||||||
advancedSettings={advancedSettings}
|
|
||||||
showLabel={false}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SpinnerErrorButton
|
|
||||||
isSpinning={isTesting}
|
|
||||||
error={saveError}
|
|
||||||
onPress={onTestPress}
|
|
||||||
>
|
|
||||||
{translate('Test')}
|
|
||||||
</SpinnerErrorButton>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<SpinnerErrorButton
|
|
||||||
isSpinning={isSaving}
|
|
||||||
error={saveError}
|
|
||||||
onPress={onSavePress}
|
|
||||||
>
|
|
||||||
{translate('Save')}
|
|
||||||
</SpinnerErrorButton>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditImportListModalContent.propTypes = {
|
|
||||||
advancedSettings: PropTypes.bool.isRequired,
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
isTesting: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
onInputChange: PropTypes.func.isRequired,
|
|
||||||
onFieldChange: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onSavePress: PropTypes.func.isRequired,
|
|
||||||
onTestPress: PropTypes.func.isRequired,
|
|
||||||
onDeleteImportListPress: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditImportListModalContent;
|
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import MovieMinimumAvailabilityPopoverContent from 'AddMovie/MovieMinimumAvailabilityPopoverContent';
|
||||||
|
import { ImportListAppState } from 'App/State/SettingsAppState';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
|
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 ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
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 Popover from 'Components/Tooltip/Popover';
|
||||||
|
import usePrevious from 'Helpers/Hooks/usePrevious';
|
||||||
|
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
|
||||||
|
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
|
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||||
|
import {
|
||||||
|
saveImportList,
|
||||||
|
setImportListFieldValue,
|
||||||
|
setImportListValue,
|
||||||
|
testImportList,
|
||||||
|
} from 'Store/Actions/settingsActions';
|
||||||
|
import { createProviderSettingsSelectorHook } from 'Store/Selectors/createProviderSettingsSelector';
|
||||||
|
import ImportList from 'typings/ImportList';
|
||||||
|
import { InputChanged } from 'typings/inputs';
|
||||||
|
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './EditImportListModalContent.css';
|
||||||
|
|
||||||
|
export interface EditImportListModalContentProps {
|
||||||
|
id?: number;
|
||||||
|
onModalClose: () => void;
|
||||||
|
onDeleteImportListPress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditImportListModalContent({
|
||||||
|
id,
|
||||||
|
onModalClose,
|
||||||
|
onDeleteImportListPress,
|
||||||
|
}: EditImportListModalContentProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const showAdvancedSettings = useShowAdvancedSettings();
|
||||||
|
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isSaving,
|
||||||
|
isTesting = false,
|
||||||
|
error,
|
||||||
|
saveError,
|
||||||
|
item,
|
||||||
|
validationErrors,
|
||||||
|
validationWarnings,
|
||||||
|
} = useSelector(
|
||||||
|
createProviderSettingsSelectorHook<ImportList, ImportListAppState>(
|
||||||
|
'importLists',
|
||||||
|
id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const wasSaving = usePrevious(isSaving);
|
||||||
|
|
||||||
|
const {
|
||||||
|
implementationName,
|
||||||
|
name,
|
||||||
|
enabled,
|
||||||
|
enableAuto,
|
||||||
|
minRefreshInterval,
|
||||||
|
monitor,
|
||||||
|
minimumAvailability,
|
||||||
|
rootFolderPath,
|
||||||
|
qualityProfileId,
|
||||||
|
searchOnAdd,
|
||||||
|
tags,
|
||||||
|
fields,
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
const handleInputChange = useCallback(
|
||||||
|
(change: InputChanged) => {
|
||||||
|
// @ts-expect-error - actions are not typed
|
||||||
|
dispatch(setImportListValue(change));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFieldChange = useCallback(
|
||||||
|
(change: InputChanged) => {
|
||||||
|
// @ts-expect-error - actions are not typed
|
||||||
|
dispatch(setImportListFieldValue(change));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTestPress = useCallback(() => {
|
||||||
|
dispatch(testImportList({ id }));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
const handleSavePress = useCallback(() => {
|
||||||
|
dispatch(saveImportList({ id }));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (wasSaving && !isSaving && !saveError) {
|
||||||
|
onModalClose();
|
||||||
|
}
|
||||||
|
}, [isSaving, wasSaving, saveError, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{id
|
||||||
|
? translate('EditImportListImplementation', { implementationName })
|
||||||
|
: translate('AddImportListImplementation', { implementationName })}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
{isFetching ? <LoadingIndicator /> : null}
|
||||||
|
|
||||||
|
{!isFetching && !!error ? (
|
||||||
|
<Alert kind={kinds.DANGER}>{translate('AddListError')}</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!isFetching && !error ? (
|
||||||
|
<Form
|
||||||
|
validationErrors={validationErrors}
|
||||||
|
validationWarnings={validationWarnings}
|
||||||
|
>
|
||||||
|
<Alert kind={kinds.INFO} className={styles.message}>
|
||||||
|
{translate('ListWillRefreshEveryInterval', {
|
||||||
|
refreshInterval: formatShortTimeSpan(minRefreshInterval.value),
|
||||||
|
})}
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('Name')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.TEXT}
|
||||||
|
name="name"
|
||||||
|
{...name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('Enable')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="enabled"
|
||||||
|
helpText={translate('ListEnabledHelpText')}
|
||||||
|
{...enabled}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('EnableAutomaticAdd')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="enableAuto"
|
||||||
|
helpText={translate('EnableAutomaticAddMovieHelpText')}
|
||||||
|
{...enableAuto}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('Monitor')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.MONITOR_MOVIES_SELECT}
|
||||||
|
name="monitor"
|
||||||
|
helpText={translate('ListMonitorMovieHelpText')}
|
||||||
|
{...monitor}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('SearchOnAdd')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="searchOnAdd"
|
||||||
|
helpText={translate('ListSearchOnAddMovieHelpText')}
|
||||||
|
{...searchOnAdd}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>
|
||||||
|
{translate('MinimumAvailability')}
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
anchor={
|
||||||
|
<Icon className={styles.labelIcon} name={icons.INFO} />
|
||||||
|
}
|
||||||
|
title={translate('MinimumAvailability')}
|
||||||
|
body={<MovieMinimumAvailabilityPopoverContent />}
|
||||||
|
position={tooltipPositions.RIGHT}
|
||||||
|
/>
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.AVAILABILITY_SELECT}
|
||||||
|
name="minimumAvailability"
|
||||||
|
{...minimumAvailability}
|
||||||
|
helpLink="https://wiki.servarr.com/radarr/faq#what-is-minimum-availability"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('QualityProfile')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.QUALITY_PROFILE_SELECT}
|
||||||
|
name="qualityProfileId"
|
||||||
|
helpText={translate('ListQualityProfileHelpText')}
|
||||||
|
{...qualityProfileId}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RootFolder')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.ROOT_FOLDER_SELECT}
|
||||||
|
name="rootFolderPath"
|
||||||
|
helpText={translate('ListRootFolderHelpText')}
|
||||||
|
{...rootFolderPath}
|
||||||
|
includeMissingValue={true}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RadarrTags')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.TAG}
|
||||||
|
name="tags"
|
||||||
|
helpText={translate('ListTagsHelpText')}
|
||||||
|
{...tags}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{fields?.length ? (
|
||||||
|
<div>
|
||||||
|
{fields.map((field) => {
|
||||||
|
return (
|
||||||
|
<ProviderFieldFormGroup
|
||||||
|
key={field.name}
|
||||||
|
{...field}
|
||||||
|
advancedSettings={showAdvancedSettings}
|
||||||
|
provider="importList"
|
||||||
|
providerData={item}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Form>
|
||||||
|
) : null}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
{id ? (
|
||||||
|
<Button
|
||||||
|
className={styles.deleteButton}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
onPress={onDeleteImportListPress}
|
||||||
|
>
|
||||||
|
{translate('Delete')}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<AdvancedSettingsButton showLabel={false} />
|
||||||
|
|
||||||
|
<SpinnerErrorButton
|
||||||
|
isSpinning={isTesting}
|
||||||
|
error={saveError}
|
||||||
|
onPress={handleTestPress}
|
||||||
|
>
|
||||||
|
{translate('Test')}
|
||||||
|
</SpinnerErrorButton>
|
||||||
|
|
||||||
|
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||||
|
|
||||||
|
<SpinnerErrorButton
|
||||||
|
isSpinning={isSaving}
|
||||||
|
error={saveError}
|
||||||
|
onPress={handleSavePress}
|
||||||
|
>
|
||||||
|
{translate('Save')}
|
||||||
|
</SpinnerErrorButton>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditImportListModalContent;
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import {
|
|
||||||
saveImportList,
|
|
||||||
setImportListFieldValue,
|
|
||||||
setImportListValue,
|
|
||||||
testImportList
|
|
||||||
} from 'Store/Actions/settingsActions';
|
|
||||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
|
||||||
import EditImportListModalContent from './EditImportListModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.advancedSettings,
|
|
||||||
createProviderSettingsSelector('importLists'),
|
|
||||||
(advancedSettings, importList) => {
|
|
||||||
return {
|
|
||||||
advancedSettings,
|
|
||||||
...importList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
setImportListValue,
|
|
||||||
setImportListFieldValue,
|
|
||||||
saveImportList,
|
|
||||||
testImportList
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditImportListModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.props.setImportListValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onFieldChange = ({ name, value }) => {
|
|
||||||
this.props.setImportListFieldValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
this.props.saveImportList({ id: this.props.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
onTestPress = () => {
|
|
||||||
this.props.testImportList({ id: this.props.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditImportListModalContent
|
|
||||||
{...this.props}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
onTestPress={this.onTestPress}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onFieldChange={this.onFieldChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditImportListModalContentConnector.propTypes = {
|
|
||||||
id: PropTypes.number,
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
setImportListValue: PropTypes.func.isRequired,
|
|
||||||
setImportListFieldValue: PropTypes.func.isRequired,
|
|
||||||
saveImportList: PropTypes.func.isRequired,
|
|
||||||
testImportList: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditImportListModalContentConnector);
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
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 formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import EditImportListModalConnector from './EditImportListModalConnector';
|
|
||||||
import styles from './ImportList.css';
|
|
||||||
|
|
||||||
class ImportList extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isEditImportListModalOpen: false,
|
|
||||||
isDeleteImportListModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onEditImportListPress = () => {
|
|
||||||
this.setState({ isEditImportListModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onEditImportListModalClose = () => {
|
|
||||||
this.setState({ isEditImportListModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteImportListPress = () => {
|
|
||||||
this.setState({
|
|
||||||
isEditImportListModalOpen: false,
|
|
||||||
isDeleteImportListModalOpen: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteImportListModalClose = () => {
|
|
||||||
this.setState({ isDeleteImportListModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onConfirmDeleteImportList = () => {
|
|
||||||
this.props.onConfirmDeleteImportList(this.props.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
enabled,
|
|
||||||
enableAuto,
|
|
||||||
tags,
|
|
||||||
tagList,
|
|
||||||
minRefreshInterval
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={styles.list}
|
|
||||||
overlayContent={true}
|
|
||||||
onPress={this.onEditImportListPress}
|
|
||||||
>
|
|
||||||
<div className={styles.name}>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.enabled}>
|
|
||||||
{
|
|
||||||
enabled ?
|
|
||||||
<Label kind={kinds.SUCCESS}>
|
|
||||||
{translate('Enabled')}
|
|
||||||
</Label> :
|
|
||||||
<Label
|
|
||||||
kind={kinds.DISABLED}
|
|
||||||
outline={true}
|
|
||||||
>
|
|
||||||
{translate('Disabled')}
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
enableAuto ?
|
|
||||||
<Label kind={kinds.SUCCESS}>
|
|
||||||
{translate('AutomaticAdd')}
|
|
||||||
</Label> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TagList
|
|
||||||
tags={tags}
|
|
||||||
tagList={tagList}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.enabled}>
|
|
||||||
<Label kind={kinds.DEFAULT} title='List Refresh Interval'>
|
|
||||||
{`${translate('Refresh')}: ${formatShortTimeSpan(minRefreshInterval)}`}
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditImportListModalConnector
|
|
||||||
id={id}
|
|
||||||
isOpen={this.state.isEditImportListModalOpen}
|
|
||||||
onModalClose={this.onEditImportListModalClose}
|
|
||||||
onDeleteImportListPress={this.onDeleteImportListPress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={this.state.isDeleteImportListModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('DeleteImportList')}
|
|
||||||
message={translate('DeleteImportListMessageText', { name })}
|
|
||||||
confirmLabel={translate('Delete')}
|
|
||||||
onConfirm={this.onConfirmDeleteImportList}
|
|
||||||
onCancel={this.onDeleteImportListModalClose}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportList.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
enabled: PropTypes.bool.isRequired,
|
|
||||||
enableAuto: PropTypes.bool.isRequired,
|
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
minRefreshInterval: PropTypes.string.isRequired,
|
|
||||||
onConfirmDeleteImportList: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImportList;
|
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
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 { deleteImportList } from 'Store/Actions/settingsActions';
|
||||||
|
import useTags from 'Tags/useTags';
|
||||||
|
import formatShortTimeSpan from 'Utilities/Date/formatShortTimeSpan';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import EditImportListModal from './EditImportListModal';
|
||||||
|
import styles from './ImportList.css';
|
||||||
|
|
||||||
|
interface ImportListProps {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
enabled: boolean;
|
||||||
|
enableAuto: boolean;
|
||||||
|
tags: number[];
|
||||||
|
minRefreshInterval: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImportList({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
enabled,
|
||||||
|
enableAuto,
|
||||||
|
tags,
|
||||||
|
minRefreshInterval,
|
||||||
|
}: ImportListProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const tagList = useTags();
|
||||||
|
|
||||||
|
const [isEditImportListModalOpen, setIsEditImportListModalOpen] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
|
const [isDeleteImportListModalOpen, setIsDeleteImportListModalOpen] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
|
const handleEditImportListPress = useCallback(() => {
|
||||||
|
setIsEditImportListModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleEditImportListModalClose = useCallback(() => {
|
||||||
|
setIsEditImportListModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteImportListPress = useCallback(() => {
|
||||||
|
setIsEditImportListModalOpen(false);
|
||||||
|
setIsDeleteImportListModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteImportListModalClose = useCallback(() => {
|
||||||
|
setIsDeleteImportListModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleConfirmDeleteImportList = useCallback(() => {
|
||||||
|
dispatch(deleteImportList({ id }));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className={styles.list}
|
||||||
|
overlayContent={true}
|
||||||
|
onPress={handleEditImportListPress}
|
||||||
|
>
|
||||||
|
<div className={styles.name}>{name}</div>
|
||||||
|
|
||||||
|
<div className={styles.enabled}>
|
||||||
|
{enabled ? (
|
||||||
|
<Label kind={kinds.SUCCESS}>{translate('Enabled')}</Label>
|
||||||
|
) : (
|
||||||
|
<Label kind={kinds.DISABLED} outline={true}>
|
||||||
|
{translate('Disabled')}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{enableAuto ? (
|
||||||
|
<Label kind={kinds.SUCCESS}>{translate('AutomaticAdd')}</Label>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TagList tags={tags} tagList={tagList} />
|
||||||
|
|
||||||
|
<div className={styles.enabled}>
|
||||||
|
<Label kind={kinds.DEFAULT} title="List Refresh Interval">
|
||||||
|
{`${translate('Refresh')}: ${formatShortTimeSpan(
|
||||||
|
minRefreshInterval
|
||||||
|
)}`}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditImportListModal
|
||||||
|
id={id}
|
||||||
|
isOpen={isEditImportListModalOpen}
|
||||||
|
onModalClose={handleEditImportListModalClose}
|
||||||
|
onDeleteImportListPress={handleDeleteImportListPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
isOpen={isDeleteImportListModalOpen}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('DeleteImportList')}
|
||||||
|
message={translate('DeleteImportListMessageText', { name })}
|
||||||
|
confirmLabel={translate('Delete')}
|
||||||
|
onConfirm={handleConfirmDeleteImportList}
|
||||||
|
onCancel={handleDeleteImportListModalClose}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImportList;
|
||||||
@@ -1,118 +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 AddImportListModal from './AddImportListModal';
|
|
||||||
import EditImportListModalConnector from './EditImportListModalConnector';
|
|
||||||
import ImportList from './ImportList';
|
|
||||||
import styles from './ImportLists.css';
|
|
||||||
|
|
||||||
class ImportLists extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isAddImportListModalOpen: false,
|
|
||||||
isEditImportListModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onAddImportListPress = () => {
|
|
||||||
this.setState({ isAddImportListModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onAddImportListModalClose = ({ listSelected = false } = {}) => {
|
|
||||||
this.setState({
|
|
||||||
isAddImportListModalOpen: false,
|
|
||||||
isEditImportListModalOpen: listSelected
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onEditImportListModalClose = () => {
|
|
||||||
this.setState({ isEditImportListModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
items,
|
|
||||||
tagList,
|
|
||||||
onConfirmDeleteImportList,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
isAddImportListModalOpen,
|
|
||||||
isEditImportListModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend={translate('ImportLists')} >
|
|
||||||
<PageSectionContent
|
|
||||||
errorMessage={translate('ImportListsLoadError')}
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<div className={styles.lists}>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<ImportList
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
tagList={tagList}
|
|
||||||
onConfirmDeleteImportList={onConfirmDeleteImportList}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
<Card
|
|
||||||
className={styles.addList}
|
|
||||||
onPress={this.onAddImportListPress}
|
|
||||||
>
|
|
||||||
<div className={styles.center}>
|
|
||||||
<Icon
|
|
||||||
name={icons.ADD}
|
|
||||||
size={45}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AddImportListModal
|
|
||||||
isOpen={isAddImportListModalOpen}
|
|
||||||
onModalClose={this.onAddImportListModalClose}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<EditImportListModalConnector
|
|
||||||
isOpen={isEditImportListModalOpen}
|
|
||||||
onModalClose={this.onEditImportListModalClose}
|
|
||||||
/>
|
|
||||||
</PageSectionContent>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportLists.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
onConfirmDeleteImportList: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ImportLists;
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { ImportListAppState } 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 { icons } from 'Helpers/Props';
|
||||||
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
|
import { fetchImportLists } from 'Store/Actions/settingsActions';
|
||||||
|
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||||
|
import ImportListModel from 'typings/ImportList';
|
||||||
|
import sortByProp from 'Utilities/Array/sortByProp';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import AddImportListModal from './AddImportListModal';
|
||||||
|
import EditImportListModal from './EditImportListModal';
|
||||||
|
import ImportList from './ImportList';
|
||||||
|
import styles from './ImportLists.css';
|
||||||
|
|
||||||
|
function ImportLists() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const { isFetching, isPopulated, items, error } = useSelector(
|
||||||
|
createSortedSectionSelector<ImportListModel, ImportListAppState>(
|
||||||
|
'settings.importLists',
|
||||||
|
sortByProp('name')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const [isAddImportListModalOpen, setIsAddImportListModalOpen] =
|
||||||
|
useState(false);
|
||||||
|
const [isEditImportListModalOpen, setIsEditImportListModalOpen] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
|
const handleAddImportListPress = useCallback(() => {
|
||||||
|
setIsAddImportListModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddImportListModalClose = useCallback(() => {
|
||||||
|
setIsAddImportListModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleImportListSelect = useCallback(() => {
|
||||||
|
setIsAddImportListModalOpen(false);
|
||||||
|
setIsEditImportListModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleEditImportListModalClose = useCallback(() => {
|
||||||
|
setIsEditImportListModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchImportLists());
|
||||||
|
dispatch(fetchRootFolders());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet legend={translate('ImportLists')}>
|
||||||
|
<PageSectionContent
|
||||||
|
errorMessage={translate('ImportListsLoadError')}
|
||||||
|
error={error}
|
||||||
|
isFetching={isFetching}
|
||||||
|
isPopulated={isPopulated}
|
||||||
|
>
|
||||||
|
<div className={styles.lists}>
|
||||||
|
{items.map((item) => {
|
||||||
|
return <ImportList key={item.id} {...item} />;
|
||||||
|
})}
|
||||||
|
|
||||||
|
<Card className={styles.addList} onPress={handleAddImportListPress}>
|
||||||
|
<div className={styles.center}>
|
||||||
|
<Icon name={icons.ADD} size={45} />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AddImportListModal
|
||||||
|
isOpen={isAddImportListModalOpen}
|
||||||
|
onImportListSelect={handleImportListSelect}
|
||||||
|
onModalClose={handleAddImportListModalClose}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EditImportListModal
|
||||||
|
isOpen={isEditImportListModalOpen}
|
||||||
|
onModalClose={handleEditImportListModalClose}
|
||||||
|
/>
|
||||||
|
</PageSectionContent>
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImportLists;
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
|
||||||
import { deleteImportList, fetchImportLists } from 'Store/Actions/settingsActions';
|
|
||||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
|
||||||
import sortByProp from 'Utilities/Array/sortByProp';
|
|
||||||
import ImportLists from './ImportLists';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createSortedSectionSelector('settings.importLists', sortByProp('name')),
|
|
||||||
createTagsSelector(),
|
|
||||||
(importLists, tagList) => {
|
|
||||||
return {
|
|
||||||
...importLists,
|
|
||||||
tagList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchImportLists,
|
|
||||||
deleteImportList,
|
|
||||||
fetchRootFolders
|
|
||||||
};
|
|
||||||
|
|
||||||
class ImportListsConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchImportLists();
|
|
||||||
this.props.fetchRootFolders();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onConfirmDeleteImportList = (id) => {
|
|
||||||
this.props.deleteImportList({ id });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ImportLists
|
|
||||||
{...this.props}
|
|
||||||
onConfirmDeleteImportList={this.onConfirmDeleteImportList}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportListsConnector.propTypes = {
|
|
||||||
fetchImportLists: PropTypes.func.isRequired,
|
|
||||||
deleteImportList: PropTypes.func.isRequired,
|
|
||||||
fetchRootFolders: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(ImportListsConnector);
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import AppState from 'App/State/AppState';
|
|
||||||
import Alert from 'Components/Alert';
|
import Alert from 'Components/Alert';
|
||||||
import FieldSet from 'Components/FieldSet';
|
import FieldSet from 'Components/FieldSet';
|
||||||
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';
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import { EnhancedSelectInputValue } from 'Components/Form/Select/EnhancedSelectInput';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
|
||||||
import { inputTypes, kinds } from 'Helpers/Props';
|
import { inputTypes, kinds } from 'Helpers/Props';
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||||
import {
|
import {
|
||||||
@@ -20,53 +20,62 @@ import createSettingsSectionSelector from 'Store/Selectors/createSettingsSection
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const SECTION = 'importListOptions';
|
const SECTION = 'importListOptions';
|
||||||
const cleanLibraryLevelOptions = [
|
const cleanLibraryLevelOptions: EnhancedSelectInputValue<string>[] = [
|
||||||
{ key: 'disabled', value: () => translate('Disabled') },
|
{
|
||||||
{ key: 'logOnly', value: () => translate('LogOnly') },
|
key: 'disabled',
|
||||||
{ key: 'keepAndUnmonitor', value: () => translate('KeepAndUnmonitorMovie') },
|
get value() {
|
||||||
{ key: 'removeAndKeep', value: () => translate('RemoveMovieAndKeepFiles') },
|
return translate('Disabled');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'logOnly',
|
||||||
|
get value() {
|
||||||
|
return translate('LogOnly');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'keepAndUnmonitor',
|
||||||
|
get value() {
|
||||||
|
return translate('KeepAndUnmonitorMovie');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'removeAndKeep',
|
||||||
|
get value() {
|
||||||
|
return translate('RemoveMovieAndKeepFiles');
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'removeAndDelete',
|
key: 'removeAndDelete',
|
||||||
value: () => translate('RemoveMovieAndDeleteFiles'),
|
get value() {
|
||||||
|
return translate('RemoveMovieAndDeleteFiles');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function createImportListOptionsSelector() {
|
interface ImportListOptionsProps {
|
||||||
return createSelector(
|
|
||||||
(state: AppState) => state.settings.advancedSettings,
|
|
||||||
createSettingsSectionSelector(SECTION),
|
|
||||||
(advancedSettings, sectionSettings) => {
|
|
||||||
return {
|
|
||||||
advancedSettings,
|
|
||||||
save: sectionSettings.isSaving,
|
|
||||||
...sectionSettings,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportListOptionsPageProps {
|
|
||||||
setChildSave(saveCallback: () => void): void;
|
setChildSave(saveCallback: () => void): void;
|
||||||
onChildStateChange(payload: unknown): void;
|
onChildStateChange(payload: unknown): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImportListOptions(props: ImportListOptionsPageProps) {
|
function ImportListOptions({
|
||||||
const { setChildSave, onChildStateChange } = props;
|
setChildSave,
|
||||||
|
onChildStateChange,
|
||||||
|
}: ImportListOptionsProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const showAdvancedSettings = useShowAdvancedSettings();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isSaving,
|
isSaving,
|
||||||
hasPendingChanges,
|
hasPendingChanges,
|
||||||
advancedSettings,
|
|
||||||
isFetching,
|
isFetching,
|
||||||
error,
|
error,
|
||||||
settings,
|
settings,
|
||||||
hasSettings,
|
hasSettings,
|
||||||
} = useSelector(createImportListOptionsSelector());
|
} = useSelector(createSettingsSectionSelector(SECTION));
|
||||||
|
|
||||||
const { listSyncLevel } = settings;
|
const { listSyncLevel } = settings;
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const onInputChange = useCallback(
|
const onInputChange = useCallback(
|
||||||
({ name, value }: { name: string; value: unknown }) => {
|
({ name, value }: { name: string; value: unknown }) => {
|
||||||
// @ts-expect-error 'setImportListOptionsValue' isn't typed yet
|
// @ts-expect-error 'setImportListOptionsValue' isn't typed yet
|
||||||
@@ -80,7 +89,7 @@ function ImportListOptions(props: ImportListOptionsPageProps) {
|
|||||||
setChildSave(() => dispatch(saveImportListOptions()));
|
setChildSave(() => dispatch(saveImportListOptions()));
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
dispatch(clearPendingChanges({ section: SECTION }));
|
dispatch(clearPendingChanges({ section: `settings.${SECTION}` }));
|
||||||
};
|
};
|
||||||
}, [dispatch, setChildSave]);
|
}, [dispatch, setChildSave]);
|
||||||
|
|
||||||
@@ -91,16 +100,11 @@ function ImportListOptions(props: ImportListOptionsPageProps) {
|
|||||||
});
|
});
|
||||||
}, [onChildStateChange, isSaving, hasPendingChanges]);
|
}, [onChildStateChange, isSaving, hasPendingChanges]);
|
||||||
|
|
||||||
const translatedLevelOptions = cleanLibraryLevelOptions.map(
|
if (!showAdvancedSettings) {
|
||||||
({ key, value }) => {
|
return null;
|
||||||
return {
|
}
|
||||||
key,
|
|
||||||
value: value(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return advancedSettings ? (
|
return (
|
||||||
<FieldSet legend={translate('Options')}>
|
<FieldSet legend={translate('Options')}>
|
||||||
{isFetching ? <LoadingIndicator /> : null}
|
{isFetching ? <LoadingIndicator /> : null}
|
||||||
|
|
||||||
@@ -110,12 +114,12 @@ function ImportListOptions(props: ImportListOptionsPageProps) {
|
|||||||
|
|
||||||
{hasSettings && !isFetching && !error ? (
|
{hasSettings && !isFetching && !error ? (
|
||||||
<Form>
|
<Form>
|
||||||
<FormGroup advancedSettings={advancedSettings} isAdvanced={true}>
|
<FormGroup advancedSettings={showAdvancedSettings} isAdvanced={true}>
|
||||||
<FormLabel>{translate('CleanLibraryLevel')}</FormLabel>
|
<FormLabel>{translate('CleanLibraryLevel')}</FormLabel>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.SELECT}
|
type={inputTypes.SELECT}
|
||||||
name="listSyncLevel"
|
name="listSyncLevel"
|
||||||
values={translatedLevelOptions}
|
values={cleanLibraryLevelOptions}
|
||||||
helpText={translate('ListSyncLevelHelpText')}
|
helpText={translate('ListSyncLevelHelpText')}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...listSyncLevel}
|
{...listSyncLevel}
|
||||||
@@ -124,7 +128,7 @@ function ImportListOptions(props: ImportListOptionsPageProps) {
|
|||||||
</Form>
|
</Form>
|
||||||
) : null}
|
) : null}
|
||||||
</FieldSet>
|
</FieldSet>
|
||||||
) : null;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImportListOptions;
|
export default ImportListOptions;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { MovieMonitor } from 'Movie/Movie';
|
||||||
import Provider from './Provider';
|
import Provider from './Provider';
|
||||||
|
|
||||||
interface ImportList extends Provider {
|
interface ImportList extends Provider {
|
||||||
@@ -7,6 +8,12 @@ interface ImportList extends Provider {
|
|||||||
qualityProfileId: number;
|
qualityProfileId: number;
|
||||||
minimumAvailability: string;
|
minimumAvailability: string;
|
||||||
rootFolderPath: string;
|
rootFolderPath: string;
|
||||||
|
monitor: MovieMonitor;
|
||||||
|
searchOnAdd: boolean;
|
||||||
|
listType: string;
|
||||||
|
listOrder: number;
|
||||||
|
minRefreshInterval: string;
|
||||||
|
name: string;
|
||||||
tags: number[];
|
tags: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user