mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-04-23 13:57:06 -04:00
Convert Notifications to TypeScript
This commit is contained in:
parent
92db4769be
commit
1765feac03
24 changed files with 835 additions and 1228 deletions
|
@ -99,7 +99,9 @@ export interface IndexerAppState
|
|||
|
||||
export interface NotificationAppState
|
||||
extends AppSectionState<Notification>,
|
||||
AppSectionDeleteState {}
|
||||
AppSectionDeleteState,
|
||||
AppSectionSaveState,
|
||||
AppSectionSchemaState<Presets<Notification>> {}
|
||||
|
||||
export interface QualityDefinitionsAppState
|
||||
extends AppSectionState<QualityDefinition>,
|
||||
|
|
|
@ -3,17 +3,15 @@ import PageContent from 'Components/Page/PageContent';
|
|||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import SettingsToolbar from 'Settings/SettingsToolbar';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NotificationsConnector from './Notifications/NotificationsConnector';
|
||||
import Notifications from './Notifications/Notifications';
|
||||
|
||||
function NotificationSettings() {
|
||||
return (
|
||||
<PageContent title={translate('ConnectSettings')}>
|
||||
<SettingsToolbar
|
||||
showSave={false}
|
||||
/>
|
||||
<SettingsToolbar showSave={false} />
|
||||
|
||||
<PageContentBody>
|
||||
<NotificationsConnector />
|
||||
<Notifications />
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
|
@ -1,111 +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 AddNotificationPresetMenuItem from './AddNotificationPresetMenuItem';
|
||||
import styles from './AddNotificationItem.css';
|
||||
|
||||
class AddNotificationItem extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onNotificationSelect = () => {
|
||||
const {
|
||||
implementation
|
||||
} = this.props;
|
||||
|
||||
this.props.onNotificationSelect({ implementation });
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
implementation,
|
||||
implementationName,
|
||||
infoLink,
|
||||
presets,
|
||||
onNotificationSelect
|
||||
} = this.props;
|
||||
|
||||
const hasPresets = !!presets && !!presets.length;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.notification}
|
||||
>
|
||||
<Link
|
||||
className={styles.underlay}
|
||||
onPress={this.onNotificationSelect}
|
||||
/>
|
||||
|
||||
<div className={styles.overlay}>
|
||||
<div className={styles.name}>
|
||||
{implementationName}
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
{
|
||||
hasPresets &&
|
||||
<span>
|
||||
<Button
|
||||
size={sizes.SMALL}
|
||||
onPress={this.onNotificationSelect}
|
||||
>
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
<Button
|
||||
className={styles.presetsMenuButton}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
{
|
||||
presets.map((preset) => {
|
||||
return (
|
||||
<AddNotificationPresetMenuItem
|
||||
key={preset.name}
|
||||
name={preset.name}
|
||||
implementation={implementation}
|
||||
onPress={onNotificationSelect}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
</span>
|
||||
}
|
||||
|
||||
<Button
|
||||
to={infoLink}
|
||||
size={sizes.SMALL}
|
||||
>
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddNotificationItem.propTypes = {
|
||||
implementation: PropTypes.string.isRequired,
|
||||
implementationName: PropTypes.string.isRequired,
|
||||
infoLink: PropTypes.string.isRequired,
|
||||
presets: PropTypes.arrayOf(PropTypes.object),
|
||||
onNotificationSelect: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AddNotificationItem;
|
|
@ -0,0 +1,88 @@
|
|||
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 { selectNotificationSchema } from 'Store/Actions/settingsActions';
|
||||
import Notification from 'typings/Notification';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddNotificationPresetMenuItem from './AddNotificationPresetMenuItem';
|
||||
import styles from './AddNotificationItem.css';
|
||||
|
||||
interface AddNotificationItemProps {
|
||||
implementation: string;
|
||||
implementationName: string;
|
||||
infoLink: string;
|
||||
presets?: Notification[];
|
||||
onNotificationSelect: () => void;
|
||||
}
|
||||
|
||||
function AddNotificationItem({
|
||||
implementation,
|
||||
implementationName,
|
||||
infoLink,
|
||||
presets,
|
||||
onNotificationSelect,
|
||||
}: AddNotificationItemProps) {
|
||||
const dispatch = useDispatch();
|
||||
const hasPresets = !!presets && !!presets.length;
|
||||
|
||||
const handleNotificationSelect = useCallback(() => {
|
||||
dispatch(
|
||||
selectNotificationSchema({
|
||||
implementation,
|
||||
implementationName,
|
||||
})
|
||||
);
|
||||
|
||||
onNotificationSelect();
|
||||
}, [implementation, implementationName, dispatch, onNotificationSelect]);
|
||||
|
||||
return (
|
||||
<div className={styles.notification}>
|
||||
<Link className={styles.underlay} onPress={handleNotificationSelect} />
|
||||
|
||||
<div className={styles.overlay}>
|
||||
<div className={styles.name}>{implementationName}</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
{hasPresets ? (
|
||||
<span>
|
||||
<Button size={sizes.SMALL} onPress={handleNotificationSelect}>
|
||||
{translate('Custom')}
|
||||
</Button>
|
||||
|
||||
<Menu className={styles.presetsMenu}>
|
||||
<Button className={styles.presetsMenuButton} size={sizes.SMALL}>
|
||||
{translate('Presets')}
|
||||
</Button>
|
||||
|
||||
<MenuContent>
|
||||
{presets.map((preset) => {
|
||||
return (
|
||||
<AddNotificationPresetMenuItem
|
||||
key={preset.name}
|
||||
name={preset.name}
|
||||
implementation={implementation}
|
||||
implementationName={implementationName}
|
||||
onPress={onNotificationSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
<Button to={infoLink} size={sizes.SMALL}>
|
||||
{translate('MoreInfo')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddNotificationItem;
|
|
@ -1,25 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import AddNotificationModalContentConnector from './AddNotificationModalContentConnector';
|
||||
|
||||
function AddNotificationModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<AddNotificationModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
AddNotificationModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AddNotificationModal;
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import AddNotificationModalContent, {
|
||||
AddNotificationModalContentProps,
|
||||
} from './AddNotificationModalContent';
|
||||
|
||||
interface AddNotificationModalProps extends AddNotificationModalContentProps {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
function AddNotificationModal({
|
||||
isOpen,
|
||||
onModalClose,
|
||||
...otherProps
|
||||
}: AddNotificationModalProps) {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
||||
<AddNotificationModalContent
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddNotificationModal;
|
|
@ -1,90 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
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 translate from 'Utilities/String/translate';
|
||||
import AddNotificationItem from './AddNotificationItem';
|
||||
import styles from './AddNotificationModalContent.css';
|
||||
|
||||
class AddNotificationModalContent extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isSchemaFetching,
|
||||
isSchemaPopulated,
|
||||
schemaError,
|
||||
schema,
|
||||
onNotificationSelect,
|
||||
onModalClose
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{translate('AddConnection')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
isSchemaFetching &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isSchemaFetching && !!schemaError &&
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('AddNotificationError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
isSchemaPopulated && !schemaError &&
|
||||
<div>
|
||||
<div className={styles.notifications}>
|
||||
{
|
||||
schema.map((notification) => {
|
||||
return (
|
||||
<AddNotificationItem
|
||||
key={notification.implementation}
|
||||
implementation={notification.implementation}
|
||||
{...notification}
|
||||
onNotificationSelect={onNotificationSelect}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
{translate('Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddNotificationModalContent.propTypes = {
|
||||
isSchemaFetching: PropTypes.bool.isRequired,
|
||||
isSchemaPopulated: PropTypes.bool.isRequired,
|
||||
schemaError: PropTypes.object,
|
||||
schema: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onNotificationSelect: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AddNotificationModalContent;
|
|
@ -0,0 +1,79 @@
|
|||
import React, { useEffect } 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 { fetchNotificationSchema } from 'Store/Actions/settingsActions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddNotificationItem from './AddNotificationItem';
|
||||
import styles from './AddNotificationModalContent.css';
|
||||
|
||||
export interface AddNotificationModalContentProps {
|
||||
onNotificationSelect: () => void;
|
||||
onModalClose: () => void;
|
||||
}
|
||||
|
||||
function AddNotificationModalContent({
|
||||
onNotificationSelect,
|
||||
onModalClose,
|
||||
}: AddNotificationModalContentProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { isSchemaFetching, isSchemaPopulated, schemaError, schema } =
|
||||
useSelector((state: AppState) => state.settings.notifications);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchNotificationSchema());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>{translate('AddNotification')}</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{isSchemaFetching ? <LoadingIndicator /> : null}
|
||||
|
||||
{!isSchemaFetching && !!schemaError ? (
|
||||
<Alert kind={kinds.DANGER}>{translate('AddNotificationError')}</Alert>
|
||||
) : null}
|
||||
|
||||
{isSchemaPopulated && !schemaError ? (
|
||||
<div>
|
||||
<Alert kind={kinds.INFO}>
|
||||
<div>{translate('SupportedNotifications')}</div>
|
||||
<div>{translate('SupportedNotificationsMoreInfo')}</div>
|
||||
</Alert>
|
||||
|
||||
<FieldSet legend={translate('Email')}>
|
||||
<div className={styles.notifications}>
|
||||
{schema.map((notification) => {
|
||||
return (
|
||||
<AddNotificationItem
|
||||
key={notification.implementation}
|
||||
{...notification}
|
||||
implementation={notification.implementation}
|
||||
onNotificationSelect={onNotificationSelect}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</FieldSet>
|
||||
</div>
|
||||
) : null}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>{translate('Close')}</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddNotificationModalContent;
|
|
@ -1,70 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchNotificationSchema, selectNotificationSchema } from 'Store/Actions/settingsActions';
|
||||
import AddNotificationModalContent from './AddNotificationModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.settings.notifications,
|
||||
(notifications) => {
|
||||
const {
|
||||
isSchemaFetching,
|
||||
isSchemaPopulated,
|
||||
schemaError,
|
||||
schema
|
||||
} = notifications;
|
||||
|
||||
return {
|
||||
isSchemaFetching,
|
||||
isSchemaPopulated,
|
||||
schemaError,
|
||||
schema
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchNotificationSchema,
|
||||
selectNotificationSchema
|
||||
};
|
||||
|
||||
class AddNotificationModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchNotificationSchema();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onNotificationSelect = ({ implementation, name }) => {
|
||||
this.props.selectNotificationSchema({ implementation, presetName: name });
|
||||
this.props.onModalClose({ notificationSelected: true });
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AddNotificationModalContent
|
||||
{...this.props}
|
||||
onNotificationSelect={this.onNotificationSelect}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddNotificationModalContentConnector.propTypes = {
|
||||
fetchNotificationSchema: PropTypes.func.isRequired,
|
||||
selectNotificationSchema: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AddNotificationModalContentConnector);
|
|
@ -1,49 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import MenuItem from 'Components/Menu/MenuItem';
|
||||
|
||||
class AddNotificationPresetMenuItem extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onPress = () => {
|
||||
const {
|
||||
name,
|
||||
implementation
|
||||
} = this.props;
|
||||
|
||||
this.props.onPress({
|
||||
name,
|
||||
implementation
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
name,
|
||||
implementation,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
{...otherProps}
|
||||
onPress={this.onPress}
|
||||
>
|
||||
{name}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddNotificationPresetMenuItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
implementation: PropTypes.string.isRequired,
|
||||
onPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AddNotificationPresetMenuItem;
|
|
@ -0,0 +1,41 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import MenuItem from 'Components/Menu/MenuItem';
|
||||
import { selectNotificationSchema } from 'Store/Actions/settingsActions';
|
||||
|
||||
interface AddNotificationPresetMenuItemProps {
|
||||
name: string;
|
||||
implementation: string;
|
||||
implementationName: string;
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
function AddNotificationPresetMenuItem({
|
||||
name,
|
||||
implementation,
|
||||
implementationName,
|
||||
onPress,
|
||||
...otherProps
|
||||
}: AddNotificationPresetMenuItemProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
dispatch(
|
||||
selectNotificationSchema({
|
||||
implementation,
|
||||
implementationName,
|
||||
presetName: name,
|
||||
})
|
||||
);
|
||||
|
||||
onPress();
|
||||
}, [name, implementation, implementationName, dispatch, onPress]);
|
||||
|
||||
return (
|
||||
<MenuItem {...otherProps} onPress={handlePress}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddNotificationPresetMenuItem;
|
|
@ -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 EditNotificationModalContentConnector from './EditNotificationModalContentConnector';
|
||||
|
||||
function EditNotificationModal({ isOpen, onModalClose, ...otherProps }) {
|
||||
return (
|
||||
<Modal
|
||||
size={sizes.MEDIUM}
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<EditNotificationModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
EditNotificationModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default EditNotificationModal;
|
|
@ -0,0 +1,44 @@
|
|||
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 {
|
||||
cancelSaveNotification,
|
||||
cancelTestNotification,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import EditNotificationModalContent, {
|
||||
EditNotificationModalContentProps,
|
||||
} from './EditNotificationModalContent';
|
||||
|
||||
const section = 'settings.notifications';
|
||||
|
||||
interface EditNotificationModalProps extends EditNotificationModalContentProps {
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
function EditNotificationModal({
|
||||
isOpen,
|
||||
onModalClose,
|
||||
...otherProps
|
||||
}: EditNotificationModalProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleModalClose = useCallback(() => {
|
||||
dispatch(clearPendingChanges({ section }));
|
||||
dispatch(cancelTestNotification({ section }));
|
||||
dispatch(cancelSaveNotification({ section }));
|
||||
onModalClose();
|
||||
}, [dispatch, onModalClose]);
|
||||
|
||||
return (
|
||||
<Modal size={sizes.MEDIUM} isOpen={isOpen} onModalClose={handleModalClose}>
|
||||
<EditNotificationModalContent
|
||||
{...otherProps}
|
||||
onModalClose={handleModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditNotificationModal;
|
|
@ -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 { cancelSaveNotification, cancelTestNotification } from 'Store/Actions/settingsActions';
|
||||
import EditNotificationModal from './EditNotificationModal';
|
||||
|
||||
function createMapDispatchToProps(dispatch, props) {
|
||||
const section = 'settings.notifications';
|
||||
|
||||
return {
|
||||
dispatchClearPendingChanges() {
|
||||
dispatch(clearPendingChanges({ section }));
|
||||
},
|
||||
|
||||
dispatchCancelTestNotification() {
|
||||
dispatch(cancelTestNotification({ section }));
|
||||
},
|
||||
|
||||
dispatchCancelSaveNotification() {
|
||||
dispatch(cancelSaveNotification({ section }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class EditNotificationModalConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onModalClose = () => {
|
||||
this.props.dispatchClearPendingChanges();
|
||||
this.props.dispatchCancelTestNotification();
|
||||
this.props.dispatchCancelSaveNotification();
|
||||
this.props.onModalClose();
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
dispatchClearPendingChanges,
|
||||
dispatchCancelTestNotification,
|
||||
dispatchCancelSaveNotification,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<EditNotificationModal
|
||||
{...otherProps}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditNotificationModalConnector.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
dispatchClearPendingChanges: PropTypes.func.isRequired,
|
||||
dispatchCancelTestNotification: PropTypes.func.isRequired,
|
||||
dispatchCancelSaveNotification: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(null, createMapDispatchToProps)(EditNotificationModalConnector);
|
|
@ -1,186 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
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 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 { inputTypes, kinds } from 'Helpers/Props';
|
||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NotificationEventItems from './NotificationEventItems';
|
||||
import styles from './EditNotificationModalContent.css';
|
||||
|
||||
function EditNotificationModalContent(props) {
|
||||
const {
|
||||
advancedSettings,
|
||||
isFetching,
|
||||
error,
|
||||
isSaving,
|
||||
isTesting,
|
||||
saveError,
|
||||
item,
|
||||
onInputChange,
|
||||
onFieldChange,
|
||||
onModalClose,
|
||||
onSavePress,
|
||||
onTestPress,
|
||||
onDeleteNotificationPress,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const {
|
||||
id,
|
||||
implementationName,
|
||||
name,
|
||||
tags,
|
||||
fields,
|
||||
message
|
||||
} = item;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id ? translate('EditConnectionImplementation', { implementationName }) : translate('AddConnectionImplementation', { implementationName })}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<Alert kind={kinds.DANGER}>
|
||||
{translate('AddNotificationError')}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !error &&
|
||||
<Form {...otherProps}>
|
||||
{
|
||||
!!message &&
|
||||
<Alert
|
||||
className={styles.message}
|
||||
kind={message.value.type}
|
||||
>
|
||||
{message.value.message}
|
||||
</Alert>
|
||||
}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('Name')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="name"
|
||||
{...name}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<NotificationEventItems
|
||||
item={item}
|
||||
onInputChange={onInputChange}
|
||||
/>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText={translate('NotificationsTagsSeriesHelpText')}
|
||||
{...tags}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{
|
||||
fields.map((field) => {
|
||||
return (
|
||||
<ProviderFieldFormGroup
|
||||
key={field.name}
|
||||
advancedSettings={advancedSettings}
|
||||
provider="notification"
|
||||
providerData={item}
|
||||
section="settings.notifications"
|
||||
{...field}
|
||||
onChange={onFieldChange}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
</Form>
|
||||
}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{
|
||||
id &&
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onDeleteNotificationPress}
|
||||
>
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
<AdvancedSettingsButton
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
EditNotificationModalContent.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,
|
||||
onDeleteNotificationPress: PropTypes.func
|
||||
};
|
||||
|
||||
export default EditNotificationModalContent;
|
|
@ -0,0 +1,203 @@
|
|||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { NotificationAppState } 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 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 usePrevious from 'Helpers/Hooks/usePrevious';
|
||||
import useShowAdvancedSettings from 'Helpers/Hooks/useShowAdvancedSettings';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import AdvancedSettingsButton from 'Settings/AdvancedSettingsButton';
|
||||
import {
|
||||
saveNotification,
|
||||
setNotificationFieldValue,
|
||||
setNotificationValue,
|
||||
testNotification,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import { createProviderSettingsSelectorHook } from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import Notification from 'typings/Notification';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NotificationEventItems from './NotificationEventItems';
|
||||
import styles from './EditNotificationModalContent.css';
|
||||
|
||||
export interface EditNotificationModalContentProps {
|
||||
id?: number;
|
||||
onModalClose: () => void;
|
||||
onDeleteNotificationPress?: () => void;
|
||||
}
|
||||
|
||||
function EditNotificationModalContent({
|
||||
id,
|
||||
onModalClose,
|
||||
onDeleteNotificationPress,
|
||||
}: EditNotificationModalContentProps) {
|
||||
const dispatch = useDispatch();
|
||||
const showAdvancedSettings = useShowAdvancedSettings();
|
||||
|
||||
const {
|
||||
isFetching,
|
||||
error,
|
||||
isSaving,
|
||||
isTesting = false,
|
||||
saveError,
|
||||
item,
|
||||
validationErrors,
|
||||
validationWarnings,
|
||||
} = useSelector(
|
||||
createProviderSettingsSelectorHook<Notification, NotificationAppState>(
|
||||
'notifications',
|
||||
id
|
||||
)
|
||||
);
|
||||
|
||||
const wasSaving = usePrevious(isSaving);
|
||||
|
||||
const { implementationName, name, fields, tags, message } = item;
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error - actions are not typed
|
||||
dispatch(setNotificationValue(change));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleFieldChange = useCallback(
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error - actions are not typed
|
||||
dispatch(setNotificationFieldValue(change));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleTestPress = useCallback(() => {
|
||||
dispatch(testNotification({ id }));
|
||||
}, [id, dispatch]);
|
||||
|
||||
const handleSavePress = useCallback(() => {
|
||||
dispatch(saveNotification({ id }));
|
||||
}, [id, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wasSaving && !isSaving && !saveError) {
|
||||
onModalClose();
|
||||
}
|
||||
}, [isSaving, wasSaving, saveError, onModalClose]);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{id
|
||||
? translate('EditConnectionImplementation', { implementationName })
|
||||
: translate('AddConnectionImplementation', { implementationName })}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{isFetching ? <LoadingIndicator /> : null}
|
||||
|
||||
{!isFetching && !!error ? (
|
||||
<Alert kind={kinds.DANGER}>{translate('AddNotificationError')}</Alert>
|
||||
) : null}
|
||||
|
||||
{!isFetching && !error ? (
|
||||
<Form
|
||||
validationErrors={validationErrors}
|
||||
validationWarnings={validationWarnings}
|
||||
>
|
||||
{message ? (
|
||||
<Alert className={styles.message} kind={message.value.type}>
|
||||
{message.value.message}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('Name')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="name"
|
||||
{...name}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<NotificationEventItems
|
||||
item={item}
|
||||
onInputChange={handleInputChange}
|
||||
/>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('Tags')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TAG}
|
||||
name="tags"
|
||||
helpText={translate('NotificationsTagsSeriesHelpText')}
|
||||
{...tags}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{fields.map((field) => {
|
||||
return (
|
||||
<ProviderFieldFormGroup
|
||||
key={field.name}
|
||||
{...field}
|
||||
advancedSettings={showAdvancedSettings}
|
||||
provider="notification"
|
||||
providerData={item}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Form>
|
||||
) : null}
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
{id ? (
|
||||
<Button
|
||||
className={styles.deleteButton}
|
||||
kind={kinds.DANGER}
|
||||
onPress={onDeleteNotificationPress}
|
||||
>
|
||||
{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 EditNotificationModalContent;
|
|
@ -1,93 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import {
|
||||
saveNotification,
|
||||
setNotificationFieldValues,
|
||||
setNotificationValue,
|
||||
testNotification
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditNotificationModalContent from './EditNotificationModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
(state) => state.settings.advancedSettings,
|
||||
createProviderSettingsSelector('notifications'),
|
||||
(advancedSettings, notification) => {
|
||||
return {
|
||||
advancedSettings,
|
||||
...notification
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setNotificationValue,
|
||||
setNotificationFieldValues,
|
||||
saveNotification,
|
||||
testNotification
|
||||
};
|
||||
|
||||
class EditNotificationModalContentConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
||||
this.props.onModalClose();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.setNotificationValue({ name, value });
|
||||
};
|
||||
|
||||
onFieldChange = ({ name, value, additionalProperties = {} }) => {
|
||||
this.props.setNotificationFieldValues({ properties: { ...additionalProperties, [name]: value } });
|
||||
};
|
||||
|
||||
onSavePress = () => {
|
||||
this.props.saveNotification({ id: this.props.id });
|
||||
};
|
||||
|
||||
onTestPress = () => {
|
||||
this.props.testNotification({ id: this.props.id });
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditNotificationModalContent
|
||||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onTestPress={this.onTestPress}
|
||||
onInputChange={this.onInputChange}
|
||||
onFieldChange={this.onFieldChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditNotificationModalContentConnector.propTypes = {
|
||||
id: PropTypes.number,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
item: PropTypes.object.isRequired,
|
||||
setNotificationValue: PropTypes.func.isRequired,
|
||||
setNotificationFieldValues: PropTypes.func.isRequired,
|
||||
saveNotification: PropTypes.func.isRequired,
|
||||
testNotification: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditNotificationModalContentConnector);
|
|
@ -1,274 +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 translate from 'Utilities/String/translate';
|
||||
import EditNotificationModalConnector from './EditNotificationModalConnector';
|
||||
import styles from './Notification.css';
|
||||
|
||||
class Notification extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isEditNotificationModalOpen: false,
|
||||
isDeleteNotificationModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onEditNotificationPress = () => {
|
||||
this.setState({ isEditNotificationModalOpen: true });
|
||||
};
|
||||
|
||||
onEditNotificationModalClose = () => {
|
||||
this.setState({ isEditNotificationModalOpen: false });
|
||||
};
|
||||
|
||||
onDeleteNotificationPress = () => {
|
||||
this.setState({
|
||||
isEditNotificationModalOpen: false,
|
||||
isDeleteNotificationModalOpen: true
|
||||
});
|
||||
};
|
||||
|
||||
onDeleteNotificationModalClose = () => {
|
||||
this.setState({ isDeleteNotificationModalOpen: false });
|
||||
};
|
||||
|
||||
onConfirmDeleteNotification = () => {
|
||||
this.props.onConfirmDeleteNotification(this.props.id);
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
onGrab,
|
||||
onDownload,
|
||||
onUpgrade,
|
||||
onImportComplete,
|
||||
onRename,
|
||||
onSeriesAdd,
|
||||
onSeriesDelete,
|
||||
onEpisodeFileDelete,
|
||||
onEpisodeFileDeleteForUpgrade,
|
||||
onHealthIssue,
|
||||
onHealthRestored,
|
||||
onApplicationUpdate,
|
||||
onManualInteractionRequired,
|
||||
supportsOnGrab,
|
||||
supportsOnDownload,
|
||||
supportsOnUpgrade,
|
||||
supportsOnImportComplete,
|
||||
supportsOnRename,
|
||||
supportsOnSeriesAdd,
|
||||
supportsOnSeriesDelete,
|
||||
supportsOnEpisodeFileDelete,
|
||||
supportsOnEpisodeFileDeleteForUpgrade,
|
||||
supportsOnHealthIssue,
|
||||
supportsOnHealthRestored,
|
||||
supportsOnApplicationUpdate,
|
||||
supportsOnManualInteractionRequired,
|
||||
tags,
|
||||
tagList
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={styles.notification}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditNotificationPress}
|
||||
>
|
||||
<div className={styles.name}>
|
||||
{name}
|
||||
</div>
|
||||
|
||||
{
|
||||
supportsOnGrab && onGrab ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnGrab')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnDownload && onDownload ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnFileImport')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnUpgrade && onDownload && onUpgrade ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnFileUpgrade')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnImportComplete && onImportComplete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnImportComplete')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnRename && onRename ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnRename')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnHealthIssue && onHealthIssue ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnHealthIssue')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnHealthRestored && onHealthRestored ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnHealthRestored')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnApplicationUpdate && onApplicationUpdate ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnApplicationUpdate')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnSeriesAdd && onSeriesAdd ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnSeriesAdd')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnSeriesDelete && onSeriesDelete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnSeriesDelete')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnEpisodeFileDelete && onEpisodeFileDelete ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnEpisodeFileDelete')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnEpisodeFileDeleteForUpgrade && onEpisodeFileDelete && onEpisodeFileDeleteForUpgrade ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnEpisodeFileDeleteForUpgrade')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
supportsOnManualInteractionRequired && onManualInteractionRequired ?
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnManualInteractionRequired')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
!onGrab && !onDownload && !onRename && !onImportComplete && !onHealthIssue && !onHealthRestored && !onApplicationUpdate && !onSeriesAdd && !onSeriesDelete && !onEpisodeFileDelete && !onManualInteractionRequired ?
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
>
|
||||
{translate('Disabled')}
|
||||
</Label> :
|
||||
null
|
||||
}
|
||||
|
||||
<TagList
|
||||
tags={tags}
|
||||
tagList={tagList}
|
||||
/>
|
||||
|
||||
<EditNotificationModalConnector
|
||||
id={id}
|
||||
isOpen={this.state.isEditNotificationModalOpen}
|
||||
onModalClose={this.onEditNotificationModalClose}
|
||||
onDeleteNotificationPress={this.onDeleteNotificationPress}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteNotificationModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteNotification')}
|
||||
message={translate('DeleteNotificationMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteNotification}
|
||||
onCancel={this.onDeleteNotificationModalClose}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Notification.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onGrab: PropTypes.bool.isRequired,
|
||||
onDownload: PropTypes.bool.isRequired,
|
||||
onUpgrade: PropTypes.bool.isRequired,
|
||||
onImportComplete: PropTypes.bool.isRequired,
|
||||
onRename: PropTypes.bool.isRequired,
|
||||
onSeriesAdd: PropTypes.bool.isRequired,
|
||||
onSeriesDelete: PropTypes.bool.isRequired,
|
||||
onEpisodeFileDelete: PropTypes.bool.isRequired,
|
||||
onEpisodeFileDeleteForUpgrade: PropTypes.bool.isRequired,
|
||||
onHealthIssue: PropTypes.bool.isRequired,
|
||||
onHealthRestored: PropTypes.bool.isRequired,
|
||||
onApplicationUpdate: PropTypes.bool.isRequired,
|
||||
onManualInteractionRequired: PropTypes.bool.isRequired,
|
||||
supportsOnGrab: PropTypes.bool.isRequired,
|
||||
supportsOnDownload: PropTypes.bool.isRequired,
|
||||
supportsOnImportComplete: PropTypes.bool.isRequired,
|
||||
supportsOnSeriesAdd: PropTypes.bool.isRequired,
|
||||
supportsOnSeriesDelete: PropTypes.bool.isRequired,
|
||||
supportsOnEpisodeFileDelete: PropTypes.bool.isRequired,
|
||||
supportsOnEpisodeFileDeleteForUpgrade: PropTypes.bool.isRequired,
|
||||
supportsOnUpgrade: PropTypes.bool.isRequired,
|
||||
supportsOnRename: PropTypes.bool.isRequired,
|
||||
supportsOnHealthIssue: PropTypes.bool.isRequired,
|
||||
supportsOnHealthRestored: PropTypes.bool.isRequired,
|
||||
supportsOnApplicationUpdate: PropTypes.bool.isRequired,
|
||||
supportsOnManualInteractionRequired: PropTypes.bool.isRequired,
|
||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onConfirmDeleteNotification: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Notification;
|
|
@ -0,0 +1,179 @@
|
|||
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 { deleteNotification } from 'Store/Actions/settingsActions';
|
||||
import useTags from 'Tags/useTags';
|
||||
import NotificationModel from 'typings/Notification';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditNotificationModal from './EditNotificationModal';
|
||||
import styles from './Notification.css';
|
||||
|
||||
function Notification({
|
||||
id,
|
||||
name,
|
||||
onGrab,
|
||||
onDownload,
|
||||
onUpgrade,
|
||||
onImportComplete,
|
||||
onRename,
|
||||
onSeriesAdd,
|
||||
onSeriesDelete,
|
||||
onEpisodeFileDelete,
|
||||
onEpisodeFileDeleteForUpgrade,
|
||||
onHealthIssue,
|
||||
onHealthRestored,
|
||||
onApplicationUpdate,
|
||||
onManualInteractionRequired,
|
||||
supportsOnGrab,
|
||||
supportsOnDownload,
|
||||
supportsOnUpgrade,
|
||||
supportsOnImportComplete,
|
||||
supportsOnRename,
|
||||
supportsOnSeriesAdd,
|
||||
supportsOnSeriesDelete,
|
||||
supportsOnEpisodeFileDelete,
|
||||
supportsOnEpisodeFileDeleteForUpgrade,
|
||||
supportsOnHealthIssue,
|
||||
supportsOnHealthRestored,
|
||||
supportsOnApplicationUpdate,
|
||||
supportsOnManualInteractionRequired,
|
||||
tags,
|
||||
}: NotificationModel) {
|
||||
const dispatch = useDispatch();
|
||||
const tagList = useTags();
|
||||
|
||||
const [isEditNotificationModalOpen, setIsEditNotificationModalOpen] =
|
||||
useState(false);
|
||||
const [isDeleteNotificationModalOpen, setIsDeleteNotificationModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const handleEditNotificationPress = useCallback(() => {
|
||||
setIsEditNotificationModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleEditNotificationModalClose = useCallback(() => {
|
||||
setIsEditNotificationModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleDeleteNotificationPress = useCallback(() => {
|
||||
setIsEditNotificationModalOpen(false);
|
||||
setIsDeleteNotificationModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleDeleteNotificationModalClose = useCallback(() => {
|
||||
setIsDeleteNotificationModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleConfirmDeleteNotification = useCallback(() => {
|
||||
dispatch(deleteNotification({ id }));
|
||||
}, [id, dispatch]);
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={styles.notification}
|
||||
overlayContent={true}
|
||||
onPress={handleEditNotificationPress}
|
||||
>
|
||||
<div className={styles.name}>{name}</div>
|
||||
|
||||
{supportsOnGrab && onGrab ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnGrab')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnDownload && onDownload ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnFileImport')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnUpgrade && onDownload && onUpgrade ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnFileUpgrade')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnImportComplete && onImportComplete ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnImportComplete')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnRename && onRename ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnRename')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnHealthIssue && onHealthIssue ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnHealthIssue')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnHealthRestored && onHealthRestored ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnHealthRestored')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnApplicationUpdate && onApplicationUpdate ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnApplicationUpdate')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnSeriesAdd && onSeriesAdd ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnSeriesAdd')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnSeriesDelete && onSeriesDelete ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnSeriesDelete')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnEpisodeFileDelete && onEpisodeFileDelete ? (
|
||||
<Label kind={kinds.SUCCESS}>{translate('OnEpisodeFileDelete')}</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnEpisodeFileDeleteForUpgrade &&
|
||||
onEpisodeFileDelete &&
|
||||
onEpisodeFileDeleteForUpgrade ? (
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnEpisodeFileDeleteForUpgrade')}
|
||||
</Label>
|
||||
) : null}
|
||||
|
||||
{supportsOnManualInteractionRequired && onManualInteractionRequired ? (
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('OnManualInteractionRequired')}
|
||||
</Label>
|
||||
) : null}
|
||||
|
||||
{!onGrab &&
|
||||
!onDownload &&
|
||||
!onRename &&
|
||||
!onImportComplete &&
|
||||
!onHealthIssue &&
|
||||
!onHealthRestored &&
|
||||
!onApplicationUpdate &&
|
||||
!onSeriesAdd &&
|
||||
!onSeriesDelete &&
|
||||
!onEpisodeFileDelete &&
|
||||
!onManualInteractionRequired ? (
|
||||
<Label kind={kinds.DISABLED} outline={true}>
|
||||
{translate('Disabled')}
|
||||
</Label>
|
||||
) : null}
|
||||
|
||||
<TagList tags={tags} tagList={tagList} />
|
||||
|
||||
<EditNotificationModal
|
||||
id={id}
|
||||
isOpen={isEditNotificationModalOpen}
|
||||
onModalClose={handleEditNotificationModalClose}
|
||||
onDeleteNotificationPress={handleDeleteNotificationPress}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={isDeleteNotificationModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteNotification')}
|
||||
message={translate('DeleteNotificationMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={handleConfirmDeleteNotification}
|
||||
onCancel={handleDeleteNotificationModalClose}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default Notification;
|
|
@ -1,19 +1,24 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import FormGroup from 'Components/Form/FormGroup';
|
||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormInputHelpText from 'Components/Form/FormInputHelpText';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import { CheckInputChanged } from 'typings/inputs';
|
||||
import Notification from 'typings/Notification';
|
||||
import { PendingSection } from 'typings/pending';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './NotificationEventItems.css';
|
||||
|
||||
function NotificationEventItems(props) {
|
||||
const {
|
||||
item,
|
||||
onInputChange
|
||||
} = props;
|
||||
interface NotificationEventItemsProps {
|
||||
item: PendingSection<Notification>;
|
||||
onInputChange: (change: CheckInputChanged) => void;
|
||||
}
|
||||
|
||||
function NotificationEventItems({
|
||||
item,
|
||||
onInputChange,
|
||||
}: NotificationEventItemsProps) {
|
||||
const {
|
||||
onGrab,
|
||||
onDownload,
|
||||
|
@ -41,7 +46,7 @@ function NotificationEventItems(props) {
|
|||
supportsOnManualInteractionRequired,
|
||||
supportsOnHealthIssue,
|
||||
supportsOnHealthRestored,
|
||||
includeHealthWarnings
|
||||
includeHealthWarnings,
|
||||
} = item;
|
||||
|
||||
return (
|
||||
|
@ -75,19 +80,18 @@ function NotificationEventItems(props) {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
onDownload.value &&
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onUpgrade"
|
||||
helpText={translate('OnFileUpgrade')}
|
||||
isDisabled={!supportsOnUpgrade.value}
|
||||
{...onUpgrade}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{onDownload.value && (
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onUpgrade"
|
||||
helpText={translate('OnFileUpgrade')}
|
||||
isDisabled={!supportsOnUpgrade.value}
|
||||
{...onUpgrade}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
|
@ -144,19 +148,18 @@ function NotificationEventItems(props) {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
onEpisodeFileDelete.value &&
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onEpisodeFileDeleteForUpgrade"
|
||||
helpText={translate('OnEpisodeFileDeleteForUpgrade')}
|
||||
isDisabled={!supportsOnEpisodeFileDeleteForUpgrade.value}
|
||||
{...onEpisodeFileDeleteForUpgrade}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{onEpisodeFileDelete.value && (
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="onEpisodeFileDeleteForUpgrade"
|
||||
helpText={translate('OnEpisodeFileDeleteForUpgrade')}
|
||||
isDisabled={!supportsOnEpisodeFileDeleteForUpgrade.value}
|
||||
{...onEpisodeFileDeleteForUpgrade}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
|
@ -180,19 +183,18 @@ function NotificationEventItems(props) {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
(onHealthIssue.value || onHealthRestored.value) &&
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeHealthWarnings"
|
||||
helpText={translate('IncludeHealthWarnings')}
|
||||
isDisabled={!supportsOnHealthIssue.value}
|
||||
{...includeHealthWarnings}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{(onHealthIssue.value || onHealthRestored.value) && (
|
||||
<div>
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="includeHealthWarnings"
|
||||
helpText={translate('IncludeHealthWarnings')}
|
||||
isDisabled={!supportsOnHealthIssue.value}
|
||||
{...includeHealthWarnings}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<FormInputGroup
|
||||
|
@ -221,9 +223,4 @@ function NotificationEventItems(props) {
|
|||
);
|
||||
}
|
||||
|
||||
NotificationEventItems.propTypes = {
|
||||
item: PropTypes.object.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default NotificationEventItems;
|
|
@ -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 AddNotificationModal from './AddNotificationModal';
|
||||
import EditNotificationModalConnector from './EditNotificationModalConnector';
|
||||
import Notification from './Notification';
|
||||
import styles from './Notifications.css';
|
||||
|
||||
class Notifications extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isAddNotificationModalOpen: false,
|
||||
isEditNotificationModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onAddNotificationPress = () => {
|
||||
this.setState({ isAddNotificationModalOpen: true });
|
||||
};
|
||||
|
||||
onAddNotificationModalClose = ({ notificationSelected = false } = {}) => {
|
||||
this.setState({
|
||||
isAddNotificationModalOpen: false,
|
||||
isEditNotificationModalOpen: notificationSelected
|
||||
});
|
||||
};
|
||||
|
||||
onEditNotificationModalClose = () => {
|
||||
this.setState({ isEditNotificationModalOpen: false });
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
tagList,
|
||||
onConfirmDeleteNotification,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isAddNotificationModalOpen,
|
||||
isEditNotificationModalOpen
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('Connections')}>
|
||||
<PageSectionContent
|
||||
errorMessage={translate('NotificationsLoadError')}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className={styles.notifications}>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<Notification
|
||||
key={item.id}
|
||||
{...item}
|
||||
tagList={tagList}
|
||||
onConfirmDeleteNotification={onConfirmDeleteNotification}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<Card
|
||||
className={styles.addNotification}
|
||||
onPress={this.onAddNotificationPress}
|
||||
>
|
||||
<div className={styles.center}>
|
||||
<Icon
|
||||
name={icons.ADD}
|
||||
size={45}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<AddNotificationModal
|
||||
isOpen={isAddNotificationModalOpen}
|
||||
onModalClose={this.onAddNotificationModalClose}
|
||||
/>
|
||||
|
||||
<EditNotificationModalConnector
|
||||
isOpen={isEditNotificationModalOpen}
|
||||
onModalClose={this.onEditNotificationModalClose}
|
||||
/>
|
||||
</PageSectionContent>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Notifications.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
tagList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onConfirmDeleteNotification: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Notifications;
|
|
@ -0,0 +1,94 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { NotificationAppState } 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 { fetchNotifications } from 'Store/Actions/settingsActions';
|
||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||
import NotificationModel from 'typings/Notification';
|
||||
import sortByProp from 'Utilities/Array/sortByProp';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AddNotificationModal from './AddNotificationModal';
|
||||
import EditNotificationModal from './EditNotificationModal';
|
||||
import Notification from './Notification';
|
||||
import styles from './Notifications.css';
|
||||
|
||||
function Notifications() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { error, isFetching, isPopulated, items } = useSelector(
|
||||
createSortedSectionSelector<NotificationModel, NotificationAppState>(
|
||||
'settings.notifications',
|
||||
sortByProp('name')
|
||||
)
|
||||
);
|
||||
|
||||
const [isAddNotificationModalOpen, setIsAddNotificationModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const [isEditNotificationModalOpen, setIsEditNotificationModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const handleAddNotificationPress = useCallback(() => {
|
||||
setIsAddNotificationModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleNotificationSelect = useCallback(() => {
|
||||
setIsAddNotificationModalOpen(false);
|
||||
setIsEditNotificationModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleAddNotificationModalClose = useCallback(() => {
|
||||
setIsAddNotificationModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleEditNotificationModalClose = useCallback(() => {
|
||||
setIsEditNotificationModalOpen(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchNotifications());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('Connections')}>
|
||||
<PageSectionContent
|
||||
errorMessage={translate('NotificationsLoadError')}
|
||||
error={error}
|
||||
isFetching={isFetching}
|
||||
isPopulated={isPopulated}
|
||||
>
|
||||
<div className={styles.notifications}>
|
||||
{items.map((item) => (
|
||||
<Notification key={item.id} {...item} />
|
||||
))}
|
||||
|
||||
<Card
|
||||
className={styles.addNotification}
|
||||
onPress={handleAddNotificationPress}
|
||||
>
|
||||
<div className={styles.center}>
|
||||
<Icon name={icons.ADD} size={45} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<AddNotificationModal
|
||||
isOpen={isAddNotificationModalOpen}
|
||||
onNotificationSelect={handleNotificationSelect}
|
||||
onModalClose={handleAddNotificationModalClose}
|
||||
/>
|
||||
|
||||
<EditNotificationModal
|
||||
isOpen={isEditNotificationModalOpen}
|
||||
onModalClose={handleEditNotificationModalClose}
|
||||
/>
|
||||
</PageSectionContent>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
|
||||
export default Notifications;
|
|
@ -1,63 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { deleteNotification, fetchNotifications } from 'Store/Actions/settingsActions';
|
||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||
import sortByProp from 'Utilities/Array/sortByProp';
|
||||
import Notifications from './Notifications';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createSortedSectionSelector('settings.notifications', sortByProp('name')),
|
||||
createTagsSelector(),
|
||||
(notifications, tagList) => {
|
||||
return {
|
||||
...notifications,
|
||||
tagList
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchNotifications,
|
||||
deleteNotification
|
||||
};
|
||||
|
||||
class NotificationsConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchNotifications();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onConfirmDeleteNotification = (id) => {
|
||||
this.props.deleteNotification({ id });
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Notifications
|
||||
{...this.props}
|
||||
onConfirmDeleteNotification={this.onConfirmDeleteNotification}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsConnector.propTypes = {
|
||||
fetchNotifications: PropTypes.func.isRequired,
|
||||
deleteNotification: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(NotificationsConnector);
|
|
@ -2,6 +2,33 @@ import Provider from './Provider';
|
|||
|
||||
interface Notification extends Provider {
|
||||
enable: boolean;
|
||||
onGrab: boolean;
|
||||
onDownload: boolean;
|
||||
onUpgrade: boolean;
|
||||
onImportComplete: boolean;
|
||||
onRename: boolean;
|
||||
onSeriesAdd: boolean;
|
||||
onSeriesDelete: boolean;
|
||||
onEpisodeFileDelete: boolean;
|
||||
onEpisodeFileDeleteForUpgrade: boolean;
|
||||
onHealthIssue: boolean;
|
||||
includeHealthWarnings: boolean;
|
||||
onHealthRestored: boolean;
|
||||
onApplicationUpdate: boolean;
|
||||
onManualInteractionRequired: boolean;
|
||||
supportsOnGrab: boolean;
|
||||
supportsOnDownload: boolean;
|
||||
supportsOnUpgrade: boolean;
|
||||
supportsOnImportComplete: boolean;
|
||||
supportsOnRename: boolean;
|
||||
supportsOnSeriesAdd: boolean;
|
||||
supportsOnSeriesDelete: boolean;
|
||||
supportsOnEpisodeFileDelete: boolean;
|
||||
supportsOnEpisodeFileDeleteForUpgrade: boolean;
|
||||
supportsOnHealthIssue: boolean;
|
||||
supportsOnHealthRestored: boolean;
|
||||
supportsOnApplicationUpdate: boolean;
|
||||
supportsOnManualInteractionRequired: boolean;
|
||||
tags: number[];
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue