parent
52760e0908
commit
89b0b04e08
@ -1,28 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import FilterBuilderRowValue from './FilterBuilderRowValue';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.languageProfiles,
|
|
||||||
(languageProfiles) => {
|
|
||||||
const tagList = languageProfiles.items.map((languageProfile) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name
|
|
||||||
} = languageProfile;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
tagList
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(FilterBuilderRowValue);
|
|
@ -1,99 +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 createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
|
||||||
import sortByName from 'Utilities/Array/sortByName';
|
|
||||||
import SelectInput from './SelectInput';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createSortedSectionSelector('settings.languageProfiles', sortByName),
|
|
||||||
(state, { includeNoChange }) => includeNoChange,
|
|
||||||
(state, { includeMixed }) => includeMixed,
|
|
||||||
(languageProfiles, includeNoChange, includeMixed) => {
|
|
||||||
const values = _.map(languageProfiles.items, (languageProfile) => {
|
|
||||||
return {
|
|
||||||
key: languageProfile.id,
|
|
||||||
value: languageProfile.name
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (includeNoChange) {
|
|
||||||
values.unshift({
|
|
||||||
key: 'noChange',
|
|
||||||
value: 'No Change',
|
|
||||||
disabled: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeMixed) {
|
|
||||||
values.unshift({
|
|
||||||
key: 'mixed',
|
|
||||||
value: '(Mixed)',
|
|
||||||
disabled: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
values
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class LanguageProfileSelectInputConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
values
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!value || !_.some(values, (option) => parseInt(option.key) === value)) {
|
|
||||||
const firstValue = _.find(values, (option) => !isNaN(parseInt(option.key)));
|
|
||||||
|
|
||||||
if (firstValue) {
|
|
||||||
this.onChange({ name, value: firstValue.key });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onChange = ({ name, value }) => {
|
|
||||||
this.props.onChange({ name, value: parseInt(value) });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<SelectInput
|
|
||||||
{...this.props}
|
|
||||||
onChange={this.onChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfileSelectInputConnector.propTypes = {
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
||||||
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
includeNoChange: PropTypes.bool.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
LanguageProfileSelectInputConnector.defaultProps = {
|
|
||||||
includeNoChange: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(LanguageProfileSelectInputConnector);
|
|
@ -0,0 +1,52 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import EnhancedSelectInput from './EnhancedSelectInput';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state, { values }) => values,
|
||||||
|
( languages ) => {
|
||||||
|
|
||||||
|
const minId = languages.reduce((min, v) => (v.key < 1 ? v.key : min), languages[0].key);
|
||||||
|
|
||||||
|
const values = languages.map(({ key, value }) => {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
dividerAfter: minId < 1 ? key === minId : false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LanguageSelectInputConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EnhancedSelectInput
|
||||||
|
{...this.props}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LanguageSelectInputConnector.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]).isRequired,
|
||||||
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps)(LanguageSelectInputConnector);
|
@ -1,37 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import Label from 'Components/Label';
|
|
||||||
import { kinds } from 'Helpers/Props';
|
|
||||||
|
|
||||||
function EpisodeLanguage(props) {
|
|
||||||
const {
|
|
||||||
className,
|
|
||||||
language,
|
|
||||||
isCutoffNotMet
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!language) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
className={className}
|
|
||||||
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
|
|
||||||
>
|
|
||||||
{language.name}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EpisodeLanguage.propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
language: PropTypes.object,
|
|
||||||
isCutoffNotMet: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
EpisodeLanguage.defaultProps = {
|
|
||||||
isCutoffNotMet: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EpisodeLanguage;
|
|
@ -0,0 +1,69 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Label from 'Components/Label';
|
||||||
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
|
import { kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
|
|
||||||
|
function EpisodeLanguages(props) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
languages,
|
||||||
|
isCutoffNotMet
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
if (!languages) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languages.length === 1) {
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
className={className}
|
||||||
|
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
|
||||||
|
>
|
||||||
|
{languages[0].name}
|
||||||
|
</Label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
className={className}
|
||||||
|
anchor={
|
||||||
|
<Label
|
||||||
|
className={className}
|
||||||
|
kind={isCutoffNotMet ? kinds.INVERSE : kinds.DEFAULT}
|
||||||
|
>
|
||||||
|
Multi-Languages
|
||||||
|
</Label>
|
||||||
|
}
|
||||||
|
title={'Languages'}
|
||||||
|
body={
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
languages.map((language) => {
|
||||||
|
return (
|
||||||
|
<li key={language.id}>
|
||||||
|
{language.name}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
position={tooltipPositions.LEFT}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
EpisodeLanguages.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
languages: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
isCutoffNotMet: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
EpisodeLanguages.defaultProps = {
|
||||||
|
isCutoffNotMet: true
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EpisodeLanguages;
|
@ -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 EditLanguageProfileModalContentConnector from './EditLanguageProfileModalContentConnector';
|
|
||||||
|
|
||||||
function EditLanguageProfileModal({ isOpen, onModalClose, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
isOpen={isOpen}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
>
|
|
||||||
<EditLanguageProfileModalContentConnector
|
|
||||||
{...otherProps}
|
|
||||||
onModalClose={onModalClose}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditLanguageProfileModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditLanguageProfileModal;
|
|
@ -1,43 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { clearPendingChanges } from 'Store/Actions/baseActions';
|
|
||||||
import EditLanguageProfileModal from './EditLanguageProfileModal';
|
|
||||||
|
|
||||||
function mapStateToProps() {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
clearPendingChanges
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditLanguageProfileModalConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.props.clearPendingChanges({ section: 'settings.languageProfiles' });
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<EditLanguageProfileModal
|
|
||||||
{...this.props}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditLanguageProfileModalConnector.propTypes = {
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
clearPendingChanges: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(EditLanguageProfileModalConnector);
|
|
@ -1,3 +0,0 @@
|
|||||||
.deleteButtonContainer {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
@ -1,165 +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 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 LanguageProfileItems from './LanguageProfileItems';
|
|
||||||
import styles from './EditLanguageProfileModalContent.css';
|
|
||||||
|
|
||||||
function EditLanguageProfileModalContent(props) {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
error,
|
|
||||||
isSaving,
|
|
||||||
saveError,
|
|
||||||
languages,
|
|
||||||
item,
|
|
||||||
isInUse,
|
|
||||||
onInputChange,
|
|
||||||
onCutoffChange,
|
|
||||||
onSavePress,
|
|
||||||
onModalClose,
|
|
||||||
onDeleteLanguageProfilePress,
|
|
||||||
...otherProps
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
upgradeAllowed,
|
|
||||||
cutoff,
|
|
||||||
languages: itemLanguages
|
|
||||||
} = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ModalContent onModalClose={onModalClose}>
|
|
||||||
<ModalHeader>
|
|
||||||
{id ? 'Edit Language Profile' : 'Add Language Profile'}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
{
|
|
||||||
isFetching &&
|
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !!error &&
|
|
||||||
<div>Unable to add a new language profile, please try again.</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
!isFetching && !error &&
|
|
||||||
<Form {...otherProps}>
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>Name</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TEXT}
|
|
||||||
name="name"
|
|
||||||
{...name}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>
|
|
||||||
Upgrades Allowed
|
|
||||||
</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="upgradeAllowed"
|
|
||||||
{...upgradeAllowed}
|
|
||||||
helpText="If disabled languages will not be upgraded"
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{
|
|
||||||
upgradeAllowed.value &&
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>Upgrade Until</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.SELECT}
|
|
||||||
name="cutoff"
|
|
||||||
{...cutoff}
|
|
||||||
value={cutoff ? cutoff.value.id : 0}
|
|
||||||
values={languages}
|
|
||||||
helpText="Once this language is reached Sonarr will no longer download episodes"
|
|
||||||
onChange={onCutoffChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
}
|
|
||||||
|
|
||||||
<LanguageProfileItems
|
|
||||||
languageProfileItems={itemLanguages.value}
|
|
||||||
errors={itemLanguages.errors}
|
|
||||||
warnings={itemLanguages.warnings}
|
|
||||||
{...otherProps}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</Form>
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
{
|
|
||||||
id &&
|
|
||||||
<div
|
|
||||||
className={styles.deleteButtonContainer}
|
|
||||||
title={isInUse && 'Can\'t delete a language profile that is attached to a series'}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
isDisabled={isInUse}
|
|
||||||
onPress={onDeleteLanguageProfilePress}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onPress={onModalClose}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<SpinnerErrorButton
|
|
||||||
isSpinning={isSaving}
|
|
||||||
error={saveError}
|
|
||||||
onPress={onSavePress}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</SpinnerErrorButton>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditLanguageProfileModalContent.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
isInUse: PropTypes.bool.isRequired,
|
|
||||||
onInputChange: PropTypes.func.isRequired,
|
|
||||||
onCutoffChange: PropTypes.func.isRequired,
|
|
||||||
onSavePress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired,
|
|
||||||
onDeleteLanguageProfilePress: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditLanguageProfileModalContent;
|
|
@ -1,189 +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 { fetchLanguageProfileSchema, saveLanguageProfile, setLanguageProfileValue } from 'Store/Actions/settingsActions';
|
|
||||||
import createProfileInUseSelector from 'Store/Selectors/createProfileInUseSelector';
|
|
||||||
import createProviderSettingsSelector from 'Store/Selectors/createProviderSettingsSelector';
|
|
||||||
import EditLanguageProfileModalContent from './EditLanguageProfileModalContent';
|
|
||||||
|
|
||||||
function createLanguagesSelector() {
|
|
||||||
return createSelector(
|
|
||||||
createProviderSettingsSelector('languageProfiles'),
|
|
||||||
(languageProfile) => {
|
|
||||||
const languages = languageProfile.item.languages;
|
|
||||||
if (!languages || !languages.value) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return _.reduceRight(languages.value, (result, { allowed, language }) => {
|
|
||||||
if (allowed) {
|
|
||||||
result.push({
|
|
||||||
key: language.id,
|
|
||||||
value: language.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createProviderSettingsSelector('languageProfiles'),
|
|
||||||
createLanguagesSelector(),
|
|
||||||
createProfileInUseSelector('languageProfileId'),
|
|
||||||
(languageProfile, languages, isInUse) => {
|
|
||||||
return {
|
|
||||||
languages,
|
|
||||||
...languageProfile,
|
|
||||||
isInUse
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
fetchLanguageProfileSchema,
|
|
||||||
setLanguageProfileValue,
|
|
||||||
saveLanguageProfile
|
|
||||||
};
|
|
||||||
|
|
||||||
class EditLanguageProfileModalContentConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
dragIndex: null,
|
|
||||||
dropIndex: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (!this.props.id && !this.props.isPopulated) {
|
|
||||||
this.props.fetchLanguageProfileSchema();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) {
|
|
||||||
this.props.onModalClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onInputChange = ({ name, value }) => {
|
|
||||||
this.props.setLanguageProfileValue({ name, value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onCutoffChange = ({ name, value }) => {
|
|
||||||
const id = parseInt(value);
|
|
||||||
const item = _.find(this.props.item.languages.value, (i) => i.language.id === id);
|
|
||||||
|
|
||||||
this.props.setLanguageProfileValue({ name, value: item.language });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSavePress = () => {
|
|
||||||
this.props.saveLanguageProfile({ id: this.props.id });
|
|
||||||
};
|
|
||||||
|
|
||||||
onLanguageProfileItemAllowedChange = (id, allowed) => {
|
|
||||||
const languageProfile = _.cloneDeep(this.props.item);
|
|
||||||
|
|
||||||
const item = _.find(languageProfile.languages.value, (i) => i.language.id === id);
|
|
||||||
item.allowed = allowed;
|
|
||||||
|
|
||||||
this.props.setLanguageProfileValue({
|
|
||||||
name: 'languages',
|
|
||||||
value: languageProfile.languages.value
|
|
||||||
});
|
|
||||||
|
|
||||||
const cutoff = languageProfile.cutoff.value;
|
|
||||||
|
|
||||||
// If the cutoff isn't allowed anymore or there isn't a cutoff set one
|
|
||||||
if (!cutoff || !_.find(languageProfile.languages.value, (i) => i.language.id === cutoff.id).allowed) {
|
|
||||||
const firstAllowed = _.find(languageProfile.languages.value, { allowed: true });
|
|
||||||
|
|
||||||
this.props.setLanguageProfileValue({ name: 'cutoff', value: firstAllowed ? firstAllowed.language : null });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onLanguageProfileItemDragMove = (dragIndex, dropIndex) => {
|
|
||||||
if (this.state.dragIndex !== dragIndex || this.state.dropIndex !== dropIndex) {
|
|
||||||
this.setState({
|
|
||||||
dragIndex,
|
|
||||||
dropIndex
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onLanguageProfileItemDragEnd = ({ id }, didDrop) => {
|
|
||||||
const {
|
|
||||||
dragIndex,
|
|
||||||
dropIndex
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
if (didDrop && dropIndex !== null) {
|
|
||||||
const languageProfile = _.cloneDeep(this.props.item);
|
|
||||||
|
|
||||||
const languages = languageProfile.languages.value.splice(dragIndex, 1);
|
|
||||||
languageProfile.languages.value.splice(dropIndex, 0, languages[0]);
|
|
||||||
|
|
||||||
this.props.setLanguageProfileValue({
|
|
||||||
name: 'languages',
|
|
||||||
value: languageProfile.languages.value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
dragIndex: null,
|
|
||||||
dropIndex: null
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (_.isEmpty(this.props.item.languages) && !this.props.isFetching) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EditLanguageProfileModalContent
|
|
||||||
{...this.state}
|
|
||||||
{...this.props}
|
|
||||||
onSavePress={this.onSavePress}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onCutoffChange={this.onCutoffChange}
|
|
||||||
onLanguageProfileItemAllowedChange={this.onLanguageProfileItemAllowedChange}
|
|
||||||
onLanguageProfileItemDragMove={this.onLanguageProfileItemDragMove}
|
|
||||||
onLanguageProfileItemDragEnd={this.onLanguageProfileItemDragEnd}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditLanguageProfileModalContentConnector.propTypes = {
|
|
||||||
id: PropTypes.number,
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
isSaving: PropTypes.bool.isRequired,
|
|
||||||
saveError: PropTypes.object,
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
setLanguageProfileValue: PropTypes.func.isRequired,
|
|
||||||
fetchLanguageProfileSchema: PropTypes.func.isRequired,
|
|
||||||
saveLanguageProfile: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(EditLanguageProfileModalContentConnector);
|
|
@ -1,31 +0,0 @@
|
|||||||
.languageProfile {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.languages {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-top: 5px;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
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 EditLanguageProfileModalConnector from './EditLanguageProfileModalConnector';
|
|
||||||
import styles from './LanguageProfile.css';
|
|
||||||
|
|
||||||
class LanguageProfile extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isEditLanguageProfileModalOpen: false,
|
|
||||||
isDeleteLanguageProfileModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onEditLanguageProfilePress = () => {
|
|
||||||
this.setState({ isEditLanguageProfileModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onEditLanguageProfileModalClose = () => {
|
|
||||||
this.setState({ isEditLanguageProfileModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteLanguageProfilePress = () => {
|
|
||||||
this.setState({
|
|
||||||
isEditLanguageProfileModalOpen: false,
|
|
||||||
isDeleteLanguageProfileModalOpen: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteLanguageProfileModalClose = () => {
|
|
||||||
this.setState({ isDeleteLanguageProfileModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
onConfirmDeleteLanguageProfile = () => {
|
|
||||||
this.props.onConfirmDeleteLanguageProfile(this.props.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
onCloneLanguageProfilePress = () => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
onCloneLanguageProfilePress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onCloneLanguageProfilePress(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
upgradeAllowed,
|
|
||||||
cutoff,
|
|
||||||
languages,
|
|
||||||
isDeleting
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={styles.languageProfile}
|
|
||||||
overlayContent={true}
|
|
||||||
onPress={this.onEditLanguageProfilePress}
|
|
||||||
>
|
|
||||||
<div className={styles.nameContainer}>
|
|
||||||
<div className={styles.name}>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
className={styles.cloneButton}
|
|
||||||
title="Clone Profile"
|
|
||||||
name={icons.CLONE}
|
|
||||||
onPress={this.onCloneLanguageProfilePress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.languages}>
|
|
||||||
{
|
|
||||||
languages.map((item) => {
|
|
||||||
if (!item.allowed) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isCutoff = upgradeAllowed && item.language.id === cutoff.id;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Label
|
|
||||||
key={item.language.id}
|
|
||||||
kind={isCutoff ? kinds.INFO : kinds.DEFAULT}
|
|
||||||
title={isCutoff ? 'Upgrade until this language is met or exceeded' : null}
|
|
||||||
>
|
|
||||||
{item.language.name}
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditLanguageProfileModalConnector
|
|
||||||
id={id}
|
|
||||||
isOpen={this.state.isEditLanguageProfileModalOpen}
|
|
||||||
onModalClose={this.onEditLanguageProfileModalClose}
|
|
||||||
onDeleteLanguageProfilePress={this.onDeleteLanguageProfilePress}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={this.state.isDeleteLanguageProfileModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title="Delete Language Profile"
|
|
||||||
message={`Are you sure you want to delete the language profile '${name}'?`}
|
|
||||||
confirmLabel="Delete"
|
|
||||||
isSpinning={isDeleting}
|
|
||||||
onConfirm={this.onConfirmDeleteLanguageProfile}
|
|
||||||
onCancel={this.onDeleteLanguageProfileModalClose}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfile.propTypes = {
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
upgradeAllowed: PropTypes.bool.isRequired,
|
|
||||||
cutoff: PropTypes.object.isRequired,
|
|
||||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
|
||||||
onConfirmDeleteLanguageProfile: PropTypes.func.isRequired,
|
|
||||||
onCloneLanguageProfilePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LanguageProfile;
|
|
@ -1,44 +0,0 @@
|
|||||||
.languageProfileItem {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #aaa;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: var(--inputBackgroundColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkContainer {
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
margin-bottom: 7px;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.languageName {
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: 2px;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 36px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragHandle {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
width: $dragHandleWidth;
|
|
||||||
text-align: center;
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragIcon {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.isDragging {
|
|
||||||
opacity: 0.25;
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import styles from './LanguageProfileItem.css';
|
|
||||||
|
|
||||||
class LanguageProfileItem extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onAllowedChange = ({ value }) => {
|
|
||||||
const {
|
|
||||||
languageId,
|
|
||||||
onLanguageProfileItemAllowedChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
onLanguageProfileItemAllowedChange(languageId, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
allowed,
|
|
||||||
isDragging,
|
|
||||||
connectDragSource
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
styles.languageProfileItem,
|
|
||||||
isDragging && styles.isDragging
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className={styles.languageName}
|
|
||||||
>
|
|
||||||
<CheckInput
|
|
||||||
containerClassName={styles.checkContainer}
|
|
||||||
name={name}
|
|
||||||
value={allowed}
|
|
||||||
onChange={this.onAllowedChange}
|
|
||||||
/>
|
|
||||||
{name}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{
|
|
||||||
connectDragSource(
|
|
||||||
<div className={styles.dragHandle}>
|
|
||||||
<Icon
|
|
||||||
className={styles.dragIcon}
|
|
||||||
name={icons.REORDER}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfileItem.propTypes = {
|
|
||||||
languageId: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
allowed: PropTypes.bool.isRequired,
|
|
||||||
sortIndex: PropTypes.number.isRequired,
|
|
||||||
isDragging: PropTypes.bool.isRequired,
|
|
||||||
connectDragSource: PropTypes.func,
|
|
||||||
onLanguageProfileItemAllowedChange: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
LanguageProfileItem.defaultProps = {
|
|
||||||
// The drag preview will not connect the drag handle.
|
|
||||||
connectDragSource: (node) => node
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LanguageProfileItem;
|
|
@ -1,4 +0,0 @@
|
|||||||
.dragPreview {
|
|
||||||
width: 380px;
|
|
||||||
opacity: 0.75;
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { DragLayer } from 'react-dnd';
|
|
||||||
import DragPreviewLayer from 'Components/DragPreviewLayer';
|
|
||||||
import { QUALITY_PROFILE_ITEM } from 'Helpers/dragTypes';
|
|
||||||
import dimensions from 'Styles/Variables/dimensions.js';
|
|
||||||
import LanguageProfileItem from './LanguageProfileItem';
|
|
||||||
import styles from './LanguageProfileItemDragPreview.css';
|
|
||||||
|
|
||||||
const formGroupSmallWidth = parseInt(dimensions.formGroupSmallWidth);
|
|
||||||
const formLabelLargeWidth = parseInt(dimensions.formLabelLargeWidth);
|
|
||||||
const formLabelRightMarginWidth = parseInt(dimensions.formLabelRightMarginWidth);
|
|
||||||
const dragHandleWidth = parseInt(dimensions.dragHandleWidth);
|
|
||||||
|
|
||||||
function collectDragLayer(monitor) {
|
|
||||||
return {
|
|
||||||
item: monitor.getItem(),
|
|
||||||
itemType: monitor.getItemType(),
|
|
||||||
currentOffset: monitor.getSourceClientOffset()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class LanguageProfileItemDragPreview extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
item,
|
|
||||||
itemType,
|
|
||||||
currentOffset
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!currentOffset || itemType !== QUALITY_PROFILE_ITEM) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The offset is shifted because the drag handle is on the right edge of the
|
|
||||||
// list item and the preview is wider than the drag handle.
|
|
||||||
|
|
||||||
const { x, y } = currentOffset;
|
|
||||||
const handleOffset = formGroupSmallWidth - formLabelLargeWidth - formLabelRightMarginWidth - dragHandleWidth;
|
|
||||||
const transform = `translate3d(${x - handleOffset}px, ${y}px, 0)`;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
position: 'absolute',
|
|
||||||
WebkitTransform: transform,
|
|
||||||
msTransform: transform,
|
|
||||||
transform
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
languageId,
|
|
||||||
name,
|
|
||||||
allowed,
|
|
||||||
sortIndex
|
|
||||||
} = item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DragPreviewLayer>
|
|
||||||
<div
|
|
||||||
className={styles.dragPreview}
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
<LanguageProfileItem
|
|
||||||
languageId={languageId}
|
|
||||||
name={name}
|
|
||||||
allowed={allowed}
|
|
||||||
sortIndex={sortIndex}
|
|
||||||
isDragging={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</DragPreviewLayer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfileItemDragPreview.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
itemType: PropTypes.string,
|
|
||||||
currentOffset: PropTypes.shape({
|
|
||||||
x: PropTypes.number.isRequired,
|
|
||||||
y: PropTypes.number.isRequired
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DragLayer(collectDragLayer)(LanguageProfileItemDragPreview);
|
|
@ -1,18 +0,0 @@
|
|||||||
.languageProfileItemDragSource {
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.languageProfileItemPlaceholder {
|
|
||||||
width: 100%;
|
|
||||||
height: 36px;
|
|
||||||
border: 1px dotted #aaa;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.languageProfileItemPlaceholderBefore {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.languageProfileItemPlaceholderAfter {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { DragSource, DropTarget } from 'react-dnd';
|
|
||||||
import { findDOMNode } from 'react-dom';
|
|
||||||
import { QUALITY_PROFILE_ITEM } from 'Helpers/dragTypes';
|
|
||||||
import LanguageProfileItem from './LanguageProfileItem';
|
|
||||||
import styles from './LanguageProfileItemDragSource.css';
|
|
||||||
|
|
||||||
const languageProfileItemDragSource = {
|
|
||||||
beginDrag({ languageId, name, allowed, sortIndex }) {
|
|
||||||
return {
|
|
||||||
languageId,
|
|
||||||
name,
|
|
||||||
allowed,
|
|
||||||
sortIndex
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
endDrag(props, monitor, component) {
|
|
||||||
props.onLanguageProfileItemDragEnd(monitor.getItem(), monitor.didDrop());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const languageProfileItemDropTarget = {
|
|
||||||
hover(props, monitor, component) {
|
|
||||||
const dragIndex = monitor.getItem().sortIndex;
|
|
||||||
const hoverIndex = props.sortIndex;
|
|
||||||
|
|
||||||
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
|
|
||||||
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
||||||
const clientOffset = monitor.getClientOffset();
|
|
||||||
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
|
||||||
|
|
||||||
// Moving up, only trigger if drag position is above 50%
|
|
||||||
if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Moving down, only trigger if drag position is below 50%
|
|
||||||
if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
props.onLanguageProfileItemDragMove(dragIndex, hoverIndex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function collectDragSource(connect, monitor) {
|
|
||||||
return {
|
|
||||||
connectDragSource: connect.dragSource(),
|
|
||||||
isDragging: monitor.isDragging()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectDropTarget(connect, monitor) {
|
|
||||||
return {
|
|
||||||
connectDropTarget: connect.dropTarget(),
|
|
||||||
isOver: monitor.isOver()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class LanguageProfileItemDragSource extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
languageId,
|
|
||||||
name,
|
|
||||||
allowed,
|
|
||||||
sortIndex,
|
|
||||||
isDragging,
|
|
||||||
isDraggingUp,
|
|
||||||
isDraggingDown,
|
|
||||||
isOver,
|
|
||||||
connectDragSource,
|
|
||||||
connectDropTarget,
|
|
||||||
onLanguageProfileItemAllowedChange
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const isBefore = !isDragging && isDraggingUp && isOver;
|
|
||||||
const isAfter = !isDragging && isDraggingDown && isOver;
|
|
||||||
|
|
||||||
// if (isDragging && !isOver) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return connectDropTarget(
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
styles.languageProfileItemDragSource,
|
|
||||||
isBefore && styles.isDraggingUp,
|
|
||||||
isAfter && styles.isDraggingDown
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
isBefore &&
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
styles.languageProfileItemPlaceholder,
|
|
||||||
styles.languageProfileItemPlaceholderBefore
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
<LanguageProfileItem
|
|
||||||
languageId={languageId}
|
|
||||||
name={name}
|
|
||||||
allowed={allowed}
|
|
||||||
sortIndex={sortIndex}
|
|
||||||
isDragging={isDragging}
|
|
||||||
isOver={isOver}
|
|
||||||
connectDragSource={connectDragSource}
|
|
||||||
onLanguageProfileItemAllowedChange={onLanguageProfileItemAllowedChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
isAfter &&
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
styles.languageProfileItemPlaceholder,
|
|
||||||
styles.languageProfileItemPlaceholderAfter
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfileItemDragSource.propTypes = {
|
|
||||||
languageId: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
allowed: PropTypes.bool.isRequired,
|
|
||||||
sortIndex: PropTypes.number.isRequired,
|
|
||||||
isDragging: PropTypes.bool,
|
|
||||||
isDraggingUp: PropTypes.bool,
|
|
||||||
isDraggingDown: PropTypes.bool,
|
|
||||||
isOver: PropTypes.bool,
|
|
||||||
connectDragSource: PropTypes.func,
|
|
||||||
connectDropTarget: PropTypes.func,
|
|
||||||
onLanguageProfileItemAllowedChange: PropTypes.func.isRequired,
|
|
||||||
onLanguageProfileItemDragMove: PropTypes.func.isRequired,
|
|
||||||
onLanguageProfileItemDragEnd: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DropTarget(
|
|
||||||
QUALITY_PROFILE_ITEM,
|
|
||||||
languageProfileItemDropTarget,
|
|
||||||
collectDropTarget
|
|
||||||
)(DragSource(
|
|
||||||
QUALITY_PROFILE_ITEM,
|
|
||||||
languageProfileItemDragSource,
|
|
||||||
collectDragSource
|
|
||||||
)(LanguageProfileItemDragSource));
|
|
@ -1,6 +0,0 @@
|
|||||||
.languages {
|
|
||||||
margin-top: 10px;
|
|
||||||
/* TODO: This should consider the number of languages in the list */
|
|
||||||
min-height: 550px;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputHelpText from 'Components/Form/FormInputHelpText';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import LanguageProfileItemDragPreview from './LanguageProfileItemDragPreview';
|
|
||||||
import LanguageProfileItemDragSource from './LanguageProfileItemDragSource';
|
|
||||||
import styles from './LanguageProfileItems.css';
|
|
||||||
|
|
||||||
class LanguageProfileItems extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
dragIndex,
|
|
||||||
dropIndex,
|
|
||||||
languageProfileItems,
|
|
||||||
errors,
|
|
||||||
warnings,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const isDragging = dropIndex !== null;
|
|
||||||
const isDraggingUp = isDragging && dropIndex > dragIndex;
|
|
||||||
const isDraggingDown = isDragging && dropIndex < dragIndex;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>Languages</FormLabel>
|
|
||||||
<div>
|
|
||||||
<FormInputHelpText
|
|
||||||
text="Languages higher in the list are more preferred. Only checked languages are wanted"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
|
||||||
errors.map((error, index) => {
|
|
||||||
return (
|
|
||||||
<FormInputHelpText
|
|
||||||
key={index}
|
|
||||||
text={error.message}
|
|
||||||
isError={true}
|
|
||||||
isCheckInput={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
warnings.map((warning, index) => {
|
|
||||||
return (
|
|
||||||
<FormInputHelpText
|
|
||||||
key={index}
|
|
||||||
text={warning.message}
|
|
||||||
isWarning={true}
|
|
||||||
isCheckInput={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className={styles.languages}>
|
|
||||||
{
|
|
||||||
languageProfileItems.map(({ allowed, language }, index) => {
|
|
||||||
return (
|
|
||||||
<LanguageProfileItemDragSource
|
|
||||||
key={language.id}
|
|
||||||
languageId={language.id}
|
|
||||||
name={language.name}
|
|
||||||
allowed={allowed}
|
|
||||||
sortIndex={index}
|
|
||||||
isDragging={isDragging}
|
|
||||||
isDraggingUp={isDraggingUp}
|
|
||||||
isDraggingDown={isDraggingDown}
|
|
||||||
{...otherProps}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}).reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
<LanguageProfileItemDragPreview />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</FormGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfileItems.propTypes = {
|
|
||||||
dragIndex: PropTypes.number,
|
|
||||||
dropIndex: PropTypes.number,
|
|
||||||
languageProfileItems: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
errors: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
warnings: PropTypes.arrayOf(PropTypes.object)
|
|
||||||
};
|
|
||||||
|
|
||||||
LanguageProfileItems.defaultProps = {
|
|
||||||
errors: [],
|
|
||||||
warnings: []
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LanguageProfileItems;
|
|
@ -1,31 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import createLanguageProfileSelector from 'Store/Selectors/createLanguageProfileSelector';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
createLanguageProfileSelector(),
|
|
||||||
(languageProfile) => {
|
|
||||||
return {
|
|
||||||
name: languageProfile.name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function LanguageProfileNameConnector({ name, ...otherProps }) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
{name}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfileNameConnector.propTypes = {
|
|
||||||
languageProfileId: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(LanguageProfileNameConnector);
|
|
@ -1,21 +0,0 @@
|
|||||||
.languageProfiles {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addLanguageProfile {
|
|
||||||
composes: languageProfile from '~./LanguageProfile.css';
|
|
||||||
|
|
||||||
background-color: var(--cardAlternateBackgroundColor);
|
|
||||||
color: var(--gray);
|
|
||||||
text-align: center;
|
|
||||||
font-size: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 5px 20px 0;
|
|
||||||
border: 1px solid var(--borderColor);
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: var(--cardCenterBackgroundColor);
|
|
||||||
}
|
|
@ -1,107 +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 EditLanguageProfileModalConnector from './EditLanguageProfileModalConnector';
|
|
||||||
import LanguageProfile from './LanguageProfile';
|
|
||||||
import styles from './LanguageProfiles.css';
|
|
||||||
|
|
||||||
class LanguageProfiles extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isLanguageProfileModalOpen: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onCloneLanguageProfilePress = (id) => {
|
|
||||||
this.props.onCloneLanguageProfilePress(id);
|
|
||||||
this.setState({ isLanguageProfileModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onEditLanguageProfilePress = () => {
|
|
||||||
this.setState({ isLanguageProfileModalOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.setState({ isLanguageProfileModalOpen: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
items,
|
|
||||||
isDeleting,
|
|
||||||
onConfirmDeleteLanguageProfile,
|
|
||||||
onCloneLanguageProfilePress,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend="Language Profiles">
|
|
||||||
<PageSectionContent
|
|
||||||
errorMessage="Unable to load Language Profiles"
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
<div className={styles.languageProfiles}>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<LanguageProfile
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
isDeleting={isDeleting}
|
|
||||||
onConfirmDeleteLanguageProfile={onConfirmDeleteLanguageProfile}
|
|
||||||
onCloneLanguageProfilePress={this.onCloneLanguageProfilePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
<Card
|
|
||||||
className={styles.addLanguageProfile}
|
|
||||||
onPress={this.onEditLanguageProfilePress}
|
|
||||||
>
|
|
||||||
<div className={styles.center}>
|
|
||||||
<Icon
|
|
||||||
name={icons.ADD}
|
|
||||||
size={45}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EditLanguageProfileModalConnector
|
|
||||||
isOpen={this.state.isLanguageProfileModalOpen}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
/>
|
|
||||||
</PageSectionContent>
|
|
||||||
</FieldSet>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfiles.propTypes = {
|
|
||||||
advancedSettings: PropTypes.bool.isRequired,
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
error: PropTypes.object,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
|
||||||
onConfirmDeleteLanguageProfile: PropTypes.func.isRequired,
|
|
||||||
onCloneLanguageProfilePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LanguageProfiles;
|
|
@ -1,69 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { cloneLanguageProfile, deleteLanguageProfile, fetchLanguageProfiles } from 'Store/Actions/settingsActions';
|
|
||||||
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
|
|
||||||
import sortByName from 'Utilities/Array/sortByName';
|
|
||||||
import LanguageProfiles from './LanguageProfiles';
|
|
||||||
|
|
||||||
function createMapStateToProps() {
|
|
||||||
return createSelector(
|
|
||||||
(state) => state.settings.advancedSettings,
|
|
||||||
createSortedSectionSelector('settings.languageProfiles', sortByName),
|
|
||||||
(advancedSettings, languageProfiles) => {
|
|
||||||
return {
|
|
||||||
advancedSettings,
|
|
||||||
...languageProfiles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
dispatchFetchLanguageProfiles: fetchLanguageProfiles,
|
|
||||||
dispatchDeleteLanguageProfile: deleteLanguageProfile,
|
|
||||||
dispatchCloneLanguageProfile: cloneLanguageProfile
|
|
||||||
};
|
|
||||||
|
|
||||||
class LanguageProfilesConnector extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.dispatchFetchLanguageProfiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onConfirmDeleteLanguageProfile = (id) => {
|
|
||||||
this.props.dispatchDeleteLanguageProfile({ id });
|
|
||||||
};
|
|
||||||
|
|
||||||
onCloneLanguageProfilePress = (id) => {
|
|
||||||
this.props.dispatchCloneLanguageProfile({ id });
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<LanguageProfiles
|
|
||||||
onConfirmDeleteLanguageProfile={this.onConfirmDeleteLanguageProfile}
|
|
||||||
onCloneLanguageProfilePress={this.onCloneLanguageProfilePress}
|
|
||||||
{...this.props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LanguageProfilesConnector.propTypes = {
|
|
||||||
dispatchFetchLanguageProfiles: PropTypes.func.isRequired,
|
|
||||||
dispatchDeleteLanguageProfile: PropTypes.func.isRequired,
|
|
||||||
dispatchCloneLanguageProfile: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(LanguageProfilesConnector);
|
|
@ -1,97 +0,0 @@
|
|||||||
import { createAction } from 'redux-actions';
|
|
||||||
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
|
|
||||||
import createFetchSchemaHandler from 'Store/Actions/Creators/createFetchSchemaHandler';
|
|
||||||
import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler';
|
|
||||||
import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler';
|
|
||||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
|
||||||
import { createThunk } from 'Store/thunks';
|
|
||||||
import getSectionState from 'Utilities/State/getSectionState';
|
|
||||||
import updateSectionState from 'Utilities/State/updateSectionState';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Variables
|
|
||||||
|
|
||||||
const section = 'settings.languageProfiles';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Actions Types
|
|
||||||
|
|
||||||
export const FETCH_LANGUAGE_PROFILES = 'settings/languageProfiles/fetchLanguageProfiles';
|
|
||||||
export const FETCH_LANGUAGE_PROFILE_SCHEMA = 'settings/languageProfiles/fetchLanguageProfileSchema';
|
|
||||||
export const SAVE_LANGUAGE_PROFILE = 'settings/languageProfiles/saveLanguageProfile';
|
|
||||||
export const DELETE_LANGUAGE_PROFILE = 'settings/languageProfiles/deleteLanguageProfile';
|
|
||||||
export const SET_LANGUAGE_PROFILE_VALUE = 'settings/languageProfiles/setLanguageProfileValue';
|
|
||||||
export const CLONE_LANGUAGE_PROFILE = 'settings/languageProfiles/cloneLanguageProfile';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Action Creators
|
|
||||||
|
|
||||||
export const fetchLanguageProfiles = createThunk(FETCH_LANGUAGE_PROFILES);
|
|
||||||
export const fetchLanguageProfileSchema = createThunk(FETCH_LANGUAGE_PROFILE_SCHEMA);
|
|
||||||
export const saveLanguageProfile = createThunk(SAVE_LANGUAGE_PROFILE);
|
|
||||||
export const deleteLanguageProfile = createThunk(DELETE_LANGUAGE_PROFILE);
|
|
||||||
|
|
||||||
export const setLanguageProfileValue = createAction(SET_LANGUAGE_PROFILE_VALUE, (payload) => {
|
|
||||||
return {
|
|
||||||
section,
|
|
||||||
...payload
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const cloneLanguageProfile = createAction(CLONE_LANGUAGE_PROFILE);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Details
|
|
||||||
|
|
||||||
export default {
|
|
||||||
|
|
||||||
//
|
|
||||||
// State
|
|
||||||
|
|
||||||
defaultState: {
|
|
||||||
isFetching: false,
|
|
||||||
isPopulated: false,
|
|
||||||
error: null,
|
|
||||||
isDeleting: false,
|
|
||||||
deleteError: null,
|
|
||||||
isSchemaFetching: false,
|
|
||||||
isSchemaPopulated: false,
|
|
||||||
schemaError: null,
|
|
||||||
schema: {},
|
|
||||||
isSaving: false,
|
|
||||||
saveError: null,
|
|
||||||
items: [],
|
|
||||||
pendingChanges: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
//
|
|
||||||
// Action Handlers
|
|
||||||
|
|
||||||
actionHandlers: {
|
|
||||||
[FETCH_LANGUAGE_PROFILES]: createFetchHandler(section, '/languageprofile'),
|
|
||||||
[FETCH_LANGUAGE_PROFILE_SCHEMA]: createFetchSchemaHandler(section, '/languageprofile/schema'),
|
|
||||||
[SAVE_LANGUAGE_PROFILE]: createSaveProviderHandler(section, '/languageprofile'),
|
|
||||||
[DELETE_LANGUAGE_PROFILE]: createRemoveItemHandler(section, '/languageprofile')
|
|
||||||
},
|
|
||||||
|
|
||||||
//
|
|
||||||
// Reducers
|
|
||||||
|
|
||||||
reducers: {
|
|
||||||
[SET_LANGUAGE_PROFILE_VALUE]: createSetSettingValueReducer(section),
|
|
||||||
|
|
||||||
[CLONE_LANGUAGE_PROFILE]: function(state, { payload }) {
|
|
||||||
const id = payload.id;
|
|
||||||
const newState = getSectionState(state, section);
|
|
||||||
const item = newState.items.find((i) => i.id === id);
|
|
||||||
const pendingChanges = { ...item, id: 0 };
|
|
||||||
delete pendingChanges.id;
|
|
||||||
|
|
||||||
pendingChanges.name = `${pendingChanges.name} - Copy`;
|
|
||||||
newState.pendingChanges = pendingChanges;
|
|
||||||
|
|
||||||
return updateSectionState(state, section, newState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
@ -0,0 +1,48 @@
|
|||||||
|
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
|
||||||
|
import { createThunk } from 'Store/thunks';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
const section = 'settings.languages';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Actions Types
|
||||||
|
|
||||||
|
export const FETCH_LANGUAGES = 'settings/languages/fetchLanguages';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Action Creators
|
||||||
|
|
||||||
|
export const fetchLanguages = createThunk(FETCH_LANGUAGES);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Details
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
//
|
||||||
|
// State
|
||||||
|
|
||||||
|
defaultState: {
|
||||||
|
isFetching: false,
|
||||||
|
isPopulated: false,
|
||||||
|
error: null,
|
||||||
|
items: []
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Action Handlers
|
||||||
|
|
||||||
|
actionHandlers: {
|
||||||
|
[FETCH_LANGUAGES]: createFetchHandler(section, '/language')
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reducers
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue