diff --git a/.eslintignore b/.eslintignore index 6cb6a3bc3..1521c8b76 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1 @@ -client dist diff --git a/.eslintrc.json b/.eslintrc.json index d15dc3367..ac9157879 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -59,6 +59,7 @@ "no-var": [2], "no-lonely-if": [2], "curly": [2], + "no-unused-vars": ["error", { "argsIgnorePattern": "next" }], "no-multiple-empty-lines": [ "error", {"max": 1} diff --git a/app.js b/app.js index d4041c93a..29817c970 100644 --- a/app.js +++ b/app.js @@ -11,14 +11,57 @@ app.use(bodyParser.json()); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); -// API Routes. +// Routes. app.use('/api/v1', require('./routes/api')); +app.use('/client', express.static(path.join(__dirname, 'dist'))); +app.use('/admin', require('./routes/admin')); -// Static Routes. -app.use('/client/', express.static(path.join(__dirname, 'dist'))); +//============================================================================== +// ERROR HANDLING +//============================================================================== -app.get('/admin*', (req, res) => { - res.render('admin', {basePath: '/client/coral-admin'}); +const ErrNotFound = new Error('Not Found'); +ErrNotFound.status = 404; + +// Catch 404 and forward to error handler. +app.use((req, res, next) => { + next(ErrNotFound); +}); + +// General error handler. Respond with the message and error if we have it while +// returning a status code that makes sense. +if (app.get('env') === 'development') { + app.use('/api', (err, req, res, next) => { + res.status(err.status || 500); + res.json({ + message: err.message, + error: err + }); + }); + + app.use('/', (err, req, res, next) => { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +app.use('/api', (err, req, res, next) => { + res.status(err.status || 500); + res.json({ + message: err.message, + error: {} + }); +}); + +app.use('/', (err, req, res, next) => { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); }); module.exports = app; diff --git a/client/.eslintrc.json b/client/.eslintrc.json new file mode 100644 index 000000000..a935eaec6 --- /dev/null +++ b/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "extends": "../.eslintrc.json", + "parserOptions": { + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + }, + "sourceType": "module" + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error" + } +} diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js new file mode 100644 index 000000000..d00caa6c5 --- /dev/null +++ b/client/coral-admin/src/AppRouter.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {Router, Route, IndexRoute, browserHistory} from 'react-router'; + +import ModerationQueue from 'containers/ModerationQueue'; +import CommentStream from 'containers/CommentStream'; +import EmbedLink from 'components/EmbedLink'; +import Configure from 'containers/Configure'; +import CommunityContainer from 'containers/CommunityContainer'; +import LayoutContainer from 'containers/LayoutContainer'; + +const routes = ( + + + + + + + +); + +const AppRouter = () => ; + +export default AppRouter; diff --git a/client/coral-admin/src/actions/comments.js b/client/coral-admin/src/actions/comments.js index 04751276e..e4a55a893 100644 --- a/client/coral-admin/src/actions/comments.js +++ b/client/coral-admin/src/actions/comments.js @@ -4,15 +4,15 @@ */ export const updateStatus = (status, id) => (dispatch, getState) => { - dispatch({ type: 'COMMENT_STATUS_UPDATE', id, status }) - dispatch({ type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id) }) -} + dispatch({type: 'COMMENT_STATUS_UPDATE', id, status}); + dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)}); +}; export const flagComment = id => (dispatch, getState) => { - dispatch({ type: 'COMMENT_FLAG', id }) - dispatch({ type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id) }) -} + dispatch({type: 'COMMENT_FLAG', id}); + dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)}); +}; export const createComment = (name, body) => dispatch => { - dispatch({ type: 'COMMENT_CREATE', name, body }) -} + dispatch({type: 'COMMENT_CREATE', name, body}); +}; diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js index 488764c17..e4df4adfe 100644 --- a/client/coral-admin/src/actions/settings.js +++ b/client/coral-admin/src/actions/settings.js @@ -1,66 +1,66 @@ -export const SETTINGS_LOADING = 'SETTINGS_LOADING' -export const SETTINGS_RECEIVED = 'SETTINGS_RECEIVED' -export const SETTINGS_FETCH_ERROR = 'SETTINGS_FETCH_ERROR' +export const SETTINGS_LOADING = 'SETTINGS_LOADING'; +export const SETTINGS_RECEIVED = 'SETTINGS_RECEIVED'; +export const SETTINGS_FETCH_ERROR = 'SETTINGS_FETCH_ERROR'; -export const SETTINGS_UPDATED = 'SETTINGS_UPDATED' +export const SETTINGS_UPDATED = 'SETTINGS_UPDATED'; -export const SAVE_SETTINGS_LOADING = 'SAVE_SETTINGS_LOADING' -export const SAVE_SETTINGS_SUCCESS = 'SAVE_SETTINGS_SUCCESS' -export const SAVE_SETTINGS_FAILED = 'SAVE_SETTINGS_FAILED' +export const SAVE_SETTINGS_LOADING = 'SAVE_SETTINGS_LOADING'; +export const SAVE_SETTINGS_SUCCESS = 'SAVE_SETTINGS_SUCCESS'; +export const SAVE_SETTINGS_FAILED = 'SAVE_SETTINGS_FAILED'; -const base = '/api/v1' +const base = '/api/v1'; const getInit = (method, body) => { const headers = new Headers({ 'Content-Type': 'application/json', 'Accept': 'application/json' - }) + }); - const init = {method, headers} + const init = {method, headers}; if (method.toLowerCase() !== 'get') { - init.body = JSON.stringify(body) + init.body = JSON.stringify(body); } - return init -} + return init; +}; const handleResp = res => { if (res.status === 401) { - throw new Error('Not Authorized to make this request') + throw new Error('Not Authorized to make this request'); } else if (res.status > 399) { - throw new Error('Error! Status ' + res.status) + throw new Error('Error! Status ' + res.status); } else if (res.status === 204) { - return res.text() + return res.text(); } else { - return res.json() + return res.json(); } -} +}; export const fetchSettings = () => dispatch => { - dispatch({type: SETTINGS_LOADING}) + dispatch({type: SETTINGS_LOADING}); fetch(`${base}/settings`, getInit('GET')) .then(handleResp) .then(settings => { - dispatch({type: SETTINGS_RECEIVED, settings}) + dispatch({type: SETTINGS_RECEIVED, settings}); }) .catch(error => { - dispatch({type: SETTINGS_FETCH_ERROR, error}) - }) -} + dispatch({type: SETTINGS_FETCH_ERROR, error}); + }); +}; export const updateSettings = settings => { - return {type: SETTINGS_UPDATED, settings} -} + return {type: SETTINGS_UPDATED, settings}; +}; export const saveSettingsToServer = () => (dispatch, getState) => { - const settings = getState().settings.toJS().settings - dispatch({type: SAVE_SETTINGS_LOADING}) + const settings = getState().settings.toJS().settings; + dispatch({type: SAVE_SETTINGS_LOADING}); fetch(`${base}/settings`, getInit('PUT', settings)) .then(handleResp) .then(() => { - dispatch({type: SAVE_SETTINGS_SUCCESS, settings}) + dispatch({type: SAVE_SETTINGS_SUCCESS, settings}); }) .catch(error => { - dispatch({type: SAVE_SETTINGS_FAILED, error}) - }) -} + dispatch({type: SAVE_SETTINGS_FAILED, error}); + }); +}; diff --git a/client/coral-admin/src/components/App.js b/client/coral-admin/src/components/App.js index 81eea850f..3c6e88a14 100644 --- a/client/coral-admin/src/components/App.js +++ b/client/coral-admin/src/components/App.js @@ -1,25 +1,16 @@ +import React from 'react'; +import {Provider} from 'react-redux'; +import 'material-design-lite'; +import store from 'services/store'; -import React from 'react' -import { Provider } from 'react-redux' -import 'material-design-lite' -import { Router, Route, browserHistory } from 'react-router' -import ModerationQueue from 'containers/ModerationQueue' -import store from 'services/store' -import CommentStream from 'containers/CommentStream' -import EmbedLink from 'components/EmbedLink' -import Configure from 'containers/Configure' +import AppRouter from '../AppRouter'; export default class App extends React.Component { - render (props) { + render () { return ( - - - - - - + - ) + ); } } diff --git a/client/coral-admin/src/components/Comment.js b/client/coral-admin/src/components/Comment.js index 7406d42db..da08945a9 100644 --- a/client/coral-admin/src/components/Comment.js +++ b/client/coral-admin/src/components/Comment.js @@ -1,10 +1,10 @@ -import React from 'react' -import { Button, Icon } from 'react-mdl' -import timeago from 'timeago.js' -import styles from './CommentList.css' -import I18n from 'coral-framework/i18n/i18n' -import translations from '../translations' +import React from 'react'; +import {Button, Icon} from 'react-mdl'; +import timeago from 'timeago.js'; +import styles from './CommentList.css'; +import I18n from 'coral-framework/i18n/i18n'; +import translations from '../translations'; // Render a single comment for the list export default props => ( @@ -30,17 +30,17 @@ export default props => ( {props.comment.get('body')} -) +); // Check if an action can be performed over a comment const canShowAction = (action, comment) => { - const status = comment.get('status') - const flagged = comment.get('flagged') + const status = comment.get('status'); + const flagged = comment.get('flagged'); if (action === 'flag' && (status || flagged === true)) { - return false + return false; } - return true -} + return true; +}; -const lang = new I18n(translations) +const lang = new I18n(translations); diff --git a/client/coral-admin/src/components/CommentBox.js b/client/coral-admin/src/components/CommentBox.js index f0f0adbf2..ce3438960 100644 --- a/client/coral-admin/src/components/CommentBox.js +++ b/client/coral-admin/src/components/CommentBox.js @@ -1,23 +1,23 @@ -import React from 'react' -import styles from './CommentBox.css' -import { Button } from 'react-mdl' +import React from 'react'; +import styles from './CommentBox.css'; +import {Button} from 'react-mdl'; // Renders a comment box for creating a new comment export default class CommentBox extends React.Component { constructor (props) { - super(props) - this.state = { name: '', body: '' } - this.onSubmit = this.onSubmit.bind(this) + super(props); + this.state = {name: '', body: ''}; + this.onSubmit = this.onSubmit.bind(this); } onSubmit () { - const { name, body } = this.state - this.props.onSubmit({ name, body }) - this.setState({ body: '', name: '' }) + const {name, body} = this.state; + this.props.onSubmit({name, body}); + this.setState({body: '', name: ''}); } - render (props, { name, body }) { + render (props, {name, body}) { return (
@@ -30,6 +30,6 @@ export default class CommentBox extends React.Component {
- ) + ); } } diff --git a/client/coral-admin/src/components/CommentList.js b/client/coral-admin/src/components/CommentList.js index 02367911f..ae74272e7 100644 --- a/client/coral-admin/src/components/CommentList.js +++ b/client/coral-admin/src/components/CommentList.js @@ -1,99 +1,99 @@ -import React from 'react' -import styles from './CommentList.css' -import key from 'keymaster' -import Hammer from 'hammerjs' -import Comment from 'components/Comment' +import React from 'react'; +import styles from './CommentList.css'; +import key from 'keymaster'; +import Hammer from 'hammerjs'; +import Comment from 'components/Comment'; // Each action has different meaning and configuration const actions = { - 'reject': { status: 'Rejected', icon: 'close', key: 'r' }, - 'approve': { status: 'Approved', icon: 'done', key: 't' }, - 'flag': { status: 'flagged', icon: 'flag', filter: 'Untouched' } -} + 'reject': {status: 'Rejected', icon: 'close', key: 'r'}, + 'approve': {status: 'Approved', icon: 'done', key: 't'}, + 'flag': {status: 'flagged', icon: 'flag', filter: 'Untouched'} +}; // Renders a comment list and allow performing actions export default class CommentList extends React.Component { constructor (props) { - super(props) + super(props); - this.state = { active: null } - this.onClickAction = this.onClickAction.bind(this) + this.state = {active: null}; + this.onClickAction = this.onClickAction.bind(this); } // remove key handlers before leaving componentWillUnmount () { - this.unbindKeyHandlers() + this.unbindKeyHandlers(); } // add key handlers and gestures componentDidMount () { - this.bindKeyHandlers() + this.bindKeyHandlers(); // this.bindGestures() // need to check whether we're on a mobile device or this throws an Error } // If entering to singleview and no active, active is the first eleement componentWillReceiveProps (nextProps) { if (nextProps.singleView && !this.state.active) { - this.setState({ active: nextProps.commentIds.get(0) }) + this.setState({active: nextProps.commentIds.get(0)}); } } // Add swipe to approve or reject bindGestures () { - const { actions } = this.props - this._hammer = new Hammer(this.base) - this._hammer.get('swipe').set({ direction: Hammer.DIRECTION_HORIZONTAL }) + const {actions} = this.props; + this._hammer = new Hammer(this.base); + this._hammer.get('swipe').set({direction: Hammer.DIRECTION_HORIZONTAL}); if (actions.indexOf('reject') !== -1) { - this._hammer.on('swipeleft', () => this.props.singleView && this.actionKeyHandler('Rejected')) + this._hammer.on('swipeleft', () => this.props.singleView && this.actionKeyHandler('Rejected')); } if (actions.indexOf('approve') !== -1) { - this._hammer.on('swiperight', () => this.props.singleView && this.actionKeyHandler('Approved')) + this._hammer.on('swiperight', () => this.props.singleView && this.actionKeyHandler('Approved')); } } // Add key handlers. Each action has one and added j/k for moving around bindKeyHandlers () { this.props.actions.filter(action => actions[action].key).forEach(action => { - key(actions[action].key, 'commentList', () => this.props.isActive && this.actionKeyHandler(actions[action].status)) - }) - key('j', 'commentList', () => this.props.isActive && this.moveKeyHandler('down')) - key('k', 'commentList', () => this.props.isActive && this.moveKeyHandler('up')) - key.setScope('commentList') + key(actions[action].key, 'commentList', () => this.props.isActive && this.actionKeyHandler(actions[action].status)); + }); + key('j', 'commentList', () => this.props.isActive && this.moveKeyHandler('down')); + key('k', 'commentList', () => this.props.isActive && this.moveKeyHandler('up')); + key.setScope('commentList'); } // Perform an action using the keys only if the comment is active actionKeyHandler (action) { if (this.props.isActive && this.state.active) { - this.onClickAction(action, this.state.active) + this.onClickAction(action, this.state.active); } } // move around with j/k moveKeyHandler (direction) { if (!this.props.isActive) { - return + return; } - const { commentIds } = this.props - const { active } = this.state + const {commentIds} = this.props; + const {active} = this.state; // check boundaries - if (active == null || !commentIds.size) { - this.setState({ active: commentIds.get(0) }) + if (active === null || !commentIds.size) { + this.setState({active: commentIds.get(0)}); } else if (direction === 'up' && active !== commentIds.first()) { - this.setState({ active: commentIds.get(commentIds.indexOf(active) - 1) }) + this.setState({active: commentIds.get(commentIds.indexOf(active) - 1)}); } else if (direction === 'down' && active !== commentIds.last()) { - this.setState({ active: commentIds.get(commentIds.indexOf(active) + 1) }) + this.setState({active: commentIds.get(commentIds.indexOf(active) + 1)}); } // scroll to the position - const index = Math.max(commentIds.indexOf(this.state.active), 0) - this.base.childNodes[index] && this.base.childNodes[index].focus() + const index = Math.max(commentIds.indexOf(this.state.active), 0); + this.base.childNodes[index] && this.base.childNodes[index].focus(); } unbindKeyHandlers () { - key.deleteScope('commentList') + key.deleteScope('commentList'); } // If we are performing an action over a comment (aka removing from the list) we need to select a new active. @@ -101,26 +101,26 @@ export default class CommentList extends React.Component { // resolve since the content of the list could change externally. For now it works as expected onClickAction (action, id) { if (id === this.state.active) { - const { commentIds } = this.props + const {commentIds} = this.props; if (commentIds.last() === this.state.active) { - this.setState({ active: commentIds.get(commentIds.size - 2) }) + this.setState({active: commentIds.get(commentIds.size - 2)}); } else { - this.setState({ active: commentIds.get(Math.min(commentIds.indexOf(this.state.active) + 1, commentIds.size - 1)) }) + this.setState({active: commentIds.get(Math.min(commentIds.indexOf(this.state.active) + 1, commentIds.size - 1))}); } } - this.props.onClickAction(action, id) + this.props.onClickAction(action, id); } render () { - const {singleView, commentIds, comments, hideActive} = this.props - const {active} = this.state + const {singleView, commentIds, comments, hideActive} = this.props; + const {active} = this.state; return (
    {commentIds.map((commentId, index) => ( { if (el && commentId === active) { this._active = el } }} - key={index + 'comment'} + ref={el => { if (el && commentId === active) { this._active = el; } }} + key={`${index }comment`} index={index} onClickAction={this.onClickAction} actions={this.props.actions} @@ -129,6 +129,6 @@ export default class CommentList extends React.Component { hideActive={hideActive} /> )).toArray()}
