mirror of
https://github.com/wassname/talk.git
synced 2026-07-12 07:47:22 +08:00
fix(coral-admin): Adapt to the new schema. Most things are not working yet but at least it bundles
This commit is contained in:
@@ -4,14 +4,13 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "./node_modules/.bin/webpack --config webpack.config.js",
|
||||
"build": "./node_modules/.bin/webpack --config webpack.config.js --watch",
|
||||
"start": "./node_modules/.bin/webpack-dev-server --config webpack.config.dev.js --inline --hot --content-base public --port 3142"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"coral-framework": "0.0.1",
|
||||
"hammerjs": "2.0.8",
|
||||
"immutable": "3.8.1",
|
||||
"keymaster": "1.6.2",
|
||||
@@ -23,8 +22,7 @@
|
||||
"react-router": "^3.0.0",
|
||||
"redux": "3.6.0",
|
||||
"redux-thunk": "2.1.0",
|
||||
"timeago.js": "2.0.2",
|
||||
"xenia-driver": "0.0.4"
|
||||
"timeago.js": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "6.5.0",
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="http://localhost:3142/bundle.js" charset="utf-8"></script>
|
||||
<script src="undefined/bundle.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"short_name": "Talk",
|
||||
"name": "Talk",
|
||||
"icons": [
|
||||
{
|
||||
"src": "https://coralproject.net/images/icon-coral-white.svg",
|
||||
"sizes": "150x150"
|
||||
}
|
||||
],
|
||||
"start_url": "./",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import React from 'react'
|
||||
import { Provider } from 'react-redux'
|
||||
import { Layout, Content } from 'react-mdl'
|
||||
import { Layout } from 'react-mdl'
|
||||
import 'material-design-lite'
|
||||
import { Router, Route, browserHistory } from 'react-router'
|
||||
import ModerationQueue from 'containers/ModerationQueue'
|
||||
@@ -10,6 +10,7 @@ import store from 'services/store'
|
||||
import CommentStream from 'containers/CommentStream'
|
||||
import EmbedLink from 'components/EmbedLink'
|
||||
import Configure from 'containers/Configure'
|
||||
import config from 'services/config'
|
||||
|
||||
export default class App extends React.Component {
|
||||
render (props) {
|
||||
@@ -19,10 +20,10 @@ export default class App extends React.Component {
|
||||
<Header>
|
||||
<div>
|
||||
<Router history={browserHistory}>
|
||||
<Route path='/' component={ModerationQueue} />
|
||||
<Route path='embed' component={CommentStream} />
|
||||
<Route path='embedlink' compoent={EmbedLink} />
|
||||
<Route path='configure' component={Configure} />
|
||||
<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>
|
||||
|
||||
@@ -12,14 +12,14 @@ export default props => (
|
||||
<div className={styles.itemHeader}>
|
||||
<div className={styles.author}>
|
||||
<i className={`material-icons ${styles.avatar}`}>person</i>
|
||||
<span>{props.comment.get('data').get('name') || lang.t('comment.anon')}</span>
|
||||
<span className={styles.created}>{timeago().format(props.comment.get('data').get('createdAt') || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}</span>
|
||||
{props.comment.get('data').get('flagged') ? <p className={styles.flagged}>{lang.t('comment.flagged')}</p> : null}
|
||||
<span>{props.comment.get('name') || lang.t('comment.anon')}</span>
|
||||
<span className={styles.created}>{timeago().format(props.comment.get('createdAt') || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}</span>
|
||||
{props.comment.get('flagged') ? <p className={styles.flagged}>{lang.t('comment.flagged')}</p> : null}
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
{props.actions.map(action => canShowAction(action, props.comment) ? (
|
||||
<Button className={styles.actionButton}
|
||||
onClick={() => props.onClickAction(props.actionsMap[action].status, props.comment.get('item_id'))}
|
||||
onClick={() => props.onClickAction(props.actionsMap[action].status, props.comment.get('_id'))}
|
||||
fab colored>
|
||||
<Icon name={props.actionsMap[action].icon} />
|
||||
</Button>
|
||||
@@ -27,16 +27,17 @@ export default props => (
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.itemBody}>
|
||||
<span className={styles.body}>{props.comment.get('data').get('body')}</span>
|
||||
<span className={styles.body}>{props.comment.get('body')}</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
|
||||
// Check if an action can be performed over a comment
|
||||
const canShowAction = (action, comment) => {
|
||||
const status = comment.get('data').get('status')
|
||||
const flagged = comment.get('data').get('flagged')
|
||||
if (action === 'flag' && (status !== 'Untouched' || flagged === true)) {
|
||||
const status = comment.get('status')
|
||||
const flagged = comment.get('flagged')
|
||||
|
||||
if (action === 'flag' && (status || flagged === true)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -112,8 +112,9 @@ export default class CommentList extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {singleView, actions, commentIds, comments, hideActive} = this.props
|
||||
const {singleView, commentIds, comments, hideActive} = this.props
|
||||
const {active} = this.state
|
||||
|
||||
return (
|
||||
<ul className={`${styles.list} ${singleView ? styles.singleView : ''}`}>
|
||||
{commentIds.map((commentId, index) => (
|
||||
@@ -122,7 +123,7 @@ export default class CommentList extends React.Component {
|
||||
key={index}
|
||||
index={index}
|
||||
onClickAction={this.onClickAction}
|
||||
actions={actions}
|
||||
actions={this.props.actions}
|
||||
actionsMap={actions}
|
||||
isActive={commentId === active}
|
||||
hideActive={hideActive} />
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
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'
|
||||
|
||||
// App header. If we add a navbar it should be here
|
||||
export default (props) => (
|
||||
<Layout fixedDrawer>
|
||||
<Header title='Talk'>
|
||||
<Navigation>
|
||||
<a className={styles.navLink} href='/'>Moderate</a>
|
||||
<a className={styles.navLink} href='/configure'>Configure</a>
|
||||
<Link className={styles.navLink} href={`/${config.base}/`}>Moderate</Link>
|
||||
<Link className={styles.navLink} href={`/${config.base}/configure`}>Configure</Link>
|
||||
</Navigation>
|
||||
</Header>
|
||||
<Drawer>
|
||||
<Navigation>
|
||||
<a className={styles.navLink} href='/'>Moderate</a>
|
||||
<a className={styles.navLink} href='/configure'>Configure</a>
|
||||
<Link className={styles.navLink} href={`/${config.base}/`}>Moderate</Link>
|
||||
<Link className={styles.navLink} href={`/${config.base}/configure`}>Configure</Link>
|
||||
</Navigation>
|
||||
</Drawer>
|
||||
{props.children}
|
||||
|
||||
@@ -74,7 +74,7 @@ class ModerationQueue extends React.Component {
|
||||
<CommentList
|
||||
isActive={activeTab === 'pending'}
|
||||
singleView={singleView}
|
||||
commentIds={comments.get('ids').filter(id => comments.get('byId').get(id).get('data').get('status') === 'Untouched')}
|
||||
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']}
|
||||
@@ -84,7 +84,7 @@ class ModerationQueue extends React.Component {
|
||||
<CommentList
|
||||
isActive={activeTab === 'rejected'}
|
||||
singleView={singleView}
|
||||
commentIds={comments.get('ids').filter(id => comments.get('byId').get(id).get('data').get('status') === 'Rejected')}
|
||||
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']}
|
||||
@@ -95,8 +95,8 @@ class ModerationQueue extends React.Component {
|
||||
isActive={activeTab === 'rejected'}
|
||||
singleView={singleView}
|
||||
commentIds={comments.get('ids').filter(id => {
|
||||
const data = comments.get('byId').get(id).get('data')
|
||||
return data.get('status') === 'Untouched' && data.get('flagged') === true
|
||||
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)}
|
||||
|
||||
@@ -46,7 +46,7 @@ const flag = (state, action) => {
|
||||
|
||||
// Replace the comment list with a new one
|
||||
const replaceComments = (action, state) => {
|
||||
const comments = fromJS(action.comments.reduce((prev, curr) => { prev[curr.item_id] = curr; return prev }, {}))
|
||||
const comments = fromJS(action.comments.reduce((prev, curr) => { prev[curr._id] = curr; return prev }, {}))
|
||||
return state.set('byId', comments).set('loading', false)
|
||||
.set('ids', List(comments.keys()))
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
* for the coral but also for wordpress comments, disqus and many more.
|
||||
*/
|
||||
|
||||
import { talkHost, xeniaHost } from 'services/config'
|
||||
import XeniaDriver from 'xenia-driver'
|
||||
|
||||
// Intercept redux actions and act over the ones we are interested
|
||||
export default store => next => action => {
|
||||
switch (action.type) {
|
||||
@@ -30,64 +27,34 @@ export default store => next => action => {
|
||||
next(action)
|
||||
}
|
||||
|
||||
// Setup xenia driver
|
||||
const xenia = XeniaDriver(`${xeniaHost}/v1`, {username: 'user', password: 'pass'})
|
||||
|
||||
// Get comments to fill each of the three lists on the mod queue
|
||||
const fetchModerationQueueComments = store => xenia()
|
||||
.collection('items')
|
||||
.match({type: 'comment', 'data.status': 'Untouched', 'data.createdAt': { $exists: true }})
|
||||
.sort(['data.createdAt', 1])
|
||||
.skip(0).limit(50)
|
||||
.addQuery().collection('items')
|
||||
.match({type: 'comment', 'data.status': 'Rejected', 'data.createdAt': { $exists: true }})
|
||||
.sort(['data.createdAt', 1])
|
||||
.skip(0).limit(50)
|
||||
.addQuery().collection('items')
|
||||
.match({type: 'comment', 'data.status': 'Untouched', 'data.flagged': true, 'data.createdAt': { $exists: true }})
|
||||
.sort(['data.createdAt', 1])
|
||||
.skip(0).limit(50)
|
||||
.exec()
|
||||
const fetchModerationQueueComments = store =>
|
||||
fetch(`/api/v1/queue`)
|
||||
.then(res => res.json())
|
||||
.then(res => store.dispatch({ type: 'COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS',
|
||||
comments: res.results.map(res => res.Docs).reduce((p, c) => p.concat(c), []) }))
|
||||
comments: res }))
|
||||
.catch(error => store.dispatch({ type: 'COMMENTS_MODERATION_QUEUE_FETCH_FAILED', error }))
|
||||
|
||||
// Update a comment. Now to update a comment we need to send back the whole object
|
||||
const updateComment = (store, comment) =>
|
||||
fetch(`${talkHost}/v1/item`, {
|
||||
method: 'PUT',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(comment)
|
||||
fetch(`/api/v1/comments/${comment._id}/status`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ status: comment.status })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => store.dispatch({ type: 'COMMENT_UPDATE_SUCCESS', res }))
|
||||
.catch(error => store.dispatch({ type: 'COMMENT_UPDATE_FAILED', error }))
|
||||
|
||||
// Create a new comment
|
||||
const createComment = (store, name, comment) =>
|
||||
fetch(`${talkHost}/v1/item`, {
|
||||
fetch(`/api/v1/comments`, {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify({
|
||||
type: 'comment',
|
||||
version: 1,
|
||||
data: {
|
||||
status: 'Untouched',
|
||||
body: comment,
|
||||
name: name,
|
||||
createdAt: Date.now()
|
||||
}
|
||||
status: 'Untouched',
|
||||
body: comment,
|
||||
name: name,
|
||||
createdAt: Date.now()
|
||||
})
|
||||
}).then(res => res.json())
|
||||
.then(res => store.dispatch({ type: 'COMMENT_CREATE_SUCCESS', comment: res }))
|
||||
.catch(error => store.dispatch({ type: 'COMMENT_CREATE_FAILED', error }))
|
||||
|
||||
// Get a comment stream. Now we don't have the concept of assets, this should
|
||||
// be adapted to retrieve the current asset when the backend supports it
|
||||
const fetchCommentStream = store => xenia()
|
||||
.collection('items')
|
||||
.match({type: 'comment', 'data.status': { $ne: 'Rejected' }, 'data.createdAt': { $exists: true }})
|
||||
.sort(['data.createdAt', 1])
|
||||
.skip(0).limit(100)
|
||||
.exec()
|
||||
.then(res => store.dispatch({ type: 'COMMENT_STREAM_FETCH_SUCCESS', comments: res.results[0].Docs }))
|
||||
.catch(error => store.dispatch({ type: 'COMMENT_STREAM_FETCH_FAILED', error }))
|
||||
|
||||
@@ -20,7 +20,7 @@ module.exports = {
|
||||
'bundle': path.join(__dirname, 'src', 'index')
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'public'),
|
||||
path: path.join(__dirname, '..', '..', 'dist', 'coral-admin'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
@@ -41,6 +41,7 @@ module.exports = {
|
||||
resolve: {
|
||||
modules: [
|
||||
path.resolve('./src'),
|
||||
path.resolve('../'),
|
||||
'node_modules'
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user