mirror of
https://github.com/wassname/talk.git
synced 2026-07-10 08:05:06 +08:00
keepin' it DRY
This commit is contained in:
@@ -150,7 +150,7 @@
|
||||
margin-top: 38px;
|
||||
}
|
||||
|
||||
.commentSettingsSection {
|
||||
.settingsSection {
|
||||
padding-bottom: 200px;
|
||||
.action {
|
||||
display: inline-block;
|
||||
|
||||
@@ -8,22 +8,20 @@ import {
|
||||
updateDomainlist
|
||||
} from '../../actions/settings';
|
||||
|
||||
import {Button, List, Item} from 'coral-ui';
|
||||
import {Button, List, Item, Card, Spinner} from 'coral-ui';
|
||||
import styles from './Configure.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import EmbedLink from './EmbedLink';
|
||||
import CommentSettings from './CommentSettings';
|
||||
import Wordlist from './Wordlist';
|
||||
import Domainlist from './Domainlist';
|
||||
import has from 'lodash/has';
|
||||
import translations from 'coral-admin/src/translations.json';
|
||||
import StreamSettings from './StreamSettings';
|
||||
import ModerationSettings from './ModerationSettings';
|
||||
import TechSettings from './TechSettings';
|
||||
|
||||
class Configure extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
activeSection: 'comments',
|
||||
activeSection: 'stream',
|
||||
changed: false,
|
||||
errors: {}
|
||||
};
|
||||
@@ -70,40 +68,51 @@ class Configure extends Component {
|
||||
|
||||
getSection (section) {
|
||||
const pageTitle = this.getPageTitle(section);
|
||||
let sectionComponent;
|
||||
switch(section){
|
||||
case 'comments':
|
||||
return <CommentSettings
|
||||
title={pageTitle}
|
||||
fetchingSettings={this.props.fetchingSettings}
|
||||
case 'stream':
|
||||
sectionComponent = <StreamSettings
|
||||
settings={this.props.settings}
|
||||
updateSettings={this.onSettingUpdate}
|
||||
errors={this.state.errors}
|
||||
settingsError={this.onSettingError}/>;
|
||||
case 'embed':
|
||||
return has(this, 'props.settings.domains.whitelist')
|
||||
? <div>
|
||||
<Domainlist
|
||||
domains={this.props.settings.domains.whitelist}
|
||||
onChangeDomainlist={this.onChangeDomainlist}/>
|
||||
<EmbedLink title={pageTitle} />
|
||||
</div>
|
||||
: <EmbedLink title={pageTitle} />;
|
||||
case 'wordlist':
|
||||
return has(this, 'props.settings.wordlist')
|
||||
? <Wordlist
|
||||
bannedWords={this.props.settings.wordlist.banned}
|
||||
suspectWords={this.props.settings.wordlist.suspect}
|
||||
onChangeWordlist={this.onChangeWordlist} />
|
||||
: <p>loading wordlists</p>;
|
||||
break;
|
||||
case 'moderation':
|
||||
sectionComponent = <ModerationSettings
|
||||
title={pageTitle}
|
||||
onChangeWordlist={this.onChangeWordlist}
|
||||
fetchingSettings={this.props.settings.fetchingSettings}
|
||||
settings={this.props.settings}
|
||||
updateSettings={this.onSettingUpdate} />;
|
||||
break;
|
||||
case 'tech':
|
||||
sectionComponent = <TechSettings
|
||||
title={pageTitle}
|
||||
fetchingSettings={this.props.settings.fetchingSettings}
|
||||
settings={this.props.settings}
|
||||
updateSettings={this.onSettingUpdate} />;
|
||||
}
|
||||
|
||||
if (this.props.settings.fetchingSettings) {
|
||||
return <Card shadow="4"><Spinner/>Loading settings...</Card>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.settingsSection}>
|
||||
<h3>{pageTitle}</h3>
|
||||
{sectionComponent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getPageTitle (section) {
|
||||
switch(section) {
|
||||
case 'comments':
|
||||
return lang.t('configure.comment-settings');
|
||||
case 'embed':
|
||||
return lang.t('configure.embed-comment-stream');
|
||||
case 'stream':
|
||||
return lang.t('configure.stream-settings');
|
||||
case 'moderation':
|
||||
return lang.t('configure.moderation-settings');
|
||||
case 'tech':
|
||||
return lang.t('configure.tech-settings');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -120,14 +129,14 @@ class Configure extends Component {
|
||||
<div className={styles.container}>
|
||||
<div className={styles.leftColumn}>
|
||||
<List onChange={this.changeSection} activeItem={activeSection}>
|
||||
<Item itemId='comments' icon="settings">
|
||||
{lang.t('configure.comment-settings')}
|
||||
<Item itemId='stream' icon='speaker_notes'>
|
||||
{lang.t('configure.stream-settings')}
|
||||
</Item>
|
||||
<Item itemId='embed' icon='code'>
|
||||
{lang.t('configure.embed-comment-stream')}
|
||||
<Item itemId='moderation' icon='thumbs_up_down'>
|
||||
{lang.t('configure.moderation-settings')}
|
||||
</Item>
|
||||
<Item itemId='wordlist' icon='settings'>
|
||||
{lang.t('configure.wordlist')}
|
||||
<Item itemId='tech' icon='code'>
|
||||
{lang.t('configure.tech-settings')}
|
||||
</Item>
|
||||
</List>
|
||||
<div className={styles.saveBox}>
|
||||
|
||||
@@ -9,19 +9,17 @@ const lang = new I18n(translations);
|
||||
|
||||
const Domainlist = ({domains, onChangeDomainlist}) => {
|
||||
return (
|
||||
<div>
|
||||
<h3>{lang.t('configure.domain-list-title')}</h3>
|
||||
<Card id={styles.domainlist}>
|
||||
<p className={styles.domainlistDesc}>{lang.t('configure.domain-list-text')}</p>
|
||||
<TagsInput
|
||||
value={domains}
|
||||
inputProps={{placeholder: 'URL'}}
|
||||
addOnPaste={true}
|
||||
pasteSplit={data => data.split(',').map(d => d.trim())}
|
||||
onChange={tags => onChangeDomainlist('whitelist', tags)}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
<Card id={styles.domainlist} className={styles.configSetting}>
|
||||
<h3 style={{margin: 0}}>{lang.t('configure.domain-list-title')}</h3>
|
||||
<p className={styles.domainlistDesc}>{lang.t('configure.domain-list-text')}</p>
|
||||
<TagsInput
|
||||
value={domains}
|
||||
inputProps={{placeholder: 'URL'}}
|
||||
addOnPaste={true}
|
||||
pasteSplit={data => data.split(',').map(d => d.trim())}
|
||||
onChange={tags => onChangeDomainlist('whitelist', tags)}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -44,19 +44,14 @@ class EmbedLink extends Component {
|
||||
"></script>
|
||||
`.trim();
|
||||
return (
|
||||
<div>
|
||||
<h3>{this.props.title}</h3>
|
||||
<div>
|
||||
<Card shadow="2">
|
||||
<p>{lang.t('configure.copy-and-paste')}</p>
|
||||
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
|
||||
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
|
||||
{lang.t('embedlink.copy')}
|
||||
</Button>
|
||||
<div className={styles.copiedText}>{this.state.copied && 'Copied!'}</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<Card shadow="2" className={styles.configSetting}>
|
||||
<p>{lang.t('configure.copy-and-paste')}</p>
|
||||
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
|
||||
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
|
||||
{lang.t('embedlink.copy')}
|
||||
</Button>
|
||||
<div className={styles.copiedText}>{this.state.copied && 'Copied!'}</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import styles from './Configure.css';
|
||||
import {Card} from 'coral-ui';
|
||||
import {Checkbox} from 'react-mdl';
|
||||
import Wordlist from './Wordlist';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const updateModeration = (updateSettings, mod) => () => {
|
||||
const moderation = mod === 'PRE' ? 'POST' : 'PRE';
|
||||
updateSettings({moderation});
|
||||
};
|
||||
|
||||
const ModerationSettings = ({settings, updateSettings, onChangeWordlist}) => {
|
||||
|
||||
// just putting this here for shorthand below
|
||||
const on = styles.enabledSetting;
|
||||
const off = styles.disabledSetting;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card className={`${styles.configSetting} ${settings.moderation === 'PRE' ? on : off}`}>
|
||||
<div className={styles.action}>
|
||||
<Checkbox
|
||||
onChange={updateModeration(updateSettings, settings.moderation)}
|
||||
checked={settings.moderation === 'PRE'} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.settingsHeader}>{lang.t('configure.enable-pre-moderation')}</div>
|
||||
<p className={settings.moderation === 'PRE' ? '' : styles.disabledSettingText}>
|
||||
{lang.t('configure.enable-pre-moderation-text')}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
<Wordlist
|
||||
bannedWords={settings.wordlist.banned}
|
||||
suspectWords={settings.wordlist.suspect}
|
||||
onChangeWordlist={onChangeWordlist} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ModerationSettings.propTypes = {
|
||||
onChangeWordlist: PropTypes.func.isRequired,
|
||||
settings: PropTypes.shape({
|
||||
moderation: PropTypes.string.isRequired,
|
||||
wordlist: PropTypes.shape({
|
||||
banned: PropTypes.array.isRequired,
|
||||
suspect: PropTypes.array.isRequired
|
||||
})
|
||||
}).isRequired,
|
||||
updateSettings: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ModerationSettings;
|
||||
+4
-43
@@ -4,7 +4,7 @@ import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import styles from './Configure.css';
|
||||
import {Textfield, Checkbox} from 'react-mdl';
|
||||
import {Card, Icon, Spinner} from 'coral-ui';
|
||||
import {Card, Icon} from 'coral-ui';
|
||||
|
||||
const TIMESTAMPS = {
|
||||
weeks: 60 * 60 * 24 * 7,
|
||||
@@ -27,11 +27,6 @@ const updateCharCount = (updateSettings, settingsError) => (event) => {
|
||||
updateSettings({charCount: charCount});
|
||||
};
|
||||
|
||||
const updateModeration = (updateSettings, mod) => () => {
|
||||
const moderation = mod === 'PRE' ? 'POST' : 'PRE';
|
||||
updateSettings({moderation});
|
||||
};
|
||||
|
||||
const updateEmailConfirmation = (updateSettings, verify) => () => {
|
||||
updateSettings({requireEmailConfirmation: !verify});
|
||||
};
|
||||
@@ -51,11 +46,6 @@ const updateClosedMessage = (updateSettings) => (event) => {
|
||||
updateSettings({closedMessage});
|
||||
};
|
||||
|
||||
const updateCustomCssUrl = (updateSettings) => (event) => {
|
||||
const customCssUrl = event.target.value;
|
||||
updateSettings({customCssUrl});
|
||||
};
|
||||
|
||||
// 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) => {
|
||||
@@ -71,31 +61,14 @@ const updateClosedTimeout = (updateSettings, ts, isMeasure) => (event) => {
|
||||
}
|
||||
};
|
||||
|
||||
const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError, settings, errors}) => {
|
||||
if (fetchingSettings) {
|
||||
return <Card shadow="4"><Spinner/>Loading settings...</Card>;
|
||||
}
|
||||
const StreamSettings = ({updateSettings, settingsError, settings, errors}) => {
|
||||
|
||||
// just putting this here for shorthand below
|
||||
const on = styles.enabledSetting;
|
||||
const off = styles.disabledSetting;
|
||||
|
||||
return (
|
||||
<div className={styles.commentSettingsSection}>
|
||||
<h3>{title}</h3>
|
||||
<Card className={`${styles.configSetting} ${settings.moderation === 'PRE' ? on : off}`}>
|
||||
<div className={styles.action}>
|
||||
<Checkbox
|
||||
onChange={updateModeration(updateSettings, settings.moderation)}
|
||||
checked={settings.moderation === 'PRE'} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.settingsHeader}>{lang.t('configure.enable-pre-moderation')}</div>
|
||||
<p className={settings.moderation === 'PRE' ? '' : styles.disabledSettingText}>
|
||||
{lang.t('configure.enable-pre-moderation-text')}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
<div>
|
||||
<Card className={`${styles.configSetting} ${settings.requireEmailConfirmation ? on : off}`}>
|
||||
<div className={styles.action}>
|
||||
<Checkbox
|
||||
@@ -193,23 +166,11 @@ const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className={styles.configSettingInfoBox}>
|
||||
<div className={styles.content}>
|
||||
{lang.t('configure.custom-css-url')}
|
||||
<p>{lang.t('configure.custom-css-url-desc')}</p>
|
||||
<br />
|
||||
<Textfield
|
||||
style={{width: '100%'}}
|
||||
label={lang.t('configure.custom-css-url')}
|
||||
value={settings.customCssUrl}
|
||||
onChange={updateCustomCssUrl(updateSettings)} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentSettings;
|
||||
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
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Card} from 'coral-ui';
|
||||
import {Textfield} from 'react-mdl';
|
||||
import Domainlist from './Domainlist';
|
||||
import EmbedLink from './EmbedLink';
|
||||
import styles from './Configure.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const updateCustomCssUrl = (updateSettings) => (event) => {
|
||||
const customCssUrl = event.target.value;
|
||||
updateSettings({customCssUrl});
|
||||
};
|
||||
|
||||
const TechSettings = ({settings, onChangeDomainlist, updateSettings}) => {
|
||||
return (
|
||||
<div>
|
||||
<Domainlist
|
||||
domains={settings.domains.whitelist}
|
||||
onChangeDomainlist={onChangeDomainlist} />
|
||||
<EmbedLink />
|
||||
<Card className={styles.configSettingInfoBox}>
|
||||
<div className={styles.content}>
|
||||
{lang.t('configure.custom-css-url')}
|
||||
<p>{lang.t('configure.custom-css-url-desc')}</p>
|
||||
<br />
|
||||
<Textfield
|
||||
style={{width: '100%'}}
|
||||
label={lang.t('configure.custom-css-url')}
|
||||
value={settings.customCssUrl}
|
||||
onChange={updateCustomCssUrl(updateSettings)} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TechSettings.propTypes = {
|
||||
settings: PropTypes.shape({
|
||||
domains: PropTypes.shape({
|
||||
whitelist: PropTypes.array.isRequired
|
||||
})
|
||||
}).isRequired,
|
||||
updateSettings: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default TechSettings;
|
||||
@@ -33,7 +33,7 @@ export default function settings (state = initialState, action) {
|
||||
.set('fetchSettingsError', null);
|
||||
case actions.SETTINGS_RECEIVED:
|
||||
return state.merge({
|
||||
fetchingSettings: null,
|
||||
fetchingSettings: false,
|
||||
fetchSettingsError: null,
|
||||
...action.settings
|
||||
});
|
||||
@@ -43,7 +43,7 @@ export default function settings (state = initialState, action) {
|
||||
.set('fetchSettingsError', action.error);
|
||||
case actions.SETTINGS_UPDATED:
|
||||
return state.merge({
|
||||
fetchingSettings: null,
|
||||
fetchingSettings: false,
|
||||
fetchSettingsError: null,
|
||||
...action.settings
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user