Merge branch 'master' into mod-queue

This commit is contained in:
gaba
2016-11-08 08:57:11 -08:00
23 changed files with 182 additions and 163 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
{
"base": "client/coral-admin"
"basePath": "client/coral-admin"
}
+3 -3
View File
@@ -26,8 +26,8 @@
},
"devDependencies": {
"autoprefixer": "6.5.0",
"buble": "0.13.0",
"buble-loader": "0.3.0",
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"copy-webpack-plugin": "3.0.1",
"css-loader": "0.25.0",
"json-loader": "0.5.4",
@@ -36,7 +36,7 @@
"precss": "1.4.0",
"standard": "8.2.0",
"style-loader": "0.13.1",
"webpack": "2.1.0-beta.25",
"webpack": "^1.13.3",
"webpack-dev-server": "1.16.1"
}
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
plugins: [
require('postcss-smart-import')({ /* ...options */ }),
require('precss')({ /* ...options */ }),
require('autoprefixer')({ /* ...options */ })
]
}
+1 -1
View File
@@ -17,6 +17,6 @@
</head>
<body>
<div id="root"></div>
<script src="undefined/bundle.js" charset="utf-8"></script>
<script src="client/coral-admin/bundle.js" charset="utf-8"></script>
</body>
</html>
+6 -14
View File
@@ -1,11 +1,9 @@
import React from 'react'
import { Provider } from 'react-redux'
import { Layout } from 'react-mdl'
import 'material-design-lite'
import { Router, Route, browserHistory } from 'react-router'
import ModerationQueue from 'containers/ModerationQueue'
import Header from 'components/Header'
import store from 'services/store'
import CommentStream from 'containers/CommentStream'
import EmbedLink from 'components/EmbedLink'
@@ -16,18 +14,12 @@ export default class App extends React.Component {
render (props) {
return (
<Provider store={store}>
<Layout>
<Header>
<div>
<Router history={browserHistory}>
<Route path={config.base} component={ModerationQueue} />
<Route path={`${config.base}/embed`} component={CommentStream} />
<Route path={`${config.base}/embedlink`} component={EmbedLink} />
<Route path={`${config.base}/configure`} component={Configure} />
</Router>
</div>
</Header>
</Layout>
<Router history={browserHistory}>
<Route path={config.basePath} component={ModerationQueue} />
<Route path={`${config.basePath}/embed`} component={CommentStream} />
<Route path={`${config.basePath}/embedlink`} component={EmbedLink} />
<Route path={`${config.basePath}/configure`} component={Configure} />
</Router>
</Provider>
)
}
@@ -120,7 +120,7 @@ export default class CommentList extends React.Component {
{commentIds.map((commentId, index) => (
<Comment comment={comments.get(commentId)}
ref={el => { if (el && commentId === active) { this._active = el } }}
key={index}
key={index + 'comment'}
index={index}
onClickAction={this.onClickAction}
actions={this.props.actions}
+4 -4
View File
@@ -9,14 +9,14 @@ export default (props) => (
<Layout fixedDrawer>
<Header title='Talk'>
<Navigation>
<Link className={styles.navLink} href={`/${config.base}/`}>Moderate</Link>
<Link className={styles.navLink} href={`/${config.base}/configure`}>Configure</Link>
<Link className={styles.navLink} to={`/${config.basePath}/`}>Moderate</Link>
<Link className={styles.navLink} to={`/${config.basePath}/configure`}>Configure</Link>
</Navigation>
</Header>
<Drawer>
<Navigation>
<Link className={styles.navLink} href={`/${config.base}/`}>Moderate</Link>
<Link className={styles.navLink} href={`/${config.base}/configure`}>Configure</Link>
<Link className={styles.navLink} to={`/${config.basePath}/`}>Moderate</Link>
<Link className={styles.navLink} to={`/${config.basePath}/configure`}>Configure</Link>
</Navigation>
</Drawer>
{props.children}
@@ -28,8 +28,8 @@ export default ({ open, onClose }) => (
<Modal open={open} onClose={onClose}>
<h3>{lang.t('modqueue.shortcuts')}</h3>
<div className={styles.container}>
{shortcuts.map(shortcut => (
<table className={styles.table}>
{shortcuts.map((shortcut, i) => (
<table className={styles.table} key={i}>
<thead>
<tr>
<th>{lang.t(shortcut.title)}</th>
@@ -37,7 +37,7 @@ export default ({ open, onClose }) => (
</thead>
<tbody>
{Object.keys(shortcut.shortcuts).map(key => (
<tr>
<tr key={key + 'tr'}>
<td className={styles.shortcut}><span className={styles.key}>{key}</span></td>
<td>{lang.t(shortcut.shortcuts[key])}</td>
</tr>
+12
View File
@@ -0,0 +1,12 @@
import React from 'react'
import {Layout} from 'react-mdl'
import 'material-design-lite'
import Header from 'components/Header'
export default (props) => (
<Layout>
<Header>
{props.children}
</Header>
</Layout>
)
@@ -6,6 +6,7 @@ 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'
/**
* Renders a comment stream using a CommentList component
@@ -43,17 +44,19 @@ class CommentStream extends React.Component {
// Render the comment box along with the CommentList
render ({ comments }, { snackbar, snackbarMsg }) {
return (
<div className={styles.container}>
<CommentBox onSubmit={this.onSubmit} />
<CommentList isActive hideActive
singleView={false}
commentIds={comments.get('ids')}
comments={comments.get('byId')}
onClickAction={this.onClickAction}
actions={['flag']}
loading={comments.loading} />
<Snackbar active={snackbar}>{snackbarMsg}</Snackbar>
</div>
<Page>
<div className={styles.container}>
<CommentBox onSubmit={this.onSubmit} />
<CommentList isActive hideActive
singleView={false}
commentIds={comments.get('ids')}
comments={comments.get('byId')}
onClickAction={this.onClickAction}
actions={['flag']}
loading={comments.loading} />
<Snackbar active={snackbar}>{snackbarMsg}</Snackbar>
</div>
</Page>
)
}
}
@@ -23,7 +23,10 @@
}
.configSettingEmbed {
composes: configSetting;
border: 1px solid #ccc;
border-radius: 4px;
height: 90px;
margin-bottom: 10px;
display: block;
height: 170px;
}
+29 -26
View File
@@ -10,6 +10,7 @@ import {
Button,
Icon
} from 'react-mdl'
import Page from 'components/Page'
import styles from './Configure.css'
class Configure extends React.Component {
@@ -59,33 +60,35 @@ class Configure extends React.Component {
: 'Embed Comment Stream'
return (
<div className={styles.container}>
<div className={styles.leftColumn}>
<List>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'comments')}
icon='settings'>Comment Settings</ListItemContent>
</ListItem>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'embed')}
icon='code'>Embed Comment Stream</ListItemContent>
</ListItem>
</List>
<Button raised colored>
<Icon name='save' /> Save Changes
</Button>
<Page>
<div className={styles.container}>
<div className={styles.leftColumn}>
<List>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'comments')}
icon='settings'>Comment Settings</ListItemContent>
</ListItem>
<ListItem className={styles.settingOption}>
<ListItemContent
onClick={this.changeSection.bind(this, 'embed')}
icon='code'>Embed Comment Stream</ListItemContent>
</ListItem>
</List>
<Button raised colored>
<Icon name='save' /> Save Changes
</Button>
</div>
<div className={styles.mainContent}>
<h1>{pageTitle}</h1>
{
this.state.activeSection === 'comments'
? this.getCommentSettings()
: this.getEmbed()
}
</div>
</div>
<div className={styles.mainContent}>
<h1>{pageTitle}</h1>
{
this.state.activeSection === 'comments'
? this.getCommentSettings()
: this.getEmbed()
}
</div>
</div>
</Page>
)
}
}
@@ -2,6 +2,7 @@
import React from 'react'
import { connect } from 'react-redux'
import ModerationKeysModal from 'components/ModerationKeysModal'
import Page from 'components/Page'
import CommentList from 'components/CommentList'
import { updateStatus } from 'actions/comments'
import styles from './ModerationQueue.css'
@@ -59,53 +60,56 @@ class ModerationQueue extends React.Component {
render () {
const { comments } = this.props
const { activeTab, singleView, modalOpen } = this.state
console.log('moderation queue', styles)
return (
<div className='mdl-tabs mdl-js-tabs mdl-js-ripple-effect'>
<div className='mdl-tabs__tab-bar'>
<a href='#pending' onClick={() => this.onTabClick('pending')}
className={`mdl-tabs__tab is-active ${styles.tab}`}>{lang.t('modqueue.pending')}</a>
<a href='#rejected' onClick={() => this.onTabClick('rejected')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.rejected')}</a>
<a href='#flagged' onClick={() => this.onTabClick('flagged')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.flagged')}</a>
<Page>
<div className='mdl-tabs mdl-js-tabs mdl-js-ripple-effect'>
<div className='mdl-tabs__tab-bar'>
<a href='#pending' onClick={() => this.onTabClick('pending')}
className={`mdl-tabs__tab is-active ${styles.tab}`}>{lang.t('modqueue.pending')}</a>
<a href='#rejected' onClick={() => this.onTabClick('rejected')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.rejected')}</a>
<a href='#flagged' onClick={() => this.onTabClick('flagged')}
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.flagged')}</a>
</div>
<div className={`mdl-tabs__panel is-active ${styles.listContainer}`} id='pending'>
<CommentList
isActive={activeTab === 'pending'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => !comments.get('byId').get(id).get('status'))}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='rejected'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => comments.get('byId').get(id).get('status') === 'rejected')}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='flagged'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => {
const data = comments.get('byId').get(id)
return !data.get('status') && data.get('flagged') === true
})}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<ModerationKeysModal open={modalOpen}
onClose={() => this.setState({ modalOpen: false })} />
</div>
<div className={`mdl-tabs__panel is-active ${styles.listContainer}`} id='pending'>
<CommentList
isActive={activeTab === 'pending'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => !comments.get('byId').get(id).get('status'))}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='rejected'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => comments.get('byId').get(id).get('status') === 'rejected')}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['approve']}
loading={comments.loading} />
</div>
<div className={`mdl-tabs__panel ${styles.listContainer}`} id='flagged'>
<CommentList
isActive={activeTab === 'rejected'}
singleView={singleView}
commentIds={comments.get('ids').filter(id => {
const data = comments.get('byId').get(id)
return !data.get('status') && data.get('flagged') === true
})}
comments={comments.get('byId')}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
</div>
<ModerationKeysModal open={modalOpen}
onClose={() => this.setState({ modalOpen: false })} />
</div>
</Page>
)
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from 'components/App'
import App from './components/App'
// Render the application into the DOM
ReactDOM.render(<App />, document.querySelector('#root'))
+8 -19
View File
@@ -1,7 +1,6 @@
const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const autoprefixer = require('autoprefixer')
const precss = require('precss')
const config = require('./config.json')
@@ -9,11 +8,9 @@ const config = require('./config.json')
// doing a string replace here because I spent a day trying to do it the "webpack" way
// ond nothing works. just trying to replace a string in an index.html file is
// apparently something no one has ever done in the js community.
let templateString = fs.readFileSync('./index.ejs').toString()
let templateString = fs.readFileSync(path.join(__dirname, 'index.ejs')).toString()
templateString = templateString.replace('<%= basePath %>', config.basePath)
fs.writeFileSync('./public/index.html', templateString)
console.log(templateString)
fs.writeFileSync(path.join(__dirname, 'public/index.html'), templateString)
module.exports = {
entry: {
@@ -25,24 +22,16 @@ module.exports = {
},
module: {
loaders: [
{ test: /.js$/, loaders: 'buble', include: path.join(__dirname, 'src') },
{ test: /.json$/, loaders: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader?modules&importLoaders=1!postcss-loader' }
{ test: /.js$/, loader: 'babel', include: path.join(__dirname, 'src'), exclude: /node_modules/ },
{ test: /\.json$/, loaders: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /.css$/, loaders: ['style-loader', 'css-loader?importLoaders=1', 'postcss-loader'] }
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
context: __dirname,
postcss: [autoprefixer, precss]
}
})
],
plugins: [ autoprefixer, precss ],
resolve: {
modules: [
root: [
path.resolve('./src'),
path.resolve('../'),
'node_modules'
path.resolve('../')
]
},
devServer: {
+7 -15
View File
@@ -4,22 +4,22 @@ const devConfig = require('./webpack.config.dev')
const autoprefixer = require('autoprefixer')
const precss = require('precss')
const Copy = require('copy-webpack-plugin')
const webpack = require('webpack')
const config = require('./config.json')
// doing a string replace here because I spent a day trying to do it the "webpack" way
// ond nothing works. just trying to replace a string in an index.html file is
// apparently something no one has ever done in the js community.
let templateString = fs.readFileSync('./index.ejs').toString()
let templateString = fs.readFileSync(path.join(__dirname, 'index.ejs')).toString()
templateString = templateString.replace('<%= basePath %>', config.basePath)
fs.writeFileSync('./public/index.html', templateString)
fs.writeFileSync(path.join(__dirname, 'public/index.html'), templateString)
module.exports = Object.assign({}, devConfig, {
module: {
context: __dirname,
loaders: [
{ test: /.js$/, loaders: 'buble', include: [path.join(__dirname, 'src'), path.join(__dirname, '../', 'coral-framework')] },
{ test: /.json$/, loaders: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader?modules&importLoaders=1!postcss-loader' }
{ test: /.js$/, loader: 'babel', include: [path.join(__dirname, 'src'), path.join(__dirname, '../', 'coral-framework')], exclude: /node_modules/ },
{ test: /.json$/, loader: 'json', include: __dirname, exclude: /node_modules/ },
{ test: /.css$/, loaders: ['style-loader', 'css-loader?importLoaders=1', 'postcss-loader'] }
]
},
plugins: [
@@ -27,14 +27,6 @@ module.exports = Object.assign({}, devConfig, {
from: path.join(__dirname, '..', 'coral-embed-stream', 'dist'),
to: './embed/comment-stream'
}]),
new webpack.LoaderOptionsPlugin({
options: {
context: __dirname,
postcss: [autoprefixer, precss],
minimize: true,
debug: false
}
}),
new webpack.optimize.UglifyJsPlugin()
autoprefixer, precss
]
})
@@ -54,8 +54,8 @@ const {setLoggedInUser} = authActions
postAction: (item, action, user) => {
return dispatch(postAction(item, action, user))
},
appendItemArray: (item, property, value) => {
return dispatch(appendItemArray(item, property, value))
appendItemArray: (item, property, value, addToFront) => {
return dispatch(appendItemArray(item, property, value, addToFront))
}
}
}
@@ -121,7 +121,7 @@ class CommentStream extends Component {
<hr aria-hidden={true}/>
<AuthorName name={comment.username}/>
<PubDate created_at={comment.created_at}/>
<Content content={comment.body}/>
<Content body={comment.body}/>
<div className="commentActions">
{
// <Flag
@@ -151,7 +151,7 @@ class CommentStream extends Component {
<hr aria-hidden={true}/>
<AuthorName name={reply.username}/>
<PubDate created_at={reply.created_at}/>
<Content content={reply.body}/>
<Content body={reply.body}/>
<div className="replyActions">
{
// <Flag
@@ -122,4 +122,5 @@ hr {
.coral-plugin-pubdate-text {
color: #CCC;
display: inline-block;
}
@@ -56,12 +56,13 @@ export const updateItem = (id, property, value) => {
}
}
export const appendItemArray = (id, property, value) => {
export const appendItemArray = (id, property, value, addToFront) => {
return {
type: APPEND_ITEM_ARRAY,
id,
property,
value
value,
addToFront
}
}
@@ -115,7 +116,7 @@ export function getStream (assetId) {
const keys = Object.keys(childComments)
for (var i=0; i < keys.length; i++ ) {
dispatch(updateItem(keys[i], 'children', childComments[keys[i]]))
dispatch(updateItem(keys[i], 'children', childComments[keys[i]].reverse()))
}
return (json)
@@ -185,13 +186,13 @@ export function postItem (item, type, id) {
return fetch('/api/v1/' + type, options)
.then(
response => {
return response.ok ? response.text()
return response.ok ? response.json()
: Promise.reject(response.status + ' ' + response.statusText)
}
)
.then((id) => {
dispatch(addItem({...item, id}))
return id
.then((json) => {
dispatch(addItem({...item, id:json.id}))
return json.id
})
}
}
@@ -15,7 +15,12 @@ export default (state = initialState, action) => {
)
case actions.APPEND_ITEM_ARRAY:
return state.updateIn([action.id, action.property], (prop) => {
return prop ? prop.unshift(action.value) : fromJS([action.value])
if (action.addToFront) {
return prop ? prop.unshift(action.value) : fromJS([action.value])
} else {
return prop ? prop.push(action.value) : fromJS([action.value])
}
}
)
default:
+1 -1
View File
@@ -35,7 +35,7 @@ class CommentBox extends Component {
updateItem(parent_id, 'showReply', false)
postItem(comment, 'comments')
.then((comment_id) => {
appendItemArray(parent_id || id, related, comment_id)
appendItemArray(parent_id || id, related, comment_id, parent_id ? false : true)
addNotification('success', 'Your comment has been posted.')
}).catch((err) => console.error(err))
this.setState({body: ''})
@@ -1,10 +1,17 @@
import React from 'react'
const name = 'coral-plugin-replies'
const Content = (props) => <div
className={name + '-text'}
style={props.styles && props.styles.text}>
{props.content}
</div>
const Content = ({body, styles}) => {
const textbreaks = body.split('\n')
return <div
className={name + '-text'}
style={styles && styles.text}>
{
textbreaks.map((line, i) => <span key={i} className={name+'-line'}>
{line} <br className={name+'-linebreak'}/>
</span>)
}
</div>
}
export default Content
+1 -1
View File
@@ -5,7 +5,7 @@
"main": "app.js",
"scripts": {
"start": "./bin/www",
"build": "./node_modules/webpack/bin/webpack.js --config ./client/coral-embed-stream/webpack.config.js",
"build": "./node_modules/webpack/bin/webpack.js --config ./client/coral-embed-stream/webpack.config.js && cd client/coral-admin && npm run build",
"lint": "eslint .",
"pretest": "npm install",
"test": "mocha tests --recursive",