diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js
index 5b5f9a524..4238dbf79 100644
--- a/client/coral-admin/src/routes/Configure/components/Configure.js
+++ b/client/coral-admin/src/routes/Configure/components/Configure.js
@@ -2,11 +2,12 @@ import React, {Component} from 'react';
import {Button, List, Item} from 'coral-ui';
import styles from './Configure.css';
-import StreamSettings from './StreamSettings';
+import StreamSettings from '../containers/StreamSettings';
import ModerationSettings from './ModerationSettings';
import TechSettings from './TechSettings';
import t from 'coral-framework/services/i18n';
import {can} from 'coral-framework/services/perms';
+import PropTypes from 'prop-types';
export default class Configure extends Component {
@@ -23,9 +24,9 @@ export default class Configure extends Component {
switch(section){
case 'stream':
sectionComponent = ;
break;
case 'moderation':
@@ -105,3 +106,17 @@ export default class Configure extends Component {
);
}
}
+
+Configure.propTypes = {
+ notify: PropTypes.func.isRequired,
+ updateWordlist: PropTypes.func.isRequired,
+ updateDomainlist: PropTypes.func.isRequired,
+ updateSettings: PropTypes.func.isRequired,
+ errors: PropTypes.object.isRequired,
+ savePending: PropTypes.func.isRequired,
+ auth: PropTypes.object.isRequired,
+ data: PropTypes.object.isRequired,
+ root: PropTypes.object.isRequired,
+ settings: PropTypes.object.isRequired,
+ canSave: PropTypes.bool.isRequired,
+};
diff --git a/client/coral-admin/src/routes/Configure/components/Domainlist.js b/client/coral-admin/src/routes/Configure/components/Domainlist.js
index 7b7565c5e..890520e8e 100644
--- a/client/coral-admin/src/routes/Configure/components/Domainlist.js
+++ b/client/coral-admin/src/routes/Configure/components/Domainlist.js
@@ -1,4 +1,5 @@
import React from 'react';
+import PropTypes from 'prop-types';
import {Card} from 'coral-ui';
import styles from './Configure.css';
import TagsInput from 'coral-admin/src/components/TagsInput';
@@ -22,4 +23,9 @@ const Domainlist = ({domains, onChangeDomainlist}) => {
);
};
+Domainlist.propTypes = {
+ domains: PropTypes.array.isRequired,
+ onChangeDomainlist: PropTypes.func.isRequired,
+};
+
export default Domainlist;
diff --git a/client/coral-admin/src/routes/Configure/components/EmbedLink.js b/client/coral-admin/src/routes/Configure/components/EmbedLink.js
index 1ce3d5304..744692092 100644
--- a/client/coral-admin/src/routes/Configure/components/EmbedLink.js
+++ b/client/coral-admin/src/routes/Configure/components/EmbedLink.js
@@ -7,11 +7,7 @@ import {BASE_URL} from 'coral-framework/constants/url';
class EmbedLink extends Component {
- constructor (props) {
- super(props);
-
- this.state = {copied: false};
- }
+ state = {copied: false};
copyToClipBoard = () => {
const copyTextarea = document.querySelector(`.${styles.embedInput}`);
diff --git a/client/coral-admin/src/routes/Configure/components/StreamSettings.js b/client/coral-admin/src/routes/Configure/components/StreamSettings.js
index dbbd96932..96878c3c8 100644
--- a/client/coral-admin/src/routes/Configure/components/StreamSettings.js
+++ b/client/coral-admin/src/routes/Configure/components/StreamSettings.js
@@ -4,6 +4,7 @@ import t from 'coral-framework/services/i18n';
import styles from './Configure.css';
import {Checkbox, Textfield} from 'react-mdl';
import {Card, Icon, TextArea} from 'coral-ui';
+import PropTypes from 'prop-types';
import MarkdownEditor from 'coral-framework/components/MarkdownEditor';
const TIMESTAMPS = {
@@ -12,187 +13,6 @@ const TIMESTAMPS = {
hours: 60 * 60
};
-const updateCharCountEnable = (updateSettings, charCountChecked) => () => {
- const charCountEnable = !charCountChecked;
- updateSettings({charCountEnable});
-};
-
-const updateCharCount = (updateSettings) => (event) => {
- let error = null;
- const charCount = event.target.value;
- if (charCount.match(/[^0-9]/) || charCount.length === 0) {
- error = true;
- }
- updateSettings({charCount: charCount}, {setError: {'charCount': error}});
-};
-
-const updateInfoBoxEnable = (updateSettings, infoBox) => () => {
- const infoBoxEnable = !infoBox;
- updateSettings({infoBoxEnable});
-};
-
-const updateInfoBoxContent = (updateSettings) => (value) => {
- const infoBoxContent = value;
- updateSettings({infoBoxContent});
-};
-
-const updateAutoClose = (updateSettings, autoCloseStream) => () => {
- updateSettings({autoCloseStream});
-};
-
-const updateClosedMessage = (updateSettings) => (event) => {
- const closedMessage = event.target.value;
- updateSettings({closedMessage});
-};
-
-// If we are changing the measure we need to recalculate using the old amount
-// Same thing if we are just changing the amount
-const updateClosedTimeout = (updateSettings, ts, isMeasure) => (event) => {
- if (isMeasure) {
- const amount = getTimeoutAmount(ts);
- const closedTimeout = amount * TIMESTAMPS[event];
- updateSettings({closedTimeout});
- } else {
- const val = event.target.value;
- const measure = getTimeoutMeasure(ts);
- const closedTimeout = val * TIMESTAMPS[measure];
- updateSettings({closedTimeout});
- }
-};
-
-const updateEditCommentWindowLength = (updateSettings) => (e) => {
- const value = e.target.value;
- const valueAsNumber = parseFloat(value);
- const milliseconds = (!isNaN(valueAsNumber)) && (valueAsNumber * 1000);
- updateSettings({editCommentWindowLength: milliseconds || value});
-};
-
-const StreamSettings = ({updateSettings, settings, errors}) => {
-
- // just putting this here for shorthand below
- const on = styles.enabledSetting;
- const off = styles.disabledSetting;
-
- return (
-
-
{t('configure.stream_settings')}
-
-
-
-
-
-
{t('configure.comment_count_header')}
-
- {t('configure.comment_count_text_pre')}
-
- {t('configure.comment_count_text_post')}
- {
- errors.charCount &&
-
-
-
- {t('configure.comment_count_error')}
-
- }
-
-
-
-
-
-
-
-
-
- {t('configure.include_comment_stream')}
-
-
- {t('configure.include_comment_stream_desc')}
-
-
-
-
-
-
-
-
-
{t('configure.closed_stream_settings')}
-
{t('configure.closed_comments_desc')}
-
-
-
-
-
- {/* Edit Comment Timeframe */}
-
- {t('configure.edit_comment_timeframe_heading')}
-
- {t('configure.edit_comment_timeframe_text_pre')}
-
-
-
- {t('configure.edit_comment_timeframe_text_post')}
-
-
-
-
-
-
-
-
{t('configure.close_after')}
-
-
-
-
-
-
-
-
-
-
-
- {/* the above card should be the last one if at all possible because of z-index issues with the selects */}
-
- );
-};
-
-export default StreamSettings;
-
// To see if we are talking about weeks, days or hours
// We talk the remainder of the division and see if it's 0
const getTimeoutMeasure = (ts) => {
@@ -208,3 +28,206 @@ const getTimeoutMeasure = (ts) => {
// Dividing the amount by it's measure (hours, days, weeks) we
// obtain the amount of time
const getTimeoutAmount = (ts) => ts / TIMESTAMPS[getTimeoutMeasure(ts)];
+
+class StreamSettings extends React.Component {
+
+ updateCharCountEnable = () => {
+ const updater = {charCountEnable: {$set: !this.props.settings.charCountEnable}};
+ this.props.updatePending({updater});
+ };
+
+ updateCharCount = (event) => {
+ let error = null;
+ const charCount = event.target.value;
+ if (charCount.match(/[^0-9]/) || charCount.length === 0) {
+ error = true;
+ }
+
+ const updater = {charCount: {$set: charCount}};
+ const errorUpdater = {charCount: {$set: error}};
+
+ this.props.updatePending({updater, errorUpdater});
+ };
+
+ updateInfoBoxEnable = () => {
+ const updater = {infoBoxEnable: {$set: !this.props.settings.infoBoxEnable}};
+ this.props.updatePending({updater});
+ };
+
+ updateInfoBoxContent = (value) => {
+ const updater = {infoBoxContent: {$set: value}};
+ this.props.updatePending({updater});
+ };
+
+ updateClosedMessage = (event) => {
+ const updater = {closedMessage: {$set: event.target.value}};
+ this.props.updatePending({updater});
+ };
+
+ updateEditCommentWindowLength = (e) => {
+ const value = e.target.value;
+ const valueAsNumber = parseFloat(value);
+ const milliseconds = (!isNaN(valueAsNumber)) && (valueAsNumber * 1000);
+
+ const updater = {editCommentWindowLength: {$set: milliseconds || value}};
+ this.props.updatePending({updater});
+ };
+
+ updateAutoClose = () => {
+ const updater = {autoCloseStream: {$set: !this.props.settings.autoCloseStream}};
+ this.props.updatePending({updater});
+ };
+
+ updateClosedTimeout = (event) => {
+ const val = event.target.value;
+ const measure = getTimeoutMeasure(this.props.settings.closedTimeout);
+
+ const updater = {closedTimeout: {$set: val * TIMESTAMPS[measure]}};
+ this.props.updatePending({updater});
+ };
+
+ // If we are changing the measure we need to recalculate using the old amount
+ // Same thing if we are just changing the amount
+ updateClosedTimeoutMeasure = (event) => {
+ const amount = getTimeoutAmount(this.props.settings.closedTimeout);
+
+ const updater = {closedTimeout: {$set: amount * TIMESTAMPS[event]}};
+ this.props.updatePending({updater});
+ };
+
+ render() {
+ const {settings, errors} = this.props;
+
+ // just putting this here for shorthand below
+ const on = styles.enabledSetting;
+ const off = styles.disabledSetting;
+
+ return (
+
+
{t('configure.stream_settings')}
+
+
+
+
+
+
{t('configure.comment_count_header')}
+
+ {t('configure.comment_count_text_pre')}
+
+ {t('configure.comment_count_text_post')}
+ {
+ errors.charCount &&
+
+
+
+ {t('configure.comment_count_error')}
+
+ }
+
+
+
+
+
+
+
+
+
+ {t('configure.include_comment_stream')}
+
+
+ {t('configure.include_comment_stream_desc')}
+
+
+
+
+
+
+
+
+
{t('configure.closed_stream_settings')}
+
{t('configure.closed_comments_desc')}
+
+
+
+
+
+ {/* Edit Comment Timeframe */}
+
+ {t('configure.edit_comment_timeframe_heading')}
+
+ {t('configure.edit_comment_timeframe_text_pre')}
+
+
+
+ {t('configure.edit_comment_timeframe_text_post')}
+
+
+
+
+
+
+
+
{t('configure.close_after')}
+
+
+
+
+
+
+
+
+
+
+
+ {/* the above card should be the last one if at all possible because of z-index issues with the selects */}
+
+ );
+ }
+}
+
+StreamSettings.propTypes = {
+ updatePending: PropTypes.func.isRequired,
+ errors: PropTypes.object.isRequired,
+ data: PropTypes.object.isRequired,
+ root: PropTypes.object.isRequired,
+ settings: PropTypes.object.isRequired,
+};
+
+export default StreamSettings;
+
diff --git a/client/coral-admin/src/routes/Configure/components/Wordlist.js b/client/coral-admin/src/routes/Configure/components/Wordlist.js
index e96f37f9f..acc4382bb 100644
--- a/client/coral-admin/src/routes/Configure/components/Wordlist.js
+++ b/client/coral-admin/src/routes/Configure/components/Wordlist.js
@@ -2,6 +2,7 @@ import React from 'react';
import t from 'coral-framework/services/i18n';
import TagsInput from 'coral-admin/src/components/TagsInput';
import styles from './Configure.css';
+import PropTypes from 'prop-types';
import {Card} from 'coral-ui';
const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
@@ -30,4 +31,10 @@ const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
);
+Wordlist.propTypes = {
+ suspectWords: PropTypes.array.isRequired,
+ bannedWords: PropTypes.array.isRequired,
+ onChangeWordlist: PropTypes.func.isRequired,
+};
+
export default Wordlist;
diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js
index 2bfee8622..f4e0d769e 100644
--- a/client/coral-admin/src/routes/Configure/containers/Configure.js
+++ b/client/coral-admin/src/routes/Configure/containers/Configure.js
@@ -5,9 +5,11 @@ import {compose, gql} from 'react-apollo';
import withQuery from 'coral-framework/hocs/withQuery';
import {Spinner} from 'coral-ui';
import {notify} from 'coral-framework/actions/notification';
+import PropTypes from 'prop-types';
import merge from 'lodash/merge';
import {withUpdateSettings} from 'coral-framework/graphql/mutations';
-import {getErrorMessages} from 'coral-framework/utils';
+import {getErrorMessages, getDefinitionName} from 'coral-framework/utils';
+import StreamSettings from './StreamSettings';
import {
updatePending,
clearPending,
@@ -110,13 +112,17 @@ const withConfigureQuery = withQuery(gql`
domains {
whitelist
}
+ ...${getDefinitionName(StreamSettings.fragments.settings)}
}
+ ...${getDefinitionName(StreamSettings.fragments.root)}
}
+ ${StreamSettings.fragments.root}
+ ${StreamSettings.fragments.settings}
`, {
- options: () => ({
- variables: {},
- }),
- });
+ options: () => ({
+ variables: {},
+ }),
+});
const mapStateToProps = (state) => ({
auth: state.auth,
@@ -138,3 +144,15 @@ export default compose(
connect(mapStateToProps, mapDispatchToProps),
)(ConfigureContainer);
+ConfigureContainer.propTypes = {
+ updatePending: PropTypes.func.isRequired,
+ updateSettings: PropTypes.func.isRequired,
+ clearPending: PropTypes.func.isRequired,
+ notify: PropTypes.func.isRequired,
+ auth: PropTypes.object.isRequired,
+ data: PropTypes.object.isRequired,
+ root: PropTypes.object.isRequired,
+ errors: PropTypes.object.isRequired,
+ canSave: PropTypes.bool.isRequired,
+ pending: PropTypes.object.isRequired,
+};
diff --git a/client/coral-admin/src/routes/Configure/containers/StreamSettings.js b/client/coral-admin/src/routes/Configure/containers/StreamSettings.js
new file mode 100644
index 000000000..9a7f79fa6
--- /dev/null
+++ b/client/coral-admin/src/routes/Configure/containers/StreamSettings.js
@@ -0,0 +1,45 @@
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {compose, gql} from 'react-apollo';
+import StreamSettings from '../components/StreamSettings';
+import withFragments from 'coral-framework/hocs/withFragments';
+import {getSlotFragmentSpreads} from 'coral-framework/utils';
+import {updatePending} from '../../../actions/configure';
+
+const slots = [
+ 'adminStreamSettings',
+];
+
+const mapStateToProps = (state) => ({
+ errors: state.configure.errors,
+});
+
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators({
+ updatePending,
+ }, dispatch);
+
+export default compose(
+ withFragments({
+ root: gql`
+ fragment TalkAdmin_StreamSettings_root on RootQuery {
+ __typename
+ ${getSlotFragmentSpreads(slots, 'root')}
+ }
+ `,
+ settings: gql`
+ fragment TalkAdmin_StreamSettings_settings on Settings {
+ infoBoxEnable
+ charCount
+ charCountEnable
+ infoBoxContent
+ editCommentWindowLength
+ autoCloseStream
+ closedTimeout
+ closedMessage
+ ${getSlotFragmentSpreads(slots, 'settings')}
+ }
+ `
+ }),
+ connect(mapStateToProps, mapDispatchToProps),
+)(StreamSettings);