diff --git a/client/coral-admin/src/components/ActionButton.js b/client/coral-admin/src/components/ActionButton.js index ae75b829e..3651ee939 100644 --- a/client/coral-admin/src/components/ActionButton.js +++ b/client/coral-admin/src/components/ActionButton.js @@ -3,9 +3,8 @@ import styles from './ModerationList.css'; import {Button} from 'coral-ui'; import {menuActionsMap} from '../containers/ModerationQueue/helpers/moderationQueueActionsMap'; -const ActionButton = ({type = '', status, ...props}) => { +const ActionButton = ({type = '', active, ...props}) => { const typeName = type.toLowerCase(); - const active = ((type === 'REJECT' && status === 'REJECTED') || (type === 'APPROVE' && status === 'ACCEPTED')); let text = menuActionsMap[type].text; if (text === 'Approve' && active) { @@ -25,7 +24,7 @@ const ActionButton = ({type = '', status, ...props}) => { }; ActionButton.propTypes = { - status: PropTypes.string + active: PropTypes.bool }; export default ActionButton; diff --git a/client/coral-admin/src/containers/Dashboard/FlagWidget.js b/client/coral-admin/src/containers/Dashboard/FlagWidget.js index 92de6c4d2..ea72cddc6 100644 --- a/client/coral-admin/src/containers/Dashboard/FlagWidget.js +++ b/client/coral-admin/src/containers/Dashboard/FlagWidget.js @@ -21,7 +21,7 @@ const FlagWidget = ({assets}) => { ? assets.map(asset => { let flagSummary = null; if (asset.action_summaries) { - flagSummary = asset.action_summaries.find(s => s.type === 'FlagAssetActionSummary'); + flagSummary = asset.action_summaries.find(s => s.__typename === 'FlagAssetActionSummary'); } return ( diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js index b76de1d05..29612b0a3 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js @@ -5,7 +5,7 @@ import key from 'keymaster'; import isEqual from 'lodash/isEqual'; import styles from './components/styles.css'; -import {modQueueQuery} from '../../graphql/queries'; +import {modQueueQuery, getQueueCounts} from '../../graphql/queries'; import {banUser, setCommentStatus} from '../../graphql/mutations'; import {fetchSettings} from 'actions/settings'; @@ -231,6 +231,7 @@ const mapDispatchToProps = dispatch => ({ export default compose( connect(mapStateToProps, mapDispatchToProps), setCommentStatus, + getQueueCounts, modQueueQuery, banUser )(ModerationContainer); diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js index 093c2f5dd..f6291c071 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js @@ -9,62 +9,79 @@ import translations from 'coral-admin/src/translations'; import LoadMore from './components/LoadMore'; const lang = new I18n(translations); -const ModerationQueue = ({ - comments, - selectedIndex, - commentCount, - singleView, - loadMore, - activeTab, - sort, - viewUserDetail, - ...props}) => { - return ( -
- - -
- ); -}; +class ModerationQueue extends React.Component { -ModerationQueue.propTypes = { - viewUserDetail: PropTypes.func.isRequired, - bannedWords: PropTypes.arrayOf(PropTypes.string).isRequired, - suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired, - currentAsset: PropTypes.object, - showBanUserDialog: PropTypes.func.isRequired, - rejectComment: PropTypes.func.isRequired, - acceptComment: PropTypes.func.isRequired, - comments: PropTypes.array.isRequired -}; + static propTypes = { + viewUserDetail: PropTypes.func.isRequired, + bannedWords: PropTypes.arrayOf(PropTypes.string).isRequired, + suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired, + currentAsset: PropTypes.object, + showBanUserDialog: PropTypes.func.isRequired, + rejectComment: PropTypes.func.isRequired, + acceptComment: PropTypes.func.isRequired, + comments: PropTypes.array.isRequired + } + + componentDidUpdate (prev) { + const {loadMore, comments, commentCount, sort, activeTab: tab, assetId: asset_id} = this.props; + + // if the user just moderated the last (visible) comment + // AND there are more comments available on the server, + // go ahead and load more comments + if (prev.comments.length > 0 && comments.length === 0 && commentCount > 0) { + loadMore({sort, tab, asset_id}); + } + } + + render () { + const { + comments, + selectedIndex, + commentCount, + singleView, + loadMore, + activeTab, + sort, + viewUserDetail, + ...props + } = this.props; + + return ( +
+ + +
+ ); + } +} export default ModerationQueue; diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js index 8956e6e03..8294e8c43 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js @@ -66,15 +66,17 @@ const Comment = ({actions = [], comment, viewUserDetail, ...props}) => {
{links ? Contains Link : null}
- {actions.map((action, i) => - props.acceptComment({commentId: comment.id})} - rejectComment={() => props.rejectComment({commentId: comment.id})} - /> - )} + {actions.map((action, i) => { + const active = (action === 'REJECT' && comment.status === 'REJECTED') || + (action === 'APPROVE' && comment.status === 'ACCEPTED'); + return props.acceptComment({commentId: comment.id})} + rejectComment={() => props.rejectComment({commentId: comment.id})} />; + })}
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js index f16635486..c90d3cd14 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js @@ -7,13 +7,16 @@ const LoadMore = ({comments, loadMore, sort, tab, assetId, showLoadMore}) => { showLoadMore && } diff --git a/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql b/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql index c77fbc32b..726194143 100644 --- a/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql +++ b/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql @@ -6,7 +6,6 @@ fragment metrics on Asset { created_at commentCount action_summaries { - type: __typename actionCount actionableItemCount } diff --git a/client/coral-admin/src/graphql/mutations/index.js b/client/coral-admin/src/graphql/mutations/index.js index 4d62201d1..6b21c8421 100644 --- a/client/coral-admin/src/graphql/mutations/index.js +++ b/client/coral-admin/src/graphql/mutations/index.js @@ -44,6 +44,7 @@ export const suspendUser = graphql(SUSPEND_USER, { }) }); +const views = ['all', 'premod', 'flagged', 'accepted', 'rejected']; export const setCommentStatus = graphql(SET_COMMENT_STATUS, { props: ({mutate}) => ({ acceptComment: ({commentId}) => { @@ -54,7 +55,9 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, { }, updateQueries: { ModQueue: (oldData) => { - const comment = oldData.all.find(c => c.id === commentId); + const comment = views.reduce((comment, view) => { + return comment ? comment : oldData[view].find(c => c.id === commentId); + }, null); let accepted; let acceptedCount = oldData.acceptedCount; @@ -76,10 +79,10 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, { return { ...oldData, - premodCount, - flaggedCount, - acceptedCount, - rejectedCount, + premodCount: Math.max(0, premodCount), + flaggedCount: Math.max(0, flaggedCount), + acceptedCount: Math.max(0, acceptedCount), + rejectedCount: Math.max(0, rejectedCount), premod, flagged, accepted, @@ -97,7 +100,9 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, { }, updateQueries: { ModQueue: (oldData) => { - const comment = oldData.all.find(c => c.id === commentId); + const comment = views.reduce((comment, view) => { + return comment ? comment : oldData[view].find(c => c.id === commentId); + }, null); let rejected; let rejectedCount = oldData.rejectedCount; @@ -119,10 +124,10 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, { return { ...oldData, - premodCount, - flaggedCount, - acceptedCount, - rejectedCount, + premodCount: Math.max(0, premodCount), + flaggedCount: Math.max(0, flaggedCount), + acceptedCount: Math.max(0, acceptedCount), + rejectedCount: Math.max(0, rejectedCount), premod, flagged, accepted, diff --git a/client/coral-admin/src/graphql/queries/getQueueCounts.graphql b/client/coral-admin/src/graphql/queries/getQueueCounts.graphql new file mode 100644 index 000000000..4a7c00d87 --- /dev/null +++ b/client/coral-admin/src/graphql/queries/getQueueCounts.graphql @@ -0,0 +1,22 @@ +query Counts ($asset_id: ID) { + allCount: commentCount(query: { + asset_id: $asset_id + }) + acceptedCount: commentCount(query: { + statuses: [ACCEPTED], + asset_id: $asset_id + }) + premodCount: commentCount(query: { + statuses: [PREMOD], + asset_id: $asset_id + }) + rejectedCount: commentCount(query: { + statuses: [REJECTED], + asset_id: $asset_id + }) + flaggedCount: commentCount(query: { + action_type: FLAG, + asset_id: $asset_id, + statuses: [NONE, PREMOD] + }) +} diff --git a/client/coral-admin/src/graphql/queries/index.js b/client/coral-admin/src/graphql/queries/index.js index 8ab3c7292..9caa90ad0 100644 --- a/client/coral-admin/src/graphql/queries/index.js +++ b/client/coral-admin/src/graphql/queries/index.js @@ -5,6 +5,7 @@ import MOD_QUEUE_LOAD_MORE from './loadMore.graphql'; import MOD_USER_FLAGGED_QUERY from './modUserFlaggedQuery.graphql'; import METRICS from './metricsQuery.graphql'; import USER_DETAIL from './userDetail.graphql'; +import GET_QUEUE_COUNTS from './getQueueCounts.graphql'; export const modQueueQuery = graphql(MOD_QUEUE_QUERY, { options: ({params: {id = null}}) => { @@ -34,34 +35,34 @@ export const getMetrics = graphql(METRICS, { } }); -export const loadMore = (fetchMore) => ({limit, cursor, sort, tab, asset_id}) => { - let statuses; +export const loadMore = (fetchMore) => ({limit = 10, cursor, sort, tab, asset_id}) => { + let variables = { + limit, + cursor, + sort, + asset_id + }; switch(tab) { case 'all': - statuses = null; + variables.statuses = null; break; case 'accepted': - statuses = ['ACCEPTED']; + variables.statuses = ['ACCEPTED']; break; case 'premod': - statuses = ['PREMOD']; + variables.statuses = ['PREMOD']; break; case 'flagged': - statuses = ['NONE', 'PREMOD']; + variables.statuses = ['NONE', 'PREMOD']; + variables.action_type = 'FLAG'; break; case 'rejected': - statuses = ['REJECTED']; + variables.statuses = ['REJECTED']; break; } return fetchMore({ query: MOD_QUEUE_LOAD_MORE, - variables: { - limit, - cursor, - sort, - statuses, - asset_id - }, + variables, updateQuery: (oldData, {fetchMoreResult:{comments}}) => { return { ...oldData, @@ -102,3 +103,14 @@ export const getUserDetail = graphql(USER_DETAIL, { }; } }); + +export const getQueueCounts = graphql(GET_QUEUE_COUNTS, { + options: ({params: {id = null}}) => { + return { + pollInterval: 5000, + variables: { + asset_id: id + } + }; + } +}); diff --git a/client/coral-admin/src/graphql/queries/loadMore.graphql b/client/coral-admin/src/graphql/queries/loadMore.graphql index 56966a804..a42c7b2bf 100644 --- a/client/coral-admin/src/graphql/queries/loadMore.graphql +++ b/client/coral-admin/src/graphql/queries/loadMore.graphql @@ -1,7 +1,7 @@ #import "../fragments/commentView.graphql" -query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!]) { - comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort}) { +query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!], $action_type: ACTION_TYPE) { + comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort, action_type: $action_type}) { ...commentView action_summaries { count diff --git a/client/coral-admin/src/services/fragmentMatcher.js b/client/coral-admin/src/services/fragmentMatcher.js index bf167cd1e..531708f31 100644 --- a/client/coral-admin/src/services/fragmentMatcher.js +++ b/client/coral-admin/src/services/fragmentMatcher.js @@ -36,6 +36,7 @@ const fm = new IntrospectionFragmentMatcher({ kind: 'INTERFACE', name: 'Action', possibleTypes: [ + {name: 'DefaultAction'}, {name: 'FlagAction'}, {name: 'DontAgreeAction'} ], @@ -44,6 +45,7 @@ const fm = new IntrospectionFragmentMatcher({ kind: 'INTERFACE', name: 'ActionSummary', possibleTypes: [ + {name: 'DefaultActionSummary'}, {name: 'FlagActionSummary'}, {name: 'DontAgreeActionSummary'} ], diff --git a/test/server/graph/loaders/metrics.js b/test/server/graph/loaders/metrics.js index 75eadaff8..a24c18f79 100644 --- a/test/server/graph/loaders/metrics.js +++ b/test/server/graph/loaders/metrics.js @@ -70,7 +70,6 @@ describe('graph.loaders.Metrics', () => { fragment metrics on Asset { id action_summaries { - type: __typename actionCount actionableItemCount }