diff --git a/client/coral-admin/src/actions/moderation.js b/client/coral-admin/src/actions/moderation.js index 4b0b0538a..3f8d975df 100644 --- a/client/coral-admin/src/actions/moderation.js +++ b/client/coral-admin/src/actions/moderation.js @@ -33,3 +33,12 @@ export const setSortOrder = (order) => ({ order }); +export const changeUserDetailStatuses = (tab) => { + let statuses; + if (tab === 'all') { + statuses = ['NONE', 'ACCEPTED', 'REJECTED', 'PREMOD']; + } else if (tab === 'rejected') { + statuses = ['REJECTED']; + } + return {type: actions.CHANGE_USER_DETAIL_STATUSES, tab, statuses}; +}; diff --git a/client/coral-admin/src/components/ActionButton.js b/client/coral-admin/src/components/ActionButton.js index 4eda2973e..e084d8e7a 100644 --- a/client/coral-admin/src/components/ActionButton.js +++ b/client/coral-admin/src/components/ActionButton.js @@ -17,11 +17,11 @@ const ActionButton = ({type = '', active, ...props}) => { return ( + >{props.minimal ? '' : t(`modqueue.${text}`)} ); }; diff --git a/client/coral-admin/src/components/ModerationList.css b/client/coral-admin/src/components/ModerationList.css index 4d4f37c9a..2b5989191 100644 --- a/client/coral-admin/src/components/ModerationList.css +++ b/client/coral-admin/src/components/ModerationList.css @@ -189,6 +189,12 @@ width: 140px; } +.minimal { + width: 45px; + min-width: 0; + float: left; +} + .approve__active { box-shadow: none; color: white; diff --git a/client/coral-admin/src/constants/moderation.js b/client/coral-admin/src/constants/moderation.js index 3586b1a75..aa8a51a24 100644 --- a/client/coral-admin/src/constants/moderation.js +++ b/client/coral-admin/src/constants/moderation.js @@ -8,3 +8,4 @@ export const HIDE_SUSPEND_USER_DIALOG = 'HIDE_SUSPEND_USER_DIALOG'; export const VIEW_USER_DETAIL = 'VIEW_USER_DETAIL'; export const HIDE_USER_DETAIL = 'HIDE_USER_DETAIL'; export const SET_SORT_ORDER = 'MODERATION_SET_SORT_ORDER'; +export const CHANGE_USER_DETAIL_STATUSES = 'CHANGE_USER_DETAIL_STATUSES'; diff --git a/client/coral-admin/src/reducers/moderation.js b/client/coral-admin/src/reducers/moderation.js index 10cbe0eca..4201ffb06 100644 --- a/client/coral-admin/src/reducers/moderation.js +++ b/client/coral-admin/src/reducers/moderation.js @@ -8,6 +8,8 @@ const initialState = fromJS({ commentId: null, commentStatus: null, userDetailId: null, + userDetailActiveTab: 'all', + userDetailStatuses: ['NONE', 'ACCEPTED', 'REJECTED', 'PREMOD'], banDialog: false, shortcutsNoteVisible: window.localStorage.getItem('coral:shortcutsNote') || 'show', sortOrder: 'REVERSE_CHRONOLOGICAL', @@ -65,6 +67,10 @@ export default function moderation (state = initialState, action) { return state.set('userDetailId', action.userId); case actions.HIDE_USER_DETAIL: return state.set('userDetailId', null); + case actions.CHANGE_USER_DETAIL_STATUSES: + return state + .set('userDetailActiveTab', action.tab) + .set('userDetailStatuses', action.statuses); case actions.SET_SORT_ORDER: return state.set('sortOrder', action.order); default : diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index 2664fefc5..d8275975a 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -23,6 +23,7 @@ const Comment = ({ viewUserDetail, suspectWords, bannedWords, + minimal, ...props }) => { const links = linkify.getMatches(comment.body); @@ -55,9 +56,13 @@ const Comment = ({
- viewUserDetail(comment.user.id)}> - {comment.user.name} - + { + !minimal && ( + viewUserDetail(comment.user.id)}> + {comment.user.name} + + ) + } {timeago(comment.created_at || Date.now() - props.index * 60 * 1000)} @@ -91,7 +96,7 @@ const Comment = ({
Story: {comment.asset.title} - {!props.currentAsset && + {!props.currentAsset && !minimal && {t('modqueue.moderate')}}
@@ -128,6 +133,7 @@ const Comment = ({ (action === 'APPROVE' && comment.status === 'ACCEPTED'); return ( + hideUserDetail={hideUserDetail} + bannedWords={settings.wordlist.banned} + suspectWords={settings.wordlist.suspect} + showBanUserDialog={props.showBanUserDialog} + showSuspendUserDialog={props.showSuspendUserDialog} + acceptComment={props.acceptComment} + rejectComment={props.rejectComment} /> )}
); } } - diff --git a/client/coral-admin/src/routes/Moderation/components/UserDetail.css b/client/coral-admin/src/routes/Moderation/components/UserDetail.css index e37971ada..1119be8c4 100644 --- a/client/coral-admin/src/routes/Moderation/components/UserDetail.css +++ b/client/coral-admin/src/routes/Moderation/components/UserDetail.css @@ -36,6 +36,23 @@ background-color: transparent; font-size: 16px; position: absolute; - width: 100%; + width: 90%; outline: none; } + +.commentStatuses { + padding: 0; + list-style: none; + + li { + display: inline-block; + margin: 0 10px; + cursor: pointer; + padding: 0 10px; + } +} + +.active { + font-weight: bold; + border-bottom: 3px solid #F36451; +} diff --git a/client/coral-admin/src/routes/Moderation/components/UserDetail.js b/client/coral-admin/src/routes/Moderation/components/UserDetail.js index e09770c63..c461220c8 100644 --- a/client/coral-admin/src/routes/Moderation/components/UserDetail.js +++ b/client/coral-admin/src/routes/Moderation/components/UserDetail.js @@ -2,12 +2,20 @@ import React, {PropTypes} from 'react'; import {Button, Drawer} from 'coral-ui'; import styles from './UserDetail.css'; import Slot from 'coral-framework/components/Slot'; +import Comment from './Comment'; +import {actionsMap} from '../helpers/moderationQueueActionsMap'; export default class UserDetail extends React.Component { static propTypes = { id: PropTypes.string.isRequired, hideUserDetail: PropTypes.func.isRequired, root: PropTypes.object.isRequired, + bannedWords: PropTypes.array.isRequired, + suspectWords: PropTypes.array.isRequired, + showBanUserDialog: PropTypes.func.isRequired, + showSuspendUserDialog: PropTypes.func.isRequired, + acceptComment: PropTypes.func.isRequired, + rejectComment: PropTypes.func.isRequired, } copyPermalink = () => { @@ -20,9 +28,33 @@ export default class UserDetail extends React.Component { } } + changeStatus = (tab) => { + if (tab === 'all') { + this.props.changeStatus('all'); + } else if (tab === 'rejected') { + this.props.changeStatus('rejected'); + } + } + render () { - const {root: {user, totalComments, rejectedComments}, hideUserDetail} = this.props; + const { + root: { + user, + totalComments, + rejectedComments, + comments: {nodes} + }, + moderation: {userDetailActiveTab: tab}, + bannedWords, + suspectWords, + showBanUserDialog, + showSuspendUserDialog, + acceptComment, + rejectComment, + hideUserDetail + } = this.props; const localProfile = user.profiles.find((p) => p.provider === 'local'); + let profile; if (localProfile) { profile = localProfile.id; @@ -62,8 +94,34 @@ export default class UserDetail extends React.Component {

{`${(rejectedPercent).toFixed(1)}%`}

+ +
+ { + nodes.map((comment, i) => { + const status = comment.action_summaries ? 'FLAGGED' : comment.status; + return {}} + actions={actionsMap[status]} + showBanUserDialog={showBanUserDialog} + showSuspendUserDialog={showSuspendUserDialog} + acceptComment={acceptComment} + rejectComment={rejectComment} + currentAsset={null} + currentUserId={this.props.id} + minimal={true} />; + }) + } +
); } } - diff --git a/client/coral-admin/src/routes/Moderation/components/styles.css b/client/coral-admin/src/routes/Moderation/components/styles.css index bfc79355d..c38f4fb5d 100644 --- a/client/coral-admin/src/routes/Moderation/components/styles.css +++ b/client/coral-admin/src/routes/Moderation/components/styles.css @@ -247,6 +247,7 @@ span { } .created { + padding: 5px; color: #262626; font-size: 14px; margin-left: 15px; @@ -433,7 +434,6 @@ span { .username { color: blue; text-decoration: underline; - padding: 5px; cursor: pointer; &:hover { diff --git a/client/coral-admin/src/routes/Moderation/containers/UserDetail.js b/client/coral-admin/src/routes/Moderation/containers/UserDetail.js index e4f98d6be..9afd10efb 100644 --- a/client/coral-admin/src/routes/Moderation/containers/UserDetail.js +++ b/client/coral-admin/src/routes/Moderation/containers/UserDetail.js @@ -1,8 +1,25 @@ import React, {PropTypes} from 'react'; import {compose, gql} from 'react-apollo'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; import UserDetail from '../components/UserDetail'; import withQuery from 'coral-framework/hocs/withQuery'; import {getSlotsFragments} from 'coral-framework/helpers/plugins'; +import {getDefinitionName} from 'coral-framework/utils'; +import {changeUserDetailStatuses} from 'coral-admin/src/actions/moderation'; +import Comment from './Comment'; + +const commentConnectionFragment = gql` + fragment CoralAdmin_Moderation_CommentConnection on CommentConnection { + nodes { + ...${getDefinitionName(Comment.fragments.comment)} + } + hasNextPage + startCursor + endCursor + } + ${Comment.fragments.comment} +`; const pluginFragments = getSlotsFragments([ 'userProfile', @@ -19,12 +36,12 @@ class UserDetailContainer extends React.Component { return null; } - return ; + return ; } } export const withUserDetailQuery = withQuery(gql` - query CoralAdmin_UserDetail($author_id: ID!) { + query CoralAdmin_UserDetail($author_id: ID!, $statuses: [COMMENT_STATUS!]) { user(id: $author_id) { id username @@ -37,18 +54,35 @@ export const withUserDetailQuery = withQuery(gql` } totalComments: commentCount(query: {author_id: $author_id}) rejectedComments: commentCount(query: {author_id: $author_id, statuses: [REJECTED]}) + comments: comments(query: { + author_id: $author_id, + statuses: $statuses + }) { + ...CoralAdmin_Moderation_CommentConnection + } ${pluginFragments.spreads('root')} } + ${Comment.fragments.comment} ${pluginFragments.definitions('user')} ${pluginFragments.definitions('root')} + ${commentConnectionFragment} `, { - options: ({id}) => { + options: ({id, moderation: {userDetailStatuses: statuses}}) => { return { - variables: {author_id: id} + variables: {author_id: id, statuses} }; } }); +const mapStateToProps = (state) => ({ + moderation: state.moderation.toJS() +}); + +const mapDispatchToProps = (dispatch) => ({ + ...bindActionCreators({changeUserDetailStatuses}, dispatch) +}); + export default compose( + connect(mapStateToProps, mapDispatchToProps), withUserDetailQuery, )(UserDetailContainer); diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js index e3f0137b4..48392cc51 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/components/Comment.js @@ -308,15 +308,6 @@ class Comment extends React.Component { commentId={comment.id} inline /> - {!disableReply && - - setActiveReplyBox(comment.id)} - parentCommentId={parentId || comment.id} - currentUserId={currentUser && currentUser.id} - banned={false} - /> - } + {!disableReply && + + setActiveReplyBox(comment.id)} + parentCommentId={parentId || comment.id} + currentUserId={currentUser && currentUser.id} + banned={false} + /> + } { users.length - ?

Because you ignored these, you do not see their comments.

+ ?

{t('framework.because_you_ignored')}

: null }
@@ -29,7 +29,7 @@ export class IgnoredUsers extends Component {
stopIgnoring({id})} - className={styles.link}>Stop ignoring + className={styles.link}>{t('framework.stop_ignoring')}
)) diff --git a/client/coral-ui/components/Drawer.css b/client/coral-ui/components/Drawer.css index d22512a3a..d6a7e6871 100644 --- a/client/coral-ui/components/Drawer.css +++ b/client/coral-ui/components/Drawer.css @@ -1,15 +1,25 @@ .drawer { max-width: 700px; - min-width: 400px; - padding: 20px; + min-width: 550px; position: fixed; top: 0; - right: 0; + right: -17px; bottom: 0; background-color: white; transition: transform 500ms ease-in-out; box-shadow: -3px 0px 4px 0px rgba(0,0,0,0.15); z-index: 10000; + + .content { + position: absolute; + padding: 20px; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow-y: scroll; + overflow-x: hidden; + } } .closeButton { diff --git a/client/coral-ui/components/Drawer.js b/client/coral-ui/components/Drawer.js index d896203a8..e9ebed92c 100644 --- a/client/coral-ui/components/Drawer.js +++ b/client/coral-ui/components/Drawer.js @@ -6,7 +6,9 @@ const Drawer = ({children, handleClickOutside}) => { return (
×
- {children} +
+ {children} +
); }; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 7f5982779..aed610ad7 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -148,6 +148,9 @@ enum ACTION_TYPE { # CommentsQuery allows the ability to query comments by a specific methods. input CommentsQuery { + # Author of the comments + author_id: ID + # Current status of a comment. Requires the `ADMIN` role. statuses: [COMMENT_STATUS!] diff --git a/locales/en.yml b/locales/en.yml index fdf0208b0..5ff20f033 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -199,10 +199,11 @@ en: flag_username: "Report username" framework: banned_account_msg: "Your account is currently suspended. This means that you cannot Like Report or write comments. Please contact us if you have any questions." + because_you_ignored: "Because you ignored the following commenters, their comments are hidden." comment: comment comment_is_ignored: "This comment is hidden because you ignored this user." comments: comments - configure_stream: "Configure Stream" + configure_stream: "Configure" content_not_available: "This content is not available" edit_name: button: Submit @@ -215,6 +216,7 @@ en: new_count: "View {0} new {1}" profile: Profile show_all_comments: "Show all comments" + stop_ignoring: "Stop ignoring" success_bio_update: "Your biography has been updated" success_name_update: "Your username has been updated" success_update_settings: "The changes you have made have been applied to the comment stream on this article" diff --git a/locales/es.yml b/locales/es.yml index 7ac1e6ad5..1f57c4861 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -215,6 +215,7 @@ es: new_count: "Ver {0} {1} nuevo" profile: Perfil show_all_comments: "Mostrar todos los comentarios" + stop_ignoring: "No ignorar más" success_bio_update: "Tu biografia fue actualizada" success_name_update: "Tu nombre de usuario ha sido actualizado" success_update_settings: "La configuración de este articulo fue actualizada" diff --git a/plugins/coral-plugin-auth/client/components/UserBox.js b/plugins/coral-plugin-auth/client/components/UserBox.js index 91959a35b..47608c0e3 100644 --- a/plugins/coral-plugin-auth/client/components/UserBox.js +++ b/plugins/coral-plugin-auth/client/components/UserBox.js @@ -10,7 +10,7 @@ const UserBox = ({loggedIn, user, logout, onShowProfile}) => ( { loggedIn ? (
- {t('sign_in.logged_in_as')} + {t('sign_in.logged_in_as')} {user.username}. {t('sign_in.not_you')} logout()}> {t('sign_in.logout')} diff --git a/plugins/coral-plugin-auth/client/components/styles.css b/plugins/coral-plugin-auth/client/components/styles.css index d3cd47b87..c8fbf9a30 100644 --- a/plugins/coral-plugin-auth/client/components/styles.css +++ b/plugins/coral-plugin-auth/client/components/styles.css @@ -70,6 +70,10 @@ input.error{ letter-spacing: 0.1px; } +.userBoxLoggedIn { + font-weight: bold; +} + .userBox a { color: black; font-weight: bold; diff --git a/plugins/coral-plugin-auth/client/translations.yml b/plugins/coral-plugin-auth/client/translations.yml index 488f505d6..bc7f06759 100644 --- a/plugins/coral-plugin-auth/client/translations.yml +++ b/plugins/coral-plugin-auth/client/translations.yml @@ -5,10 +5,10 @@ en: verify_email: "Thank you for creating an account! We sent an email to the address you provided to verify your account." verify_email2: "You must verify your account before engaging with the community." not_you: "Not you?" - logged_in_as: "Logged in as" + logged_in_as: "Signed in as" facebook_sign_in: "Sign in with Facebook" facebook_sign_up: "Sign up with Facebook" - logout: "Logout" + logout: "Sign out" sign_in: "Sign in" sign_in_to_join: "Sign in to join the conversation" or: "Or"