Merge branch 'master' into feature/talk-plugin-toxic-comments

This commit is contained in:
Kim Gardner
2017-08-03 09:31:05 -04:00
committed by GitHub
16 changed files with 357 additions and 30 deletions
@@ -0,0 +1,42 @@
.tag {
border: 1px solid #696969;
display: inline-block;
color: #696969;
background-color: white;
box-sizing: border-box;
padding: 2px 8px;
border-radius: 2px;
font-size: 12px;
height: 28px;
transition: background-color .2s cubic-bezier(.4,0,.2,1), color .2s cubic-bezier(.4,0,.2,1), border-color .2s cubic-bezier(.4,0,.2,1);
margin: 2px 0px;
letter-spacing: 0.4px;
}
.tag:hover {
background-color: #5384B2;
border-color: #5384B2;
color: white;
cursor: pointer;
}
.tag.featured {
background-color: #10589b;
border-color: #10589b;
color: white;
}
.tag.featured:hover {
background-color: white;
border-color: #5384B2;
color: #5384B2;
cursor: pointer;
}
.tagIcon {
margin-right: 5px;
font-size: 15px;
vertical-align: text-bottom;
}
@@ -0,0 +1,61 @@
import React from 'react';
import cn from 'classnames';
import styles from './ModTag.css';
import {t} from 'plugin-api/beta/client/services';
import {Icon} from 'plugin-api/beta/client/components/ui';
import * as notification from 'coral-admin/src/services/notification';
export default class ModTag extends React.Component {
constructor() {
super();
this.state = {
on: false
};
}
handleMouseEnter = (e) => {
e.preventDefault();
this.setState({
on: true
});
}
handleMouseLeave = (e) => {
e.preventDefault();
this.setState({
on: false
});
}
postTag = async () => {
try {
await this.props.postTag();
notification.success(t('talk-plugin-featured-comments.notify_self_featured', this.props.comment.user.username));
}
catch(err) {
notification.showMutationErrors(err);
}
}
render() {
const {alreadyTagged, deleteTag} = this.props;
return alreadyTagged ? (
<span className={cn(styles.tag, styles.featured)}
onClick={deleteTag}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave} >
<Icon name="star_outline" className={cn(styles.tagIcon)} />
{!this.state.on ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.un_feature')}
</span>
) : (
<span className={cn(styles.tag, {[styles.featured]: alreadyTagged})}
onClick={this.postTag} >
<Icon name="star_outline" className={cn(styles.tagIcon)} />
{alreadyTagged ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.feature')}
</span>
);
}
}
@@ -0,0 +1,110 @@
import React from 'react';
import {gql} from 'react-apollo';
import {connect} from 'react-redux';
import Comment from 'coral-admin/src/routes/Moderation/containers/Comment';
import {handleCommentChange} from 'coral-admin/src/graphql/utils';
import {getDefinitionName} from 'coral-framework/utils';
import truncate from 'lodash/truncate';
import t from 'coral-framework/services/i18n';
function prepareNotificationText(text) {
return truncate(text, {length: 50}).replace('\n', ' ');
}
class ModSubscription extends React.Component {
subscriptions = null;
componentWillMount() {
const configs = [
{
document: COMMENT_FEATURED_SUBSCRIPTION,
variables: {
assetId: this.props.data.variables.asset_id,
},
updateQuery: (prev, {subscriptionData: {data: {commentFeatured: {user, comment}}}}) => {
const sort = this.props.data.variables.sort;
const text = this.props.user.id === user.id
? {}
: t(
'talk-plugin-featured-comments.notify_featured',
user.username,
prepareNotificationText(comment.body),
);
const notify = {
activeQueue: this.props.activeTab,
text,
anyQueue: true,
};
return handleCommentChange(prev, comment, sort, notify);
},
},
{
document: COMMENT_UNFEATURED_SUBSCRIPTION,
variables: {
assetId: this.props.data.variables.asset_id,
},
updateQuery: (prev, {subscriptionData: {data: {commentUnfeatured: {user, comment}}}}) => {
const sort = this.props.data.variables.sort;
const text = this.props.user.id === user.id
? {}
: t(
'talk-plugin-featured-comments.notify_unfeatured',
user.username,
prepareNotificationText(comment.body),
);
const notify = {
activeQueue: this.props.activeTab,
text,
anyQueue: true,
};
return handleCommentChange(prev, comment, sort, notify);
}
},
];
this.subscriptions = configs.map((config) => this.props.data.subscribeToMore(config));
}
componentWillUnmount() {
this.subscriptions.forEach((unsubscribe) => unsubscribe());
}
render() {
return null;
}
}
const COMMENT_FEATURED_SUBSCRIPTION = gql`
subscription CommentFeatured($assetId: ID){
commentFeatured(asset_id: $assetId) {
comment {
...${getDefinitionName(Comment.fragments.comment)}
}
user {
id
username
}
}
}
${Comment.fragments.comment}
`;
const COMMENT_UNFEATURED_SUBSCRIPTION = gql`
subscription CommentUnfeatured($assetId: ID){
commentUnfeatured(asset_id: $assetId){
comment {
...${getDefinitionName(Comment.fragments.comment)}
}
user {
id
username
}
}
}
${Comment.fragments.comment}
`;
const mapStateToProps = (state) => ({
user: state.auth.toJS().user,
});
export default connect(mapStateToProps, null)(ModSubscription);
@@ -0,0 +1,5 @@
import ModTag from '../components/ModTag';
import {withTags} from 'plugin-api/beta/client/hocs';
export default withTags('featured')(ModTag);
@@ -5,6 +5,8 @@ import TabPane from './containers/TabPane';
import translations from './translations.yml';
import update from 'immutability-helper';
import reducer from './reducer';
import ModTag from './containers/ModTag';
import ModSubscription from './containers/ModSubscription';
import {findCommentInEmbedQuery} from 'coral-embed-stream/src/graphql/utils';
import {insertCommentsSorted} from 'plugin-api/beta/client/utils';
@@ -16,7 +18,9 @@ export default {
streamTabs: [Tab],
streamTabPanes: [TabPane],
commentInfoBar: [Tag],
commentReactions: [Button]
commentReactions: [Button],
adminModeration: [ModSubscription],
adminCommentInfoBar: [ModTag],
},
mutations: {
IgnoreUser: ({variables}) => ({
@@ -1,12 +1,19 @@
en:
talk-plugin-featured-comments:
un_feature: Un-Feature
feature: Feature
featured: Featured
featured_comments: Featured Comments
go_to_conversation: Go to conversation
tooltip_description: Comments selected by our team as worth reading
notify_self_featured: 'The comment from {0} is now featured and approved'
notify_featured: '{0} featured and approved comment "{1}"'
notify_unfeatured: '{0} unfeatured comment "{1}"'
es:
talk-plugin-featured-comments:
un_feature: Desmarcar
feature: Remarcar
featured: Remarcado
featured_comments: Comentarios Remarcados
go_to_conversation: Ir al comentario
tooltip_description: Comentarios seleccionados por nuestro equipo que valen la pena ser leidos
tooltip_description: Comentarios seleccionados por nuestro equipo que valen la pena ser leidos
@@ -1,4 +1,87 @@
const {check} = require('perms/utils');
module.exports = {
typeDefs: `
type CommentFeaturedData {
comment: Comment!
user: User!
}
type CommentUnfeaturedData {
comment: Comment!
user: User!
}
type Subscription {
# Subscribe to featured comments.
commentFeatured(asset_id: ID): CommentFeaturedData
# Subscribe to featured comments.
commentUnfeatured(asset_id: ID): CommentUnfeaturedData
}
`,
resolvers: {
Subscription: {
commentFeatured: ({user, comment}) => {
return {user, comment};
},
commentUnfeatured: ({user, comment}) => {
return {user, comment};
},
},
},
setupFunctions: {
commentFeatured: (options, args) => ({
commentFeatured: {
filter: ({comment}, {user}) => {
if (args.asset_id === null) {
return check(user, ['ADMIN', 'MODERATOR']);
}
return comment.asset_id === args.asset_id;
},
},
}),
commentUnfeatured: (options, args) => ({
commentUnfeatured: {
filter: ({comment}, {user}) => {
if (args.asset_id === null) {
return check(user, ['ADMIN', 'MODERATOR']);
}
return comment.asset_id === args.asset_id;
},
},
}),
},
hooks: {
RootMutation: {
addTag: {
async post(obj, {tag: {name, id, item_type}}, {user, mutators: {Comment}, pubsub}, info, result) {
if (name === 'FEATURED' && item_type === 'COMMENTS') {
const comment = await Comment.setStatus({id: id, status: 'ACCEPTED'});
if (comment) {
pubsub.publish('commentFeatured', {comment, user});
}
return result;
}
return result;
},
},
removeTag: {
async post(obj, {tag: {name, id, item_type}}, {user, loaders: {Comments}, pubsub}, info, result) {
if (name === 'FEATURED' && item_type === 'COMMENTS') {
const comment = await Comments.get.load(id);
if (comment) {
pubsub.publish('commentUnfeatured', {comment, user});
}
return result;
}
return result;
},
},
},
},
tags: [
{
name: 'FEATURED',