mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
New: App Sync Profiles
This commit is contained in:
@@ -5,6 +5,7 @@ import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import AppProfilesConnector from 'Settings/Profiles/App/AppProfilesConnector';
|
||||
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import ApplicationsConnector from './Applications/ApplicationsConnector';
|
||||
@@ -45,6 +46,7 @@ class ApplicationSettings extends Component {
|
||||
|
||||
<PageContentBody>
|
||||
<ApplicationsConnector />
|
||||
<AppProfilesConnector />
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
|
31
frontend/src/Settings/Profiles/App/AppProfile.css
Normal file
31
frontend/src/Settings/Profiles/App/AppProfile.css
Normal file
@@ -0,0 +1,31 @@
|
||||
.appProfile {
|
||||
composes: card from '~Components/Card.css';
|
||||
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.nameContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.name {
|
||||
@add-mixin truncate;
|
||||
|
||||
margin-bottom: 20px;
|
||||
font-weight: 300;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.cloneButton {
|
||||
composes: button from '~Components/Link/IconButton.css';
|
||||
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.tooltipLabel {
|
||||
composes: label from '~Components/Label.css';
|
||||
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
155
frontend/src/Settings/Profiles/App/AppProfile.js
Normal file
155
frontend/src/Settings/Profiles/App/AppProfile.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Card from 'Components/Card';
|
||||
import Label from 'Components/Label';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditAppProfileModalConnector from './EditAppProfileModalConnector';
|
||||
import styles from './AppProfile.css';
|
||||
|
||||
class AppProfile extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isEditAppProfileModalOpen: false,
|
||||
isDeleteAppProfileModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onEditAppProfilePress = () => {
|
||||
this.setState({ isEditAppProfileModalOpen: true });
|
||||
}
|
||||
|
||||
onEditAppProfileModalClose = () => {
|
||||
this.setState({ isEditAppProfileModalOpen: false });
|
||||
}
|
||||
|
||||
onDeleteAppProfilePress = () => {
|
||||
this.setState({
|
||||
isEditAppProfileModalOpen: false,
|
||||
isDeleteAppProfileModalOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteAppProfileModalClose = () => {
|
||||
this.setState({ isDeleteAppProfileModalOpen: false });
|
||||
}
|
||||
|
||||
onConfirmDeleteAppProfile = () => {
|
||||
this.props.onConfirmDeleteAppProfile(this.props.id);
|
||||
}
|
||||
|
||||
onCloneAppProfilePress = () => {
|
||||
const {
|
||||
id,
|
||||
onCloneAppProfilePress
|
||||
} = this.props;
|
||||
|
||||
onCloneAppProfilePress(id);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
enableRss,
|
||||
enableAutomaticSearch,
|
||||
enableInteractiveSearch,
|
||||
isDeleting
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={styles.appProfile}
|
||||
overlayContent={true}
|
||||
onPress={this.onEditAppProfilePress}
|
||||
>
|
||||
<div className={styles.nameContainer}>
|
||||
<div className={styles.name}>
|
||||
{name}
|
||||
</div>
|
||||
|
||||
<IconButton
|
||||
className={styles.cloneButton}
|
||||
title={translate('CloneProfile')}
|
||||
name={icons.CLONE}
|
||||
onPress={this.onCloneAppProfilePress}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.enabled}>
|
||||
{
|
||||
<Label
|
||||
kind={enableRss ? kinds.SUCCESS : kinds.DISABLED}
|
||||
outline={!enableRss}
|
||||
>
|
||||
{translate('RSS')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
<Label
|
||||
kind={enableAutomaticSearch ? kinds.SUCCESS : kinds.DISABLED}
|
||||
outline={!enableAutomaticSearch}
|
||||
>
|
||||
{translate('AutomaticSearch')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
<Label
|
||||
kind={enableInteractiveSearch ? kinds.SUCCESS : kinds.DISABLED}
|
||||
outline={!enableInteractiveSearch}
|
||||
>
|
||||
{translate('InteractiveSearch')}
|
||||
</Label>
|
||||
}
|
||||
</div>
|
||||
|
||||
<EditAppProfileModalConnector
|
||||
id={id}
|
||||
isOpen={this.state.isEditAppProfileModalOpen}
|
||||
onModalClose={this.onEditAppProfileModalClose}
|
||||
onDeleteAppProfilePress={this.onDeleteAppProfilePress}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={this.state.isDeleteAppProfileModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteAppProfile')}
|
||||
message={translate('AppProfileDeleteConfirm', [name])}
|
||||
confirmLabel={translate('Delete')}
|
||||
isSpinning={isDeleting}
|
||||
onConfirm={this.onConfirmDeleteAppProfile}
|
||||
onCancel={this.onDeleteAppProfileModalClose}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppProfile.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
enableRss: PropTypes.bool.isRequired,
|
||||
enableAutomaticSearch: PropTypes.bool.isRequired,
|
||||
enableInteractiveSearch: PropTypes.bool.isRequired,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
onConfirmDeleteAppProfile: PropTypes.func.isRequired,
|
||||
onCloneAppProfilePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AppProfile;
|
@@ -0,0 +1,31 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import createAppProfileSelector from 'Store/Selectors/createAppProfileSelector';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createAppProfileSelector(),
|
||||
(appProfile) => {
|
||||
return {
|
||||
name: appProfile.name
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function AppProfileNameConnector({ name, ...otherProps }) {
|
||||
return (
|
||||
<span>
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
AppProfileNameConnector.propTypes = {
|
||||
appProfileId: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps)(AppProfileNameConnector);
|
21
frontend/src/Settings/Profiles/App/AppProfiles.css
Normal file
21
frontend/src/Settings/Profiles/App/AppProfiles.css
Normal file
@@ -0,0 +1,21 @@
|
||||
.appProfiles {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.addAppProfile {
|
||||
composes: appProfile from '~./AppProfile.css';
|
||||
|
||||
background-color: $cardAlternateBackgroundColor;
|
||||
color: $gray;
|
||||
text-align: center;
|
||||
font-size: 45px;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: inline-block;
|
||||
padding: 5px 20px 0;
|
||||
border: 1px solid $borderColor;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
}
|
107
frontend/src/Settings/Profiles/App/AppProfiles.js
Normal file
107
frontend/src/Settings/Profiles/App/AppProfiles.js
Normal file
@@ -0,0 +1,107 @@
|
||||
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 AppProfile from './AppProfile';
|
||||
import EditAppProfileModalConnector from './EditAppProfileModalConnector';
|
||||
import styles from './AppProfiles.css';
|
||||
|
||||
class AppProfiles extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isAppProfileModalOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onCloneAppProfilePress = (id) => {
|
||||
this.props.onCloneAppProfilePress(id);
|
||||
this.setState({ isAppProfileModalOpen: true });
|
||||
}
|
||||
|
||||
onEditAppProfilePress = () => {
|
||||
this.setState({ isAppProfileModalOpen: true });
|
||||
}
|
||||
|
||||
onModalClose = () => {
|
||||
this.setState({ isAppProfileModalOpen: false });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
isDeleting,
|
||||
onConfirmDeleteAppProfile,
|
||||
onCloneAppProfilePress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('AppProfiles')}>
|
||||
<PageSectionContent
|
||||
errorMessage={translate('UnableToLoadAppProfiles')}
|
||||
{...otherProps}c={true}
|
||||
>
|
||||
<div className={styles.appProfiles}>
|
||||
{
|
||||
items.map((item) => {
|
||||
return (
|
||||
<AppProfile
|
||||
key={item.id}
|
||||
{...item}
|
||||
isDeleting={isDeleting}
|
||||
onConfirmDeleteAppProfile={onConfirmDeleteAppProfile}
|
||||
onCloneAppProfilePress={this.onCloneAppProfilePress}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<Card
|
||||
className={styles.addAppProfile}
|
||||
onPress={this.onEditAppProfilePress}
|
||||
>
|
||||
<div className={styles.center}>
|
||||
<Icon
|
||||
name={icons.ADD}
|
||||
size={45}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<EditAppProfileModalConnector
|
||||
isOpen={this.state.isAppProfileModalOpen}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
</PageSectionContent>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppProfiles.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
isDeleting: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
onConfirmDeleteAppProfile: PropTypes.func.isRequired,
|
||||
onCloneAppProfilePress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default AppProfiles;
|
63
frontend/src/Settings/Profiles/App/AppProfilesConnector.js
Normal file
63
frontend/src/Settings/Profiles/App/AppProfilesConnector.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { cloneAppProfile, deleteAppProfile, fetchAppProfiles } from 'Store/Actions/settingsActions';
|
||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
||||
import sortByName from 'Utilities/Array/sortByName';
|
||||
import AppProfiles from './AppProfiles';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createSortedSectionSelector('settings.appProfiles', sortByName),
|
||||
(appProfiles) => appProfiles
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
dispatchFetchAppProfiles: fetchAppProfiles,
|
||||
dispatchDeleteAppProfile: deleteAppProfile,
|
||||
dispatchCloneAppProfile: cloneAppProfile
|
||||
};
|
||||
|
||||
class AppProfilesConnector extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
componentDidMount() {
|
||||
this.props.dispatchFetchAppProfiles();
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onConfirmDeleteAppProfile = (id) => {
|
||||
this.props.dispatchDeleteAppProfile({ id });
|
||||
}
|
||||
|
||||
onCloneAppProfilePress = (id) => {
|
||||
this.props.dispatchCloneAppProfile({ id });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AppProfiles
|
||||
onConfirmDeleteAppProfile={this.onConfirmDeleteAppProfile}
|
||||
onCloneAppProfilePress={this.onCloneAppProfilePress}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppProfilesConnector.propTypes = {
|
||||
dispatchFetchAppProfiles: PropTypes.func.isRequired,
|
||||
dispatchDeleteAppProfile: PropTypes.func.isRequired,
|
||||
dispatchCloneAppProfile: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(AppProfilesConnector);
|
37
frontend/src/Settings/Profiles/App/EditAppProfileModal.js
Normal file
37
frontend/src/Settings/Profiles/App/EditAppProfileModal.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import EditAppProfileModalContentConnector from './EditAppProfileModalContentConnector';
|
||||
|
||||
class EditAppProfileModal extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
onModalClose,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onModalClose={onModalClose}
|
||||
>
|
||||
<EditAppProfileModalContentConnector
|
||||
{...otherProps}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditAppProfileModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default EditAppProfileModal;
|
@@ -0,0 +1,43 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
||||
import EditAppProfileModal from './EditAppProfileModal';
|
||||
|
||||
function mapStateToProps() {
|
||||
return {};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
clearPendingChanges
|
||||
};
|
||||
|
||||
class EditAppProfileModalConnector extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onModalClose = () => {
|
||||
this.props.clearPendingChanges({ section: 'settings.appProfiles' });
|
||||
this.props.onModalClose();
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditAppProfileModal
|
||||
{...this.props}
|
||||
onModalClose={this.onModalClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditAppProfileModalConnector.propTypes = {
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
clearPendingChanges: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EditAppProfileModalConnector);
|
@@ -0,0 +1,3 @@
|
||||
.deleteButtonContainer {
|
||||
margin-right: auto;
|
||||
}
|
184
frontend/src/Settings/Profiles/App/EditAppProfileModalContent.js
Normal file
184
frontend/src/Settings/Profiles/App/EditAppProfileModalContent.js
Normal file
@@ -0,0 +1,184 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } 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 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 translate from 'Utilities/String/translate';
|
||||
import styles from './EditAppProfileModalContent.css';
|
||||
|
||||
class EditAppProfileModalContent extends Component {
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
isFetching,
|
||||
error,
|
||||
isSaving,
|
||||
saveError,
|
||||
item,
|
||||
isInUse,
|
||||
onInputChange,
|
||||
onSavePress,
|
||||
onModalClose,
|
||||
onDeleteAppProfilePress,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
enableRss,
|
||||
enableInteractiveSearch,
|
||||
enableAutomaticSearch
|
||||
} = item;
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
|
||||
<ModalHeader>
|
||||
{id ? translate('EditAppProfile') : translate('AddAppProfile')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
{
|
||||
isFetching &&
|
||||
<LoadingIndicator />
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !!error &&
|
||||
<div>
|
||||
{translate('UnableToAddANewAppProfilePleaseTryAgain')}
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!isFetching && !error &&
|
||||
<Form
|
||||
{...otherProps}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('Name')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
name="name"
|
||||
{...name}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('EnableRss')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableRss"
|
||||
{...enableRss}
|
||||
helpText={translate('EnableRssHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('EnableInteractiveSearch')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableInteractiveSearch"
|
||||
{...enableInteractiveSearch}
|
||||
helpText={translate('EnableInteractiveSearchHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>
|
||||
{translate('EnableAutomaticSearch')}
|
||||
</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticSearch"
|
||||
{...enableAutomaticSearch}
|
||||
helpText={translate('EnableAutomaticSearchHelpText')}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{
|
||||
id ?
|
||||
<div
|
||||
className={styles.deleteButtonContainer}
|
||||
title={
|
||||
isInUse ?
|
||||
translate('AppProfileInUse') :
|
||||
undefined
|
||||
}
|
||||
>
|
||||
<Button
|
||||
kind={kinds.DANGER}
|
||||
isDisabled={isInUse}
|
||||
onPress={onDeleteAppProfilePress}
|
||||
>
|
||||
{translate('Delete')}
|
||||
</Button>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
<Button
|
||||
onPress={onModalClose}
|
||||
>
|
||||
{translate('Cancel')}
|
||||
</Button>
|
||||
|
||||
<SpinnerErrorButton
|
||||
isSpinning={isSaving}
|
||||
error={saveError}
|
||||
onPress={onSavePress}
|
||||
>
|
||||
{translate('Save')}
|
||||
</SpinnerErrorButton>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditAppProfileModalContent.propTypes = {
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
item: PropTypes.object.isRequired,
|
||||
isInUse: PropTypes.bool.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
onSavePress: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
onDeleteAppProfilePress: PropTypes.func
|
||||
};
|
||||
|
||||
export default EditAppProfileModalContent;
|
@@ -0,0 +1,82 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchAppProfileSchema, saveAppProfile, setAppProfileValue } from 'Store/Actions/settingsActions';
|
||||
import createProfileInUseSelector from 'Store/Selectors/createProfileInUseSelector';
|
||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
||||
import EditAppProfileModalContent from './EditAppProfileModalContent';
|
||||
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createProviderSettingsSelector('appProfiles'),
|
||||
createProfileInUseSelector('appProfileId'),
|
||||
(appProfile, isInUse) => {
|
||||
return {
|
||||
...appProfile,
|
||||
isInUse
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchAppProfileSchema,
|
||||
setAppProfileValue,
|
||||
saveAppProfile
|
||||
};
|
||||
|
||||
class EditAppProfileModalContentConnector extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.id && !this.props.isPopulated) {
|
||||
this.props.fetchAppProfileSchema();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
||||
this.props.onModalClose();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onInputChange = ({ name, value }) => {
|
||||
this.props.setAppProfileValue({ name, value });
|
||||
}
|
||||
|
||||
onSavePress = () => {
|
||||
this.props.saveAppProfile({ id: this.props.id });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EditAppProfileModalContent
|
||||
{...this.state}
|
||||
{...this.props}
|
||||
onSavePress={this.onSavePress}
|
||||
onInputChange={this.onInputChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditAppProfileModalContentConnector.propTypes = {
|
||||
id: PropTypes.number,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
isPopulated: PropTypes.bool.isRequired,
|
||||
isSaving: PropTypes.bool.isRequired,
|
||||
saveError: PropTypes.object,
|
||||
item: PropTypes.object.isRequired,
|
||||
setAppProfileValue: PropTypes.func.isRequired,
|
||||
fetchAppProfileSchema: PropTypes.func.isRequired,
|
||||
saveAppProfile: PropTypes.func.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditAppProfileModalContentConnector);
|
Reference in New Issue
Block a user