parent
fb9a5efe05
commit
c62fc9d05b
@ -0,0 +1,6 @@
|
|||||||
|
import { AppSectionProviderState } from 'App/State/AppSectionState';
|
||||||
|
import Metadata from 'typings/Metadata';
|
||||||
|
|
||||||
|
interface MetadataAppState extends AppSectionProviderState<Metadata> {}
|
||||||
|
|
||||||
|
export default MetadataAppState;
|
@ -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 EditMetadataModalContentConnector from './EditMetadataModalContentConnector';
|
|
||||||
|
|
||||||
function EditMetadataModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<EditMetadataModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMetadataModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditMetadataModal;
|
|
@ -0,0 +1,36 @@
|
|||||||
|
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 EditMetadataModalContent, {
|
||||||
|
EditMetadataModalContentProps,
|
||||||
|
} from './EditMetadataModalContent';
|
||||||
|
|
||||||
|
interface EditMetadataModalProps extends EditMetadataModalContentProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditMetadataModal({
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
}: EditMetadataModalProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleModalClose = useCallback(() => {
|
||||||
|
dispatch(clearPendingChanges({ section: 'metadata' }));
|
||||||
|
onModalClose();
|
||||||
|
}, [dispatch, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal size={sizes.MEDIUM} isOpen={isOpen} onModalClose={handleModalClose}>
|
||||||
|
<EditMetadataModalContent
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={handleModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditMetadataModal;
|
@ -1,44 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|
||||||
import EditMetadataModal from './EditMetadataModal';
|
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
|
||||||
const section = 'settings.metadata';
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispatchClearPendingChanges() {
|
|
||||||
dispatch(clearPendingChanges({ section }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditMetadataModalConnector extends Component {
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.dispatchClearPendingChanges({ section: 'metadata' });
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditMetadataModal
|
|
||||||
{...this.props}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMetadataModalConnector.propTypes = {
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
dispatchClearPendingChanges: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(null, createMapDispatchToProps)(EditMetadataModalConnector);
|
|
@ -0,0 +1,5 @@
|
|||||||
|
.message {
|
||||||
|
composes: alert from '~Components/Alert.css';
|
||||||
|
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'message': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
@ -1,105 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
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 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 } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function EditMetadataModalContent(props) {
|
|
||||||
const {
|
|
||||||
advancedSettings,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
item,
|
|
||||||
onInputChange,
|
|
||||||
onFieldChange,
|
|
||||||
onModalClose,
|
|
||||||
onSavePress,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
enable,
|
|
||||||
fields
|
|
||||||
} = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('EditMetadata', { metadataType: name.value })}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<Form {...otherProps}>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('Enable')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="enable"
|
|
||||||
helpText={translate('EnableMetadataHelpText')}
|
|
||||||
{...enable}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{
|
|
||||||
fields.map((field) => {
|
|
||||||
return (
|
|
||||||
<ProviderFieldFormGroup
|
|
||||||
key={field.name}
|
|
||||||
advancedSettings={advancedSettings}
|
|
||||||
provider="metadata"
|
|
||||||
{...field}
|
|
||||||
isDisabled={!enable.value}
|
|
||||||
onChange={onFieldChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</Form>
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
{translate('Cancel')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<SpinnerErrorButton
|
|
||||||
isSpinning={isSaving}
|
|
||||||
error={saveError}
|
|
||||||
onPress={onSavePress}
|
|
||||||
>
|
|
||||||
{translate('Save')}
|
|
||||||
</SpinnerErrorButton>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMetadataModalContent.propTypes = {
|
|
||||||
advancedSettings: PropTypes.bool.isRequired,
|
|
||||||
isSaving: 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,
|
|
||||||
onDeleteMetadataPress: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditMetadataModalContent;
|
|
@ -0,0 +1,128 @@
|
|||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
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 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 } from 'Helpers/Props';
|
||||||
|
import {
|
||||||
|
saveMetadata,
|
||||||
|
setMetadataFieldValue,
|
||||||
|
setMetadataValue,
|
||||||
|
} from 'Store/Actions/settingsActions';
|
||||||
|
import selectSettings from 'Store/Selectors/selectSettings';
|
||||||
|
import { InputChanged } from 'typings/inputs';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './EditMetadataModalContent.css';
|
||||||
|
|
||||||
|
export interface EditMetadataModalContentProps {
|
||||||
|
id: number;
|
||||||
|
advancedSettings: boolean;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditMetadataModalContent({
|
||||||
|
id,
|
||||||
|
advancedSettings,
|
||||||
|
onModalClose,
|
||||||
|
}: EditMetadataModalContentProps) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const { isSaving, saveError, pendingChanges, items } = useSelector(
|
||||||
|
(state: AppState) => state.settings.metadata
|
||||||
|
);
|
||||||
|
|
||||||
|
const { settings, ...otherSettings } = useMemo(() => {
|
||||||
|
const item = items.find((item) => item.id === id)!;
|
||||||
|
|
||||||
|
return selectSettings(item, pendingChanges, saveError);
|
||||||
|
}, [id, items, pendingChanges, saveError]);
|
||||||
|
|
||||||
|
const { name, enable, fields, message } = settings;
|
||||||
|
|
||||||
|
const handleInputChange = useCallback(
|
||||||
|
({ name, value }: InputChanged) => {
|
||||||
|
// @ts-expect-error not typed
|
||||||
|
dispatch(setMetadataValue({ name, value }));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFieldChange = useCallback(
|
||||||
|
({ name, value }: InputChanged) => {
|
||||||
|
// @ts-expect-error not typed
|
||||||
|
dispatch(setMetadataFieldValue({ name, value }));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSavePress = useCallback(() => {
|
||||||
|
dispatch(saveMetadata({ id }));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{translate('EditMetadata', { metadataType: name.value })}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<Form {...otherSettings}>
|
||||||
|
{message ? (
|
||||||
|
<Alert className={styles.message} kind={message.value.type}>
|
||||||
|
{message.value.message}
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('Enable')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="enable"
|
||||||
|
helpText={translate('EnableMetadataHelpText')}
|
||||||
|
{...enable}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{fields.map((field) => {
|
||||||
|
return (
|
||||||
|
<ProviderFieldFormGroup
|
||||||
|
key={field.name}
|
||||||
|
advancedSettings={advancedSettings}
|
||||||
|
provider="metadata"
|
||||||
|
{...field}
|
||||||
|
isDisabled={!enable.value}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Form>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||||
|
|
||||||
|
<SpinnerErrorButton
|
||||||
|
isSpinning={isSaving}
|
||||||
|
error={saveError}
|
||||||
|
onPress={handleSavePress}
|
||||||
|
>
|
||||||
|
{translate('Save')}
|
||||||
|
</SpinnerErrorButton>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditMetadataModalContent;
|
@ -1,95 +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 { saveMetadata, setMetadataFieldValue, setMetadataValue } from 'Store/Actions/settingsActions';
|
|
||||||
import selectSettings from 'Store/Selectors/selectSettings';
|
|
||||||
import EditMetadataModalContent from './EditMetadataModalContent';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.advancedSettings,
|
|
||||||
(state, { id }) => id,
|
|
||||||
(state) => state.settings.metadata,
|
|
||||||
(advancedSettings, id, metadata) => {
|
|
||||||
const {
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
pendingChanges,
|
|
||||||
items
|
|
||||||
} = metadata;
|
|
||||||
|
|
||||||
const settings = selectSettings(_.find(items, { id }), pendingChanges, saveError);
|
|
||||||
|
|
||||||
return {
|
|
||||||
advancedSettings,
|
|
||||||
id,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
item: settings.settings,
|
|
||||||
...settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
setMetadataValue,
|
|
||||||
setMetadataFieldValue,
|
|
||||||
saveMetadata
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditMetadataModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.props.setMetadataValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onFieldChange = ({ name, value }) => {
|
|
||||||
this.props.setMetadataFieldValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
this.props.saveMetadata({ id: this.props.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditMetadataModalContent
|
|
||||||
{...this.props}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onFieldChange={this.onFieldChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditMetadataModalContentConnector.propTypes = {
|
|
||||||
id: PropTypes.number,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
setMetadataValue: PropTypes.func.isRequired,
|
|
||||||
setMetadataFieldValue: PropTypes.func.isRequired,
|
|
||||||
saveMetadata: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditMetadataModalContentConnector);
|
|
@ -1,150 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Card from 'Components/Card';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import EditMetadataModalConnector from './EditMetadataModalConnector';
|
|
||||||
import styles from './Metadata.css';
|
|
||||||
|
|
||||||
class Metadata extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isEditMetadataModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onEditMetadataPress = () => {
|
|
||||||
this.setState({ isEditMetadataModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onEditMetadataModalClose = () => {
|
|
||||||
this.setState({ isEditMetadataModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
enable,
|
|
||||||
fields
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const metadataFields = [];
|
|
||||||
const imageFields = [];
|
|
||||||
|
|
||||||
fields.forEach((field) => {
|
|
||||||
if (field.section === 'metadata') {
|
|
||||||
metadataFields.push(field);
|
|
||||||
} else {
|
|
||||||
imageFields.push(field);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={styles.metadata}
|
|
||||||
overlayContent={true}
|
|
||||||
onPress={this.onEditMetadataPress}
|
|
||||||
>
|
|
||||||
<div className={styles.name}>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
enable ?
|
|
||||||
<Label kind={kinds.SUCCESS}>
|
|
||||||
{translate('Enabled')}
|
|
||||||
</Label> :
|
|
||||||
<Label
|
|
||||||
kind={kinds.DISABLED}
|
|
||||||
outline={true}
|
|
||||||
>
|
|
||||||
{translate('Disabled')}
|
|
||||||
</Label>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
enable && !!metadataFields.length &&
|
|
||||||
<div>
|
|
||||||
<div className={styles.section}>
|
|
||||||
{translate('Metadata')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
metadataFields.map((field) => {
|
|
||||||
if (!field.value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={field.label}
|
|
||||||
kind={kinds.SUCCESS}
|
|
||||||
>
|
|
||||||
{field.label}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
enable && !!imageFields.length &&
|
|
||||||
<div>
|
|
||||||
<div className={styles.section}>
|
|
||||||
{translate('Images')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
imageFields.map((field) => {
|
|
||||||
if (!field.value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={field.label}
|
|
||||||
kind={kinds.SUCCESS}
|
|
||||||
>
|
|
||||||
{field.label}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<EditMetadataModalConnector
|
|
||||||
id={id}
|
|
||||||
isOpen={this.state.isEditMetadataModalOpen}
|
|
||||||
onModalClose={this.onEditMetadataModalClose}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Metadata.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
enable: PropTypes.bool.isRequired,
|
|
||||||
fields: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Metadata;
|
|
@ -0,0 +1,107 @@
|
|||||||
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
|
import Card from 'Components/Card';
|
||||||
|
import Label from 'Components/Label';
|
||||||
|
import { kinds } from 'Helpers/Props';
|
||||||
|
import Field from 'typings/Field';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import EditMetadataModal from './EditMetadataModal';
|
||||||
|
import styles from './Metadata.css';
|
||||||
|
|
||||||
|
interface MetadataProps {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
enable: boolean;
|
||||||
|
fields: Field[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function Metadata({ id, name, enable, fields }: MetadataProps) {
|
||||||
|
const [isEditMetadataModalOpen, setIsEditMetadataModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const { metadataFields, imageFields } = useMemo(() => {
|
||||||
|
return fields.reduce<{ metadataFields: Field[]; imageFields: Field[] }>(
|
||||||
|
(acc, field) => {
|
||||||
|
if (field.section === 'metadata') {
|
||||||
|
acc.metadataFields.push(field);
|
||||||
|
} else {
|
||||||
|
acc.imageFields.push(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ metadataFields: [], imageFields: [] }
|
||||||
|
);
|
||||||
|
}, [fields]);
|
||||||
|
|
||||||
|
const handleOpenPress = useCallback(() => {
|
||||||
|
setIsEditMetadataModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleModalClose = useCallback(() => {
|
||||||
|
setIsEditMetadataModalOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className={styles.metadata}
|
||||||
|
overlayContent={true}
|
||||||
|
onPress={handleOpenPress}
|
||||||
|
>
|
||||||
|
<div className={styles.name}>{name}</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{enable ? (
|
||||||
|
<Label kind={kinds.SUCCESS}>{translate('Enabled')}</Label>
|
||||||
|
) : (
|
||||||
|
<Label kind={kinds.DISABLED} outline={true}>
|
||||||
|
{translate('Disabled')}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{enable && metadataFields.length ? (
|
||||||
|
<div>
|
||||||
|
<div className={styles.section}>{translate('Metadata')}</div>
|
||||||
|
|
||||||
|
{metadataFields.map((field) => {
|
||||||
|
if (!field.value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label key={field.label} kind={kinds.SUCCESS}>
|
||||||
|
{field.label}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{enable && imageFields.length ? (
|
||||||
|
<div>
|
||||||
|
<div className={styles.section}>{translate('Images')}</div>
|
||||||
|
|
||||||
|
{imageFields.map((field) => {
|
||||||
|
if (!field.value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label key={field.label} kind={kinds.SUCCESS}>
|
||||||
|
{field.label}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<EditMetadataModal
|
||||||
|
advancedSettings={false}
|
||||||
|
id={id}
|
||||||
|
isOpen={isEditMetadataModalOpen}
|
||||||
|
onModalClose={handleModalClose}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Metadata;
|
@ -1,44 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import FieldSet from 'Components/FieldSet';
|
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import Metadata from './Metadata';
|
|
||||||
import styles from './Metadatas.css';
|
|
||||||
|
|
||||||
function Metadatas(props) {
|
|
||||||
const {
|
|
||||||
items,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend={translate('Metadata')}>
|
|
||||||
<PageSectionContent
|
|
||||||
errorMessage={translate('MetadataLoadError')}
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<div className={styles.metadatas}>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<Metadata
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</PageSectionContent>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Metadatas.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Metadatas;
|
|
@ -0,0 +1,52 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import MetadataAppState from 'App/State/MetadataAppState';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
|
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||||
|
import { fetchMetadata } from 'Store/Actions/settingsActions';
|
||||||
|
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||||
|
import MetadataType from 'typings/Metadata';
|
||||||
|
import sortByProp from 'Utilities/Array/sortByProp';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import Metadata from './Metadata';
|
||||||
|
import styles from './Metadatas.css';
|
||||||
|
|
||||||
|
function createMetadatasSelector() {
|
||||||
|
return createSelector(
|
||||||
|
createSortedSectionSelector<MetadataType>(
|
||||||
|
'settings.metadata',
|
||||||
|
sortByProp('name')
|
||||||
|
),
|
||||||
|
(metadata: MetadataAppState) => metadata
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Metadatas() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { isFetching, error, items, ...otherProps } = useSelector(
|
||||||
|
createMetadatasSelector()
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchMetadata());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet legend={translate('Metadata')}>
|
||||||
|
<PageSectionContent
|
||||||
|
isFetching={isFetching}
|
||||||
|
errorMessage={translate('MetadataLoadError')}
|
||||||
|
{...otherProps}
|
||||||
|
>
|
||||||
|
<div className={styles.metadatas}>
|
||||||
|
{items.map((item) => {
|
||||||
|
return <Metadata key={item.id} {...item} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</PageSectionContent>
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Metadatas;
|
@ -0,0 +1,7 @@
|
|||||||
|
import Provider from './Provider';
|
||||||
|
|
||||||
|
interface Metadata extends Provider {
|
||||||
|
enable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Metadata;
|
@ -0,0 +1,20 @@
|
|||||||
|
import ModelBase from 'App/ModelBase';
|
||||||
|
import { Kind } from 'Helpers/Props/kinds';
|
||||||
|
import Field from './Field';
|
||||||
|
|
||||||
|
export interface ProviderMessage {
|
||||||
|
message: string;
|
||||||
|
type: Extract<Kind, 'info' | 'error' | 'warning'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Provider extends ModelBase {
|
||||||
|
name: string;
|
||||||
|
fields: Field[];
|
||||||
|
implementationName: string;
|
||||||
|
implementation: string;
|
||||||
|
configContract: string;
|
||||||
|
infoLink: string;
|
||||||
|
message: ProviderMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Provider;
|
@ -0,0 +1,34 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Core.Extras.Metadata;
|
||||||
|
using NzbDrone.Core.Extras.Metadata.Consumers.Kometa;
|
||||||
|
using NzbDrone.Core.Localization;
|
||||||
|
using NzbDrone.Core.ThingiProvider.Events;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.HealthCheck.Checks
|
||||||
|
{
|
||||||
|
[CheckOn(typeof(ProviderUpdatedEvent<IMetadata>))]
|
||||||
|
public class MetadataCheck : HealthCheckBase
|
||||||
|
{
|
||||||
|
private readonly IMetadataFactory _metadataFactory;
|
||||||
|
|
||||||
|
public MetadataCheck(IMetadataFactory metadataFactory, ILocalizationService localizationService)
|
||||||
|
: base(localizationService)
|
||||||
|
{
|
||||||
|
_metadataFactory = metadataFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HealthCheck Check()
|
||||||
|
{
|
||||||
|
var enabled = _metadataFactory.Enabled();
|
||||||
|
|
||||||
|
if (enabled.Any(m => m.Definition.Implementation == nameof(KometaMetadata)))
|
||||||
|
{
|
||||||
|
return new HealthCheck(GetType(),
|
||||||
|
HealthCheckResult.Warning,
|
||||||
|
$"{_localizationService.GetLocalizedString("MetadataKometaDeprecated")}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HealthCheck(GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue