diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index b28c4582b..bd72bbb8c 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -4,7 +4,6 @@ import {Router, Route, IndexRoute, IndexRedirect, browserHistory} from 'react-ro import Streams from 'containers/Streams/Streams'; import Configure from 'containers/Configure/Configure'; import LayoutContainer from 'containers/LayoutContainer'; -import CommentStream from 'containers/CommentStream/CommentStream'; import InstallContainer from 'containers/Install/InstallContainer'; import CommunityContainer from 'containers/Community/CommunityContainer'; @@ -16,7 +15,6 @@ const routes = ( - diff --git a/client/coral-admin/src/actions/comments.js b/client/coral-admin/src/actions/comments.js deleted file mode 100644 index 14f33bf36..000000000 --- a/client/coral-admin/src/actions/comments.js +++ /dev/null @@ -1,103 +0,0 @@ -import coralApi from '../../../coral-framework/helpers/response'; -import * as commentTypes from '../constants/comments'; -import * as actionTypes from '../constants/actions'; - -function addUsersCommentsActions (dispatch, {comments, users, actions}) { - dispatch({type: commentTypes.USERS_MODERATION_QUEUE_FETCH_SUCCESS, users}); - dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS, comments}); - dispatch({type: actionTypes.ACTIONS_MODERATION_QUEUE_FETCH_SUCCESS, actions}); -} - -// Get comments to fill each of the three lists on the mod queue -export const fetchModerationQueueComments = () => { - return dispatch => { - dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST}); - - return Promise.all([ - coralApi('/queue/comments/premod'), - coralApi('/queue/users/flagged'), - coralApi('/queue/comments/rejected'), - coralApi('/queue/comments/flagged') - ]) - .then(([premodComments, pendingUsers, rejected, flagged]) => { - - /* Combine seperate calls into a single object */ - flagged.comments.forEach(comment => comment.flagged = true); - return { - comments: [...premodComments.comments, ...rejected.comments, ...flagged.comments], - users: [...premodComments.users, ...pendingUsers.users, ...rejected.users, ...flagged.users], - actions: [...premodComments.actions, ...pendingUsers.actions, ...rejected.actions, ...flagged.actions] - }; - }) - .then(addUsersCommentsActions.bind(this, dispatch)); - }; -}; - -export const fetchPremodQueue = () => { - return dispatch => { - dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST}); - - return coralApi('/queue/comments/premod') - .then(addUsersCommentsActions.bind(this, dispatch)); - }; -}; - -export const fetchPendingUsersQueue = () => { - return dispatch => { - dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST}); - - return coralApi('/queue/users/flagged') - .then(addUsersCommentsActions.bind(this, dispatch)); - }; -}; - -export const fetchRejectedQueue = () => { - return dispatch => { - dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST}); - - return coralApi('/queue/comments/rejected') - .then(addUsersCommentsActions.bind(this, dispatch)); - }; -}; - -export const fetchFlaggedQueue = () => { - return dispatch => { - dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST}); - - return coralApi('/queue/comments/flagged') - .then(results => { - results.comments.forEach(comment => comment.flagged = true); - return results; - }) - .then(addUsersCommentsActions.bind(this, dispatch)); - }; -}; - -// Create a new comment -export const createComment = (name, body) => { - return (dispatch) => { - const formData = {body, name}; - return coralApi('/comments', {method: 'POST', body: formData}) - .then(res => dispatch({type: commentTypes.COMMENT_CREATE_SUCCESS, comment: res})) - .catch(error => dispatch({type: commentTypes.COMMENT_CREATE_FAILED, error})); - }; -}; - -/** - * Action disptacher related to comments - */ - -// Update a comment. Now to update a comment we need to send back the whole object -export const updateStatus = (status, comment) => { - return dispatch => { - dispatch({type: commentTypes.COMMENT_STATUS_UPDATE_REQUEST, id: comment.id, status}); - return coralApi(`/comments/${comment.id}/status`, {method: 'PUT', body: {status}}) - .then(res => dispatch({type: commentTypes.COMMENT_STATUS_UPDATE_SUCCESS, res})) - .catch(error => dispatch({type: commentTypes.COMMENT_STATUS_UPDATE_FAILURE, error})); - }; -}; - -export const flagComment = id => (dispatch, getState) => { - dispatch({type: commentTypes.COMMENT_FLAG, id}); - dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)}); -}; diff --git a/client/coral-admin/src/containers/CommentStream/CommentStream.css b/client/coral-admin/src/containers/CommentStream/CommentStream.css deleted file mode 100644 index 9247183e5..000000000 --- a/client/coral-admin/src/containers/CommentStream/CommentStream.css +++ /dev/null @@ -1,13 +0,0 @@ - -@custom-media --big-viewport (min-width: 780px); - -.container { - max-width: 860px; - margin: 0 auto; -} - -@media (--big-viewport) { - .tab { - flex: none; - } -} diff --git a/client/coral-admin/src/containers/CommentStream/CommentStream.js b/client/coral-admin/src/containers/CommentStream/CommentStream.js deleted file mode 100644 index e2fac3702..000000000 --- a/client/coral-admin/src/containers/CommentStream/CommentStream.js +++ /dev/null @@ -1,66 +0,0 @@ -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 ModerationList from 'components/ModerationList'; -import CommentBox from 'components/CommentBox'; - -/** - * Renders a comment stream using a ModerationList component - * and adds a box for adding a new comment - */ - -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); - } - - // Fetch the comments before mounting - componentWillMount () { - this.props.dispatch({type: 'COMMENT_STREAM_FETCH'}); - } - - // Submit the new comment - onSubmit (comment) { - this.props.dispatch(createComment(comment.name, comment.body)); - } - - // The only action for now is flagging - onClickAction (action, id) { - if (action === 'flag') { - 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 ModerationList - render ({comments, users}, {snackbar, snackbarMsg}) { - return ( -
- - - {snackbarMsg} -
- ); - } -} - -const mapStateToProps = state => ({ - comments: state.comments.toJS(), - users: state.users.toJS() -}); - -export default connect(mapStateToProps)(CommentStream); diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js index 5497a25d4..d30f24c1f 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js @@ -9,12 +9,13 @@ const ModerationQueue = ({activeTab = 'premod', ...props}) => {
    { props.data[activeTab].map((comment, i) => { + const status = comment.action_summaries ? 'FLAGGED' : comment.status; return asset.comments.length} loadMore={this.props.loadMore}/> diff --git a/client/coral-embed-stream/src/LoadMore.js b/client/coral-embed-stream/src/LoadMore.js index ff56f6065..89708272a 100644 --- a/client/coral-embed-stream/src/LoadMore.js +++ b/client/coral-embed-stream/src/LoadMore.js @@ -1,11 +1,11 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from 'coral-framework/translations.json'; import {ADDTL_COMMENTS_ON_LOAD_MORE} from 'coral-framework/constants/comments'; import {Button} from 'coral-ui'; const lang = new I18n(translations); -const loadMoreComments = (id, comments, loadMore, parentId) => { +const loadMoreComments = (assetId, comments, loadMore, parentId) => { if (!comments.length) { return; @@ -18,20 +18,29 @@ const loadMoreComments = (id, comments, loadMore, parentId) => { loadMore({ limit: ADDTL_COMMENTS_ON_LOAD_MORE, cursor, - asset_id: id, + assetId, parent_id: parentId, sort: parentId ? 'CHRONOLOGICAL' : 'REVERSE_CHRONOLOGICAL' }); }; -const LoadMore = ({id, comments, loadMore, moreComments, parentId}) => moreComments ? - - : null; +const LoadMore = ({assetId, comments, loadMore, moreComments, parentId}) => ( + moreComments + ? + : null +); + +LoadMore.propTypes = { + assetId: PropTypes.string.isRequired, + comments: PropTypes.array.isRequired, + moreComments: PropTypes.bool.isRequired, + loadMore: PropTypes.func.isRequired +}; export default LoadMore; diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index 23f7eb29d..24b66f4bd 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -18,7 +18,7 @@ const getCountsByAssetID = (context, asset_ids) => { $in: asset_ids }, status: { - $in: [null, 'ACCEPTED'] + $in: ['NONE', 'ACCEPTED'] }, parent_id: null } @@ -51,7 +51,7 @@ const getCountsByParentID = (context, parent_ids) => { $in: parent_ids }, status: { - $in: [null, 'ACCEPTED'] + $in: ['NONE', 'ACCEPTED'] } } }, @@ -88,7 +88,7 @@ const getCommentsByQuery = ({user}, {ids, statuses, asset_id, parent_id, author_ } else { comments = comments.where({ status: { - $in: [null, 'ACCEPTED'] + $in: ['NONE', 'ACCEPTED'] } }); } diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 98891953c..91b2b0d01 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -11,10 +11,10 @@ const Wordlist = require('../../services/wordlist'); * @param {String} body body of the comment * @param {String} asset_id asset for the comment * @param {String} parent_id optional parent of the comment - * @param {String} [status=null] the status of the new comment + * @param {String} [status='NONE'] the status of the new comment * @return {Promise} resolves to the created comment */ -const createComment = ({user, loaders: {Comments}}, {body, asset_id, parent_id = null}, status = null) => { +const createComment = ({user, loaders: {Comments}}, {body, asset_id, parent_id = null}, status = 'NONE') => { return CommentsService.publicCreate({ body, asset_id, @@ -105,7 +105,7 @@ const resolveNewCommentStatus = (context, {asset_id, body}, wordlist = {}) => { if (charCountEnable && body.length > charCount) { return 'REJECTED'; } - return moderation === 'PRE' ? 'PREMOD' : null; + return moderation === 'PRE' ? 'PREMOD' : 'NONE'; }); } diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index b1c4824e8..d338c7c71 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -67,6 +67,10 @@ type Tag { # The statuses that a comment may have. enum COMMENT_STATUS { + # The comment is not PREMOD, but was not applied a moderation status by a + # moderator. + NONE + # The comment has been accepted by a moderator. ACCEPTED @@ -93,7 +97,7 @@ enum ACTION_TYPE { input CommentsQuery { # current status of a comment. - statuses: [COMMENT_STATUS] + statuses: [COMMENT_STATUS!] # asset that a comment is on. asset_id: ID @@ -152,7 +156,7 @@ type Comment { asset: Asset # The current status of a comment. - status: COMMENT_STATUS + status: COMMENT_STATUS! # The time when the comment was created created_at: Date! diff --git a/models/comment.js b/models/comment.js index ac44d904e..c88041c8a 100644 --- a/models/comment.js +++ b/models/comment.js @@ -6,7 +6,7 @@ const STATUSES = [ 'ACCEPTED', 'REJECTED', 'PREMOD', - null + 'NONE' ]; /** @@ -66,7 +66,11 @@ const CommentSchema = new Schema({ asset_id: String, author_id: String, status_history: [StatusSchema], - status: {type: String, default: null}, + status: { + type: String, + enum: STATUSES, + default: 'NONE' + }, tags: [TagSchema], parent_id: String }, { diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index 96f3911d4..e77ca0c30 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -52,7 +52,7 @@ router.get('/', (req, res, next) => { if (user_id) { query = CommentsService.findByUserId(user_id, authorization.has(req.user, 'ADMIN')); } else if (status) { - query = assetIDWrap(CommentsService.findByStatus(status === 'NEW' ? null : status)); + query = assetIDWrap(CommentsService.findByStatus(status === 'NEW' ? 'NONE' : status)); } else if (action_type) { query = CommentsService .findIdsByActionType(action_type) diff --git a/services/comments.js b/services/comments.js index bcc534c51..8ec2b6038 100644 --- a/services/comments.js +++ b/services/comments.js @@ -11,6 +11,7 @@ const STATUSES = [ 'ACCEPTED', 'REJECTED', 'PREMOD', + 'NONE', ]; module.exports = class CommentsService { @@ -31,7 +32,7 @@ module.exports = class CommentsService { body, asset_id, parent_id, - status = null, + status = 'NONE', author_id } = comment; @@ -146,7 +147,7 @@ module.exports = class CommentsService { * @param {String} status status of the comment to search for * @return {Promise} resovles to comment array */ - static findByStatus(status = null) { + static findByStatus(status = 'NONE') { return CommentModel.find({status}); } @@ -155,7 +156,7 @@ module.exports = class CommentsService { * @param {String} asset_id * @return {Promise} */ - static moderationQueue(status = null, asset_id = null) { + static moderationQueue(status = 'NONE', asset_id = null) { // Fetch the comments with statuses. let comments = CommentModel.find({status}); diff --git a/test/services/comments.js b/test/services/comments.js index 4c3e0eab9..0bab40bfa 100644 --- a/test/services/comments.js +++ b/test/services/comments.js @@ -131,7 +131,7 @@ describe('services.CommentsService', () => { expect(c2).to.not.be.null; expect(c2.id).to.be.uuid; - expect(c2.status).to.be.null; + expect(c2.status).to.be.equal('NONE'); expect(c3).to.not.be.null; expect(c3.id).to.be.uuid; @@ -225,7 +225,7 @@ describe('services.CommentsService', () => { return CommentsService.findById(comment_id) .then((c) => { - expect(c.status).to.be.null; + expect(c.status).to.be.equal('NONE'); return CommentsService.pushStatus(comment_id, 'REJECTED', '123'); })