- ) + ); } } diff --git a/client/coral-admin/src/components/EmbedLink.js b/client/coral-admin/src/components/EmbedLink.js index 21fe52f3f..41cbdf170 100644 --- a/client/coral-admin/src/components/EmbedLink.js +++ b/client/coral-admin/src/components/EmbedLink.js @@ -1,22 +1,22 @@ -import React from 'react' -import styles from './EmbedLink.css' -import I18n from 'coral-framework/i18n/i18n' -import translations from '../translations' -import { Button } from 'react-mdl' +import React from 'react'; +import styles from './EmbedLink.css'; +import I18n from 'coral-framework/i18n/i18n'; +import translations from '../translations'; +import {Button} from 'react-mdl'; const embedText = -`
` +`
`; -const copyToClipBoard = event => { - const copyTextarea = document.querySelector('.' + styles.embedTextarea) - copyTextarea.select() +const copyToClipBoard = () => { + const copyTextarea = document.querySelector(`.${ styles.embedTextarea}`); + copyTextarea.select(); try { - document.execCommand('copy') + document.execCommand('copy'); } catch (err) { - console.error('Unable to copy') + console.error('Unable to copy'); } -} +}; const EmbedLink = () =>

Embed Comment Stream

@@ -30,8 +30,8 @@ const EmbedLink = () =>
{lang.t('embedlink.copy')}
-
+; -export default EmbedLink +export default EmbedLink; -const lang = new I18n(translations) +const lang = new I18n(translations); diff --git a/client/coral-admin/src/components/Header.js b/client/coral-admin/src/components/Header.js index ea268108e..1a89da513 100644 --- a/client/coral-admin/src/components/Header.js +++ b/client/coral-admin/src/components/Header.js @@ -1,24 +1,23 @@ -import React from 'react' -import { Layout, Navigation, Drawer, Header } from 'react-mdl' -import { Link } from 'react-router' -import styles from './Header.css' -import config from 'services/config' +import React from 'react'; +import {Layout, Navigation, Drawer, Header} from 'react-mdl'; +import {Link} from 'react-router'; +import styles from './Header.css'; // App header. If we add a navbar it should be here export default (props) => (
- Moderate - Configure + Moderate + Configure
- Moderate - Configure + Moderate + Configure {props.children}
-) +); diff --git a/client/coral-admin/src/components/Modal.js b/client/coral-admin/src/components/Modal.js index 44535eda7..99c27c3a1 100644 --- a/client/coral-admin/src/components/Modal.js +++ b/client/coral-admin/src/components/Modal.js @@ -1,13 +1,13 @@ -import React from 'react' -import { Button, Icon } from 'react-mdl' -import styles from './Modal.css' +import React from 'react'; +import {Button, Icon} from 'react-mdl'; +import styles from './Modal.css'; -export default ({ open, children, onClose }) => ( +export default ({open, children, onClose}) => (
{children}
-) +); diff --git a/client/coral-admin/src/components/ModerationKeysModal.js b/client/coral-admin/src/components/ModerationKeysModal.js index efe235df7..5cf9fe17e 100644 --- a/client/coral-admin/src/components/ModerationKeysModal.js +++ b/client/coral-admin/src/components/ModerationKeysModal.js @@ -1,9 +1,8 @@ -import I18n from 'coral-framework/i18n/i18n' -import translations from '../translations' -import React from 'react' -import Modal from 'components/Modal' -import styles from './ModerationKeysModal.css' -import { Map } from 'immutable' +import I18n from 'coral-framework/i18n/i18n'; +import translations from '../translations'; +import React from 'react'; +import Modal from 'components/Modal'; +import styles from './ModerationKeysModal.css'; const shortcuts = [ { @@ -22,9 +21,9 @@ const shortcuts = [ 'r': 'modqueue.reject' } } -] +]; -export default ({ open, onClose }) => ( +export default ({open, onClose}) => (

{lang.t('modqueue.shortcuts')}

@@ -37,7 +36,7 @@ export default ({ open, onClose }) => ( {Object.keys(shortcut.shortcuts).map(key => ( - + {key} {lang.t(shortcut.shortcuts[key])} @@ -47,6 +46,6 @@ export default ({ open, onClose }) => ( ))}
-) +); -const lang = new I18n(translations) +const lang = new I18n(translations); diff --git a/client/coral-admin/src/components/Page.js b/client/coral-admin/src/components/Page.js index 733cec677..845c10cb8 100644 --- a/client/coral-admin/src/components/Page.js +++ b/client/coral-admin/src/components/Page.js @@ -1,7 +1,7 @@ -import React from 'react' -import {Layout} from 'react-mdl' -import 'material-design-lite' -import Header from 'components/Header' +import React from 'react'; +import {Layout} from 'react-mdl'; +import 'material-design-lite'; +import Header from 'components/Header'; export default (props) => ( @@ -9,4 +9,4 @@ export default (props) => ( {props.children} -) +); diff --git a/client/coral-admin/src/components/ui/Drawer.js b/client/coral-admin/src/components/ui/Drawer.js new file mode 100644 index 000000000..9ea727911 --- /dev/null +++ b/client/coral-admin/src/components/ui/Drawer.js @@ -0,0 +1,14 @@ +import React from 'react'; +import {Navigation, Drawer} from 'react-mdl'; +import {Link} from 'react-router'; +import styles from './Header.css'; + +export default () => ( + + + Moderate + Community + Configure + + +); diff --git a/client/coral-admin/src/components/Header.css b/client/coral-admin/src/components/ui/Header.css similarity index 100% rename from client/coral-admin/src/components/Header.css rename to client/coral-admin/src/components/ui/Header.css diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js new file mode 100644 index 000000000..b7c654bd4 --- /dev/null +++ b/client/coral-admin/src/components/ui/Header.js @@ -0,0 +1,14 @@ +import React from 'react'; +import {Navigation, Header} from 'react-mdl'; +import {Link} from 'react-router'; +import styles from './Header.css'; + +export default () => ( +
+ + Moderate + Community + Configure + +
+); diff --git a/client/coral-admin/src/components/ui/Layout.js b/client/coral-admin/src/components/ui/Layout.js new file mode 100644 index 000000000..e80d55c30 --- /dev/null +++ b/client/coral-admin/src/components/ui/Layout.js @@ -0,0 +1,12 @@ +import React from 'react'; +import {Layout as LayoutMDL} from 'react-mdl'; +import Header from './Header'; +import Drawer from './Drawer'; + +export const Layout = ({children}) => ( + +
+ + {children} + +); diff --git a/client/coral-admin/src/containers/CommentStream.js b/client/coral-admin/src/containers/CommentStream.js index 4f0a03698..da4d03a22 100644 --- a/client/coral-admin/src/containers/CommentStream.js +++ b/client/coral-admin/src/containers/CommentStream.js @@ -1,12 +1,10 @@ - -import React from 'react' -import styles from './CommentStream.css' -import { Snackbar } from 'react-mdl' -import { connect } from 'react-redux' -import { createComment, flagComment } from 'actions/comments' -import CommentList from 'components/CommentList' -import CommentBox from 'components/CommentBox' -import Page from 'components/Page' +import React from 'react'; +import styles from './CommentStream.css'; +import {Snackbar} from 'react-mdl'; +import {connect} from 'react-redux'; +import {createComment, flagComment} from 'actions/comments'; +import CommentList from 'components/CommentList'; +import CommentBox from 'components/CommentBox'; /** * Renders a comment stream using a CommentList component @@ -15,50 +13,48 @@ import Page from 'components/Page' class CommentStream extends React.Component { constructor (props) { - super(props) - this.state = { snackbar: false, snackbarMsg: '' } - this.onSubmit = this.onSubmit.bind(this) - this.onClickAction = this.onClickAction.bind(this) + super(props); + this.state = {snackbar: false, snackbarMsg: ''}; + this.onSubmit = this.onSubmit.bind(this); + this.onClickAction = this.onClickAction.bind(this); } // Fetch the comments before mounting componentWillMount () { - this.props.dispatch({ type: 'COMMENT_STREAM_FETCH' }) + this.props.dispatch({type: 'COMMENT_STREAM_FETCH'}); } // Submit the new comment onSubmit (comment) { - this.props.dispatch(createComment(comment.name, comment.body)) + this.props.dispatch(createComment(comment.name, comment.body)); } // The only action for now is flagging onClickAction (action, id) { if (action === 'flagged') { - this.props.dispatch(flagComment(id)) - clearTimeout(this._snackTimeout) - this.setState({ snackbar: true, snackbarMsg: 'Thank you for reporting this comment. Our moderation team has been notified and will review it shortly.' }) - this._snackTimeout = setTimeout(() => this.setState({ snackbar: false }), 30000) + this.props.dispatch(flagComment(id)); + clearTimeout(this._snackTimeout); + this.setState({snackbar: true, snackbarMsg: 'Thank you for reporting this comment. Our moderation team has been notified and will review it shortly.'}); + this._snackTimeout = setTimeout(() => this.setState({snackbar: false}), 30000); } } // Render the comment box along with the CommentList - render ({ comments }, { snackbar, snackbarMsg }) { + render ({comments}, {snackbar, snackbarMsg}) { return ( - -
- - - {snackbarMsg} -
-
- ) +
+ + + {snackbarMsg} +
+ ); } } -export default connect(({ comments }) => ({ comments }))(CommentStream) +export default connect(({comments}) => ({comments}))(CommentStream); diff --git a/client/coral-admin/src/containers/CommunityContainer.js b/client/coral-admin/src/containers/CommunityContainer.js new file mode 100644 index 000000000..b936edcec --- /dev/null +++ b/client/coral-admin/src/containers/CommunityContainer.js @@ -0,0 +1,11 @@ +import React, {Component} from 'react'; + +export default class CommunityContainer extends Component { + render() { + return ( +
+

Community

+
+ ); + } +} diff --git a/client/coral-admin/src/containers/Configure.css b/client/coral-admin/src/containers/Configure.css index b5ec3735b..98cb0e254 100644 --- a/client/coral-admin/src/containers/Configure.css +++ b/client/coral-admin/src/containers/Configure.css @@ -26,10 +26,19 @@ .configSettingEmbed { border: 1px solid #ccc; border-radius: 4px; - height: 90px; margin-bottom: 10px; display: block; - height: 170px; +} + +.copiedText { + color: #008000; + float: right; + padding: 12px; + font-size: 14px; +} + +.copyButton { + float: right; } .embedInput { @@ -41,6 +50,6 @@ margin-bottom: 10px; color: #555; padding: 14px; - font-size: 16px; + font-size: 14px; letter-spacing: 0.03em; } diff --git a/client/coral-admin/src/containers/Configure.js b/client/coral-admin/src/containers/Configure.js index 898c5fa74..9f0b32dcf 100644 --- a/client/coral-admin/src/containers/Configure.js +++ b/client/coral-admin/src/containers/Configure.js @@ -1,6 +1,7 @@ -import React from 'react' -import {connect} from 'react-redux' -import {fetchSettings, updateSettings, saveSettingsToServer} from '../actions/settings' + +import React from 'react'; +import {connect} from 'react-redux'; +import {fetchSettings, updateSettings, saveSettingsToServer} from '../actions/settings'; import { List, ListItem, @@ -10,31 +11,33 @@ import { Checkbox, Button, Icon -} from 'react-mdl' -import Page from 'components/Page' -import styles from './Configure.css' -import I18n from 'coral-framework/i18n/i18n' -import translations from '../translations' +} from 'react-mdl'; +import styles from './Configure.css'; +import I18n from 'coral-framework/i18n/i18n'; +import translations from '../translations'; class Configure extends React.Component { constructor (props) { - super(props) - this.state = {activeSection: 'comments'} - this.updateModeration = this.updateModeration.bind(this) - this.saveSettings = this.saveSettings.bind(this) + super(props); + + this.state = {activeSection: 'comments', copied: false}; + + this.copyToClipBoard = this.copyToClipBoard.bind(this); + this.updateModeration = this.updateModeration.bind(this); + this.saveSettings = this.saveSettings.bind(this); } componentWillMount () { - this.props.dispatch(fetchSettings()) + this.props.dispatch(fetchSettings()); } updateModeration () { - const moderation = this.props.settings.moderation === 'pre' ? 'post' : 'pre' - this.props.dispatch(updateSettings({moderation})) + const moderation = this.props.settings.moderation === 'pre' ? 'post' : 'pre'; + this.props.dispatch(updateSettings({moderation})); } saveSettings () { - this.props.dispatch(saveSettingsToServer()) + this.props.dispatch(saveSettingsToServer()); } getCommentSettings () { @@ -59,48 +62,51 @@ class Configure extends React.Component { error='Input is not a number!' label='Maximum Characters' /> - + ; } - copyToClipBoard (event) { - const copyTextarea = document.querySelector('.' + styles.embedInput) - copyTextarea.select() + copyToClipBoard () { + const copyTextarea = document.querySelector(`.${ styles.embedInput}`); + copyTextarea.select(); try { - document.execCommand('copy') + document.execCommand('copy'); + this.setState({copied: true}); } catch (err) { - console.error('Unable to copy') + console.error('Unable to copy', err); } } getEmbed () { const embedText = - `
` + `
`; return

Copy and paste code below into your CMS to embed your comment box in your articles

- - +