mirror of
https://github.com/wassname/talk.git
synced 2026-08-14 12:50:17 +08:00
Adding users, comments, and actions to endpoint and moving to store.
This commit is contained in:
@@ -12,26 +12,27 @@ const linkify = new Linkify();
|
||||
|
||||
// Render a single comment for the list
|
||||
export default props => {
|
||||
const links = linkify.getMatches(props.comment.get('body'));
|
||||
const {comment, author} = props;
|
||||
const links = linkify.getMatches(comment.get('body'));
|
||||
|
||||
return (
|
||||
<li tabIndex={props.index} className={`${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
|
||||
<div className={styles.itemHeader}>
|
||||
<div className={styles.author}>
|
||||
<i className={`material-icons ${styles.avatar}`}>person</i>
|
||||
<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}
|
||||
<span>{author.get('displayName') || lang.t('comment.anon')}</span>
|
||||
<span className={styles.created}>{timeago().format(comment.get('createdAt') || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}</span>
|
||||
{comment.get('flagged') ? <p className={styles.flagged}>{lang.t('comment.flagged')}</p> : null}
|
||||
</div>
|
||||
<div>
|
||||
{links ?
|
||||
<span className={styles.hasLinks}><Icon name='error_outline'/> Contains Link</span> : null}
|
||||
<div className={styles.actions}>
|
||||
{props.actions.map((action, i) => canShowAction(action, props.comment) ? (
|
||||
{props.actions.map((action, i) => canShowAction(action, comment) ? (
|
||||
<FabButton icon={props.actionsMap[action].icon} className={styles.actionButton}
|
||||
cStyle={action}
|
||||
key={i}
|
||||
onClick={() => props.onClickAction(props.actionsMap[action].status, props.comment.get('id'))}
|
||||
onClick={() => props.onClickAction(props.actionsMap[action].status, comment.get('id'))}
|
||||
/>
|
||||
) : null)}
|
||||
</div>
|
||||
@@ -40,7 +41,7 @@ export default props => {
|
||||
<div className={styles.itemBody}>
|
||||
<span className={styles.body}>
|
||||
<Linkify component='span' properties={{style: linkStyles}}>
|
||||
{props.comment.get('body')}
|
||||
{comment.get('body')}
|
||||
</Linkify>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -112,13 +112,15 @@ export default class CommentList extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {singleView, commentIds, comments, hideActive, key} = this.props;
|
||||
const {singleView, commentIds, comments, users, hideActive, key} = this.props;
|
||||
const {active} = this.state;
|
||||
|
||||
return (
|
||||
<ul className={`${styles.list} ${singleView ? styles.singleView : ''}`} {...key}>
|
||||
{commentIds.map((commentId, index) => (
|
||||
<Comment comment={comments.get(commentId)}
|
||||
{commentIds.map((commentId, index) => {
|
||||
const comment = comments.get(commentId);
|
||||
return <Comment comment={comment}
|
||||
author={users.get(comment.get('author_id'))}
|
||||
ref={el => { if (el && commentId === active) { this._active = el; } }}
|
||||
key={index}
|
||||
index={index}
|
||||
@@ -126,8 +128,8 @@ export default class CommentList extends React.Component {
|
||||
actions={this.props.actions}
|
||||
actionsMap={actions}
|
||||
isActive={commentId === active}
|
||||
hideActive={hideActive} />
|
||||
)).toArray()}
|
||||
hideActive={hideActive} />;
|
||||
}).toArray()}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class ModerationQueue extends React.Component {
|
||||
|
||||
// Render the tabbed lists moderation queues
|
||||
render () {
|
||||
const {comments} = this.props;
|
||||
const {comments, users} = this.props;
|
||||
const {activeTab, singleView, modalOpen} = this.state;
|
||||
|
||||
return (
|
||||
@@ -86,6 +86,7 @@ class ModerationQueue extends React.Component {
|
||||
.get('status'))
|
||||
}
|
||||
comments={comments.get('byId')}
|
||||
users={users.get('byId')}
|
||||
onClickAction={(action, id) => this.onCommentAction(action, id)}
|
||||
actions={['reject', 'approve']}
|
||||
loading={comments.loading} />
|
||||
@@ -104,6 +105,7 @@ class ModerationQueue extends React.Component {
|
||||
.get('status') === 'rejected')
|
||||
}
|
||||
comments={comments.get('byId')}
|
||||
users={users.get('byId')}
|
||||
onClickAction={(action, id) => this.onCommentAction(action, id)}
|
||||
actions={['approve']}
|
||||
loading={comments.loading} />
|
||||
@@ -117,6 +119,7 @@ class ModerationQueue extends React.Component {
|
||||
return !data.get('status') && data.get('flagged') === true;
|
||||
})}
|
||||
comments={comments.get('byId')}
|
||||
users={users.get('byId')}
|
||||
onClickAction={(action, id) => this.onCommentAction(action, id)}
|
||||
actions={['reject', 'approve']}
|
||||
loading={comments.loading} />
|
||||
@@ -129,6 +132,6 @@ class ModerationQueue extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(({comments}) => ({comments}))(ModerationQueue);
|
||||
export default connect(({comments, users}) => ({comments, users}))(ModerationQueue);
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
@@ -2,6 +2,7 @@ import {combineReducers} from 'redux';
|
||||
import comments from 'reducers/comments';
|
||||
import settings from 'reducers/settings';
|
||||
import community from 'reducers/community';
|
||||
import users from 'reducers/users';
|
||||
import auth from 'reducers/auth';
|
||||
|
||||
// Combine all reducers into a main one
|
||||
@@ -9,6 +10,6 @@ export default combineReducers({
|
||||
settings,
|
||||
comments,
|
||||
community,
|
||||
auth
|
||||
auth,
|
||||
users
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import {Map, List, fromJS} from 'immutable';
|
||||
|
||||
const initialState = Map({
|
||||
byId: Map(),
|
||||
ids: List()
|
||||
});
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case 'USERS_MODERATION_QUEUE_FETCH_SUCCESS': return replaceUsers(action, state);
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
||||
// Replace the comment list with a new one
|
||||
const replaceUsers = (action, state) => {
|
||||
const users = fromJS(action.users.reduce((prev, curr) => { prev[curr.id] = curr; return prev; }, {}));
|
||||
return state.set('byId', users)
|
||||
.set('ids', List(users.keys()));
|
||||
};
|
||||
@@ -44,8 +44,12 @@ Promise.all([
|
||||
res[2] = res[2].map(comment => { comment.flagged = true; return comment; });
|
||||
return res.reduce((prev, curr) => prev.concat(curr), []);
|
||||
})
|
||||
.then(res => store.dispatch({type: 'COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS',
|
||||
comments: res}))
|
||||
.then(res => {
|
||||
store.dispatch({type: 'COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS',
|
||||
comments: res.comments});
|
||||
store.dispatch({type: 'USERS_MODERATION_QUEUE_FETCH_SUCCESS',
|
||||
users: res.users});
|
||||
})
|
||||
.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
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const express = require('express');
|
||||
const Comment = require('../../../models/comment');
|
||||
const User = require('../../../models/user');
|
||||
const Action = require('../../../models/action');
|
||||
const Setting = require('../../../models/setting');
|
||||
const _ = require('lodash');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -13,11 +16,24 @@ const router = express.Router();
|
||||
// Pre-moderation: New comments are shown in the moderator queues immediately.
|
||||
// Post-moderation: New comments do not appear in moderation queues unless they are flagged by other users.
|
||||
router.get('/comments/pending', (req, res, next) => {
|
||||
Setting.getModerationSetting().then(function({moderation}){
|
||||
Comment.moderationQueue(moderation).then((comments) => {
|
||||
res.status(200).json(comments);
|
||||
});
|
||||
Setting.getModerationSetting().then(({moderation}) =>
|
||||
Comment.moderationQueue(moderation))
|
||||
.then((comments) => {
|
||||
return Promise.all([
|
||||
comments,
|
||||
User.findByIdArray(_.uniq(comments.map((comment) => comment.author_id))),
|
||||
Action.getActionSummaries(_.uniq([
|
||||
...comments.map((comment) => comment.id),
|
||||
...comments.map((comment) => comment.author_id)
|
||||
]))
|
||||
]);
|
||||
})
|
||||
.then(([comments, users, actions])=>
|
||||
res.status(200).json({
|
||||
comments,
|
||||
users,
|
||||
actions
|
||||
}))
|
||||
.catch(error => {
|
||||
next(error);
|
||||
});
|
||||
|
||||
@@ -20,17 +20,15 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
describe('Get moderation queues rejected, pending, flags', () => {
|
||||
const comments = [{
|
||||
let comments = [{
|
||||
id: 'abc',
|
||||
body: 'comment 10',
|
||||
asset_id: 'asset',
|
||||
author_id: '123',
|
||||
status: 'rejected'
|
||||
}, {
|
||||
id: 'def',
|
||||
body: 'comment 20',
|
||||
asset_id: 'asset',
|
||||
author_id: '456'
|
||||
asset_id: 'asset'
|
||||
}, {
|
||||
id: 'hij',
|
||||
body: 'comment 30',
|
||||
@@ -39,41 +37,52 @@ describe('Get moderation queues rejected, pending, flags', () => {
|
||||
}];
|
||||
|
||||
const users = [{
|
||||
id: '456',
|
||||
displayName: 'Ana',
|
||||
email: 'ana@gmail.com',
|
||||
password: '123'
|
||||
}, {
|
||||
id: '123',
|
||||
displayName: 'Maria',
|
||||
email: 'maria@gmail.com',
|
||||
password: '123'
|
||||
}];
|
||||
|
||||
const actions = [{
|
||||
let actions = [{
|
||||
action_type: 'flag',
|
||||
item_id: 'abc',
|
||||
item_type: 'comment'
|
||||
}, {
|
||||
action_type: 'like',
|
||||
item_id: 'hij',
|
||||
item_type: 'comment'
|
||||
}];
|
||||
|
||||
beforeEach(() => {
|
||||
return Promise.all([
|
||||
Comment.create(comments),
|
||||
User.createLocalUsers(users),
|
||||
Action.create(actions)
|
||||
]);
|
||||
return User.createLocalUsers(users)
|
||||
.then((u) => {
|
||||
comments[0].author_id = u[0].id;
|
||||
comments[1].author_id = u[1].id;
|
||||
comments[2].author_id = u[1].id;
|
||||
|
||||
return Comment.create(comments);
|
||||
})
|
||||
.then((c) => {
|
||||
actions[0].item_id = c[0].id;
|
||||
actions[1].item_id = c[1].id;
|
||||
|
||||
return Action.create(actions);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all the pending comments', function(done){
|
||||
it('should return all the pending comments, users and actions', function(done){
|
||||
chai.request(app)
|
||||
.get('/api/v1/queue/comments/pending')
|
||||
.set(passport.inject({roles: ['admin']}))
|
||||
.end(function(err, res){
|
||||
expect(err).to.be.null;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.body[0]).to.have.property('id', 'def');
|
||||
expect(res.body.comments[0]).to.have.property('body');
|
||||
expect(res.body.users[0]).to.have.property('displayName');
|
||||
expect(res.body.actions[0]).to.have.property('action_type');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user