diff --git a/.gitignore b/.gitignore index e8dfd14e7..a619f0988 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,6 @@ plugins/* !plugins/coral-plugin-facebook-auth !plugins/coral-plugin-respect !plugins/coral-plugin-offtopic +!plugins/coral-plugin-like **/node_modules/* 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 afd732cc7..abf348897 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 eb59f369e..7a3aab909 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'; @@ -220,6 +220,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 433859e9a..a25e7b5e3 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js @@ -9,51 +9,67 @@ 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, ...props}) => { - return ( -
- - -
- ); -}; +class ModerationQueue extends React.Component { -ModerationQueue.propTypes = { - 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 = { + 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, ...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 e575beb0a..619a3cd7a 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, ...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/CommentCount.js b/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js index 14cf8ecfc..0517caf0f 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js @@ -1,9 +1,25 @@ import React, {PropTypes} from 'react'; import styles from './CommentCount.css'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from 'coral-admin/src/translations.json'; +const lang = new I18n(translations); -const CommentCount = props => ( - {props.count} -); +const CommentCount = ({count}) => { + let number = count; + + // shorten large counts to abbreviations + if (number / 1e9 > 1) { + number = `${(number / 1e9).toFixed(1)}${lang.t('modqueue.billion')}`; + } else if (number / 1e6 > 1) { + number = `${(number / 1e6).toFixed(1)}${lang.t('modqueue.million')}`; + } else if (number / 1e3 > 1) { + number = `${(number / 1e3).toFixed(1)}${lang.t('modqueue.thousand')}`; + } + + return ( + {number} + ); +}; CommentCount.propTypes = { count: PropTypes.number.isRequired 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 9d2357ce7..f26c8464e 100644 --- a/client/coral-admin/src/graphql/queries/index.js +++ b/client/coral-admin/src/graphql/queries/index.js @@ -4,6 +4,7 @@ import MOD_QUEUE_QUERY from './modQueueQuery.graphql'; import MOD_QUEUE_LOAD_MORE from './loadMore.graphql'; import MOD_USER_FLAGGED_QUERY from './modUserFlaggedQuery.graphql'; import METRICS from './metricsQuery.graphql'; +import GET_QUEUE_COUNTS from './getQueueCounts.graphql'; export const modQueueQuery = graphql(MOD_QUEUE_QUERY, { options: ({params: {id = null}}) => { @@ -33,34 +34,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, @@ -93,3 +94,14 @@ export const modQueueResort = (id, fetchMore) => (sort) => { updateQuery: (oldData, {fetchMoreResult:{data}}) => data }); }; + +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 afb87d445..531708f31 100644 --- a/client/coral-admin/src/services/fragmentMatcher.js +++ b/client/coral-admin/src/services/fragmentMatcher.js @@ -20,7 +20,6 @@ const fm = new IntrospectionFragmentMatcher({ name: 'Response', possibleTypes: [ {name: 'CreateCommentResponse'}, - {name: 'CreateLikeResponse'}, {name: 'CreateFlagResponse'}, {name: 'CreateDontAgreeResponse'}, {name: 'DeleteActionResponse'}, @@ -37,8 +36,8 @@ const fm = new IntrospectionFragmentMatcher({ kind: 'INTERFACE', name: 'Action', possibleTypes: [ + {name: 'DefaultAction'}, {name: 'FlagAction'}, - {name: 'LikeAction'}, {name: 'DontAgreeAction'} ], }, @@ -46,8 +45,8 @@ const fm = new IntrospectionFragmentMatcher({ kind: 'INTERFACE', name: 'ActionSummary', possibleTypes: [ + {name: 'DefaultActionSummary'}, {name: 'FlagActionSummary'}, - {name: 'LikeActionSummary'}, {name: 'DontAgreeActionSummary'} ], }, @@ -57,7 +56,6 @@ const fm = new IntrospectionFragmentMatcher({ possibleTypes: [ {name: 'DefaultAssetActionSummary'}, {name: 'FlagAssetActionSummary'}, - {name: 'LikeAssetActionSummary'} ] } ], diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index 6401f4990..3f9392656 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -63,7 +63,10 @@ "impersonating": "Impersonating", "offensive": "Offensive", "spam/ads": "Spam/Ads", - "other": "Other" + "other": "Other", + "thousand": "k", + "million": "M", + "billion": "B" }, "comment": { "flagged": "flagged", @@ -248,7 +251,10 @@ "impersonating": "Suplantación", "offensive": "Ofensivo", "spam/ads": "Spam/Propaganda", - "other": "Otros" + "other": "Otros", + "thousand": "m", + "million": "M", + "billion": "B" }, "comment": { "flagged": "marcado", diff --git a/client/coral-configure/components/ConfigureCommentStream.css b/client/coral-configure/components/ConfigureCommentStream.css index 335c94377..a8cbc36cb 100644 --- a/client/coral-configure/components/ConfigureCommentStream.css +++ b/client/coral-configure/components/ConfigureCommentStream.css @@ -3,10 +3,8 @@ } .apply { - position: absolute; - top: 38%; - transform: translateX(-50%); - right: 0; + float: right; + margin: 0 10px; } .wrapper ul { diff --git a/client/coral-configure/components/ConfigureCommentStream.js b/client/coral-configure/components/ConfigureCommentStream.js index a7153823b..fe93f3272 100644 --- a/client/coral-configure/components/ConfigureCommentStream.js +++ b/client/coral-configure/components/ConfigureCommentStream.js @@ -10,8 +10,7 @@ export default ({handleChange, handleApply, changed, ...props}) => (
-

{lang.t('configure.title')}

-

{lang.t('configure.description')}

+

{lang.t('configureCommentStream.title')}

+

{lang.t('configureCommentStream.description')}