From 78e74336db1d463c43843c61a305de7f4225196c Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 31 Jul 2017 18:07:37 -0300 Subject: [PATCH 1/8] Moderator Featured Tag/Button --- .../src/components/CommentType.css | 3 +- .../routes/Moderation/components/Comment.js | 16 +++++---- .../routes/Moderation/components/styles.css | 7 ++-- .../client/components/ModTag.css | 34 +++++++++++++++++++ .../client/components/ModTag.js | 19 +++++++++++ .../client/components/Tag.js | 2 +- .../client/index.js | 4 ++- .../client/translations.yml | 4 +++ 8 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 plugins/talk-plugin-featured-comments/client/components/ModTag.css create mode 100644 plugins/talk-plugin-featured-comments/client/components/ModTag.js diff --git a/client/coral-admin/src/components/CommentType.css b/client/coral-admin/src/components/CommentType.css index beafc2a7c..b5dbc229b 100644 --- a/client/coral-admin/src/components/CommentType.css +++ b/client/coral-admin/src/components/CommentType.css @@ -2,12 +2,11 @@ display: inline-block; color: white; background: grey; - height: 32px; box-sizing: border-box; - line-height: 29px; padding: 2px 8px; border-radius: 2px; font-size: 12px; + height: 28px; > i { font-size: 14px; diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index e37fa887c..7bbecb319 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -88,15 +88,17 @@ class Comment extends React.Component { } - +
+ + +
- +
Story: {comment.asset.title} {!props.currentAsset && diff --git a/client/coral-admin/src/routes/Moderation/components/styles.css b/client/coral-admin/src/routes/Moderation/components/styles.css index 948f72158..ccc762641 100644 --- a/client/coral-admin/src/routes/Moderation/components/styles.css +++ b/client/coral-admin/src/routes/Moderation/components/styles.css @@ -493,7 +493,10 @@ span { top: .3em; } -.commentType { +.adminCommentInfoBar { + min-width: 100px; position: absolute; right: 0px; -} + top: 0px; + text-align: right; +} \ No newline at end of file diff --git a/plugins/talk-plugin-featured-comments/client/components/ModTag.css b/plugins/talk-plugin-featured-comments/client/components/ModTag.css new file mode 100644 index 000000000..06b2fdaec --- /dev/null +++ b/plugins/talk-plugin-featured-comments/client/components/ModTag.css @@ -0,0 +1,34 @@ +.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; +} + +.tagIcon { + margin-right: 5px; + font-size: 15px; + vertical-align: text-bottom; +} + \ No newline at end of file diff --git a/plugins/talk-plugin-featured-comments/client/components/ModTag.js b/plugins/talk-plugin-featured-comments/client/components/ModTag.js new file mode 100644 index 000000000..6a63cd3a8 --- /dev/null +++ b/plugins/talk-plugin-featured-comments/client/components/ModTag.js @@ -0,0 +1,19 @@ +import React from 'react'; +import cn from 'classnames'; +import styles from './ModTag.css'; +import {t} from 'plugin-api/beta/client/services'; + +// import {isTagged} from 'plugin-api/beta/client/utils'; +import {Icon} from 'plugin-api/beta/client/components/ui'; + +export default class Tag extends React.Component { + render() { + + // isTagged(this.props.comment.tags, 'FEATURED') + return( + + {t('talk-plugin-featured-comments.feature')} + + ); + } +} diff --git a/plugins/talk-plugin-featured-comments/client/components/Tag.js b/plugins/talk-plugin-featured-comments/client/components/Tag.js index ee2d13b19..c912c61b1 100644 --- a/plugins/talk-plugin-featured-comments/client/components/Tag.js +++ b/plugins/talk-plugin-featured-comments/client/components/Tag.js @@ -15,7 +15,7 @@ export default class Tag extends React.Component { } - showTooltip = e => { + showTooltip = (e) => { e.preventDefault(); this.setState({ tooltip: true diff --git a/plugins/talk-plugin-featured-comments/client/index.js b/plugins/talk-plugin-featured-comments/client/index.js index 8e4d18058..c63647aba 100644 --- a/plugins/talk-plugin-featured-comments/client/index.js +++ b/plugins/talk-plugin-featured-comments/client/index.js @@ -5,6 +5,7 @@ import TabPane from './containers/TabPane'; import translations from './translations.yml'; import update from 'immutability-helper'; import reducer from './reducer'; +import ModTag from './components/ModTag'; import {findCommentInEmbedQuery} from 'coral-embed-stream/src/graphql/utils'; import {insertCommentsSorted} from 'plugin-api/beta/client/utils'; @@ -16,7 +17,8 @@ export default { streamTabs: [Tab], streamTabPanes: [TabPane], commentInfoBar: [Tag], - commentReactions: [Button] + commentReactions: [Button], + adminCommentInfoBar: [ModTag] }, mutations: { IgnoreUser: ({variables}) => ({ diff --git a/plugins/talk-plugin-featured-comments/client/translations.yml b/plugins/talk-plugin-featured-comments/client/translations.yml index 50e51d586..4505782f8 100644 --- a/plugins/talk-plugin-featured-comments/client/translations.yml +++ b/plugins/talk-plugin-featured-comments/client/translations.yml @@ -1,11 +1,15 @@ 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 es: talk-plugin-featured-comments: + un_feature: Desmarcar + feature: Remarcar featured: Remarcado featured_comments: Comentarios Remarcados go_to_conversation: Ir al comentario From 5266bfa6477c2c7d9caff64a9c64deea917954a3 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 1 Aug 2017 12:38:41 -0300 Subject: [PATCH 2/8] Components, UI/UX and Styling --- .../client/components/ModTag.css | 8 ++++ .../client/components/ModTag.js | 44 ++++++++++++++++--- .../client/containers/ModTag.js | 15 +++++++ .../client/index.js | 2 +- 4 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 plugins/talk-plugin-featured-comments/client/containers/ModTag.js diff --git a/plugins/talk-plugin-featured-comments/client/components/ModTag.css b/plugins/talk-plugin-featured-comments/client/components/ModTag.css index 06b2fdaec..c8ba1b6ba 100644 --- a/plugins/talk-plugin-featured-comments/client/components/ModTag.css +++ b/plugins/talk-plugin-featured-comments/client/components/ModTag.css @@ -26,6 +26,14 @@ color: white; } +.tag.featured:hover { + background-color: white; + border-color: #5384B2; + color: #5384B2; + cursor: pointer; +} + + .tagIcon { margin-right: 5px; font-size: 15px; diff --git a/plugins/talk-plugin-featured-comments/client/components/ModTag.js b/plugins/talk-plugin-featured-comments/client/components/ModTag.js index 6a63cd3a8..3595f23f5 100644 --- a/plugins/talk-plugin-featured-comments/client/components/ModTag.js +++ b/plugins/talk-plugin-featured-comments/client/components/ModTag.js @@ -2,17 +2,49 @@ import React from 'react'; import cn from 'classnames'; import styles from './ModTag.css'; import {t} from 'plugin-api/beta/client/services'; +import {isTagged} from 'plugin-api/beta/client/utils'; // import {isTagged} from 'plugin-api/beta/client/utils'; import {Icon} from 'plugin-api/beta/client/components/ui'; -export default class Tag extends React.Component { - render() { +export default class ModTag extends React.Component { + constructor() { + super(); - // isTagged(this.props.comment.tags, 'FEATURED') - return( - - {t('talk-plugin-featured-comments.feature')} + this.state = { + on: false + }; + + } + + handleMouseEnter = (e) => { + e.preventDefault(); + this.setState({ + on: true + }); + } + + handleMouseLeave = (e) => { + e.preventDefault(); + this.setState({ + on: false + }); + } + + render() { + const isFeatured = isTagged(this.props.comment.tags, 'FEATURED'); + + return isFeatured ? ( + + + {!this.state.on ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.un_feature')} + + ) : ( + + + {isFeatured ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.feature')} ); } diff --git a/plugins/talk-plugin-featured-comments/client/containers/ModTag.js b/plugins/talk-plugin-featured-comments/client/containers/ModTag.js new file mode 100644 index 000000000..827bc1c9d --- /dev/null +++ b/plugins/talk-plugin-featured-comments/client/containers/ModTag.js @@ -0,0 +1,15 @@ +import {gql} from 'react-apollo'; +import ModTag from '../components/ModTag'; +import {withFragments} from 'plugin-api/beta/client/hocs'; + +export default withFragments({ + comment: gql` + fragment TalkFeaturedComments_ModTab_comment on Comment { + tags { + tag { + name + } + } + } + ` +})(ModTag); diff --git a/plugins/talk-plugin-featured-comments/client/index.js b/plugins/talk-plugin-featured-comments/client/index.js index c63647aba..a1a6c35a7 100644 --- a/plugins/talk-plugin-featured-comments/client/index.js +++ b/plugins/talk-plugin-featured-comments/client/index.js @@ -5,7 +5,7 @@ import TabPane from './containers/TabPane'; import translations from './translations.yml'; import update from 'immutability-helper'; import reducer from './reducer'; -import ModTag from './components/ModTag'; +import ModTag from './containers/ModTag'; import {findCommentInEmbedQuery} from 'coral-embed-stream/src/graphql/utils'; import {insertCommentsSorted} from 'plugin-api/beta/client/utils'; From 779260c245e97d240851eadcb45c75c646db9462 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 1 Aug 2017 13:06:21 -0300 Subject: [PATCH 3/8] Working featured comments on the admin side --- .../src/routes/Moderation/components/Comment.js | 4 +++- .../client/components/ModTag.js | 15 +++++++-------- .../client/containers/ModTag.js | 15 ++------------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index 7bbecb319..60983b018 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -93,8 +93,10 @@ class Comment extends React.Component { + />
diff --git a/plugins/talk-plugin-featured-comments/client/components/ModTag.js b/plugins/talk-plugin-featured-comments/client/components/ModTag.js index 3595f23f5..f4dbcd614 100644 --- a/plugins/talk-plugin-featured-comments/client/components/ModTag.js +++ b/plugins/talk-plugin-featured-comments/client/components/ModTag.js @@ -2,9 +2,6 @@ import React from 'react'; import cn from 'classnames'; import styles from './ModTag.css'; import {t} from 'plugin-api/beta/client/services'; -import {isTagged} from 'plugin-api/beta/client/utils'; - -// import {isTagged} from 'plugin-api/beta/client/utils'; import {Icon} from 'plugin-api/beta/client/components/ui'; export default class ModTag extends React.Component { @@ -32,19 +29,21 @@ export default class ModTag extends React.Component { } render() { - const isFeatured = isTagged(this.props.comment.tags, 'FEATURED'); + const {alreadyTagged, deleteTag, postTag} = this.props; - return isFeatured ? ( + return alreadyTagged ? ( + onMouseLeave={this.handleMouseLeave} > {!this.state.on ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.un_feature')} ) : ( - + - {isFeatured ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.feature')} + {alreadyTagged ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.feature')} ); } diff --git a/plugins/talk-plugin-featured-comments/client/containers/ModTag.js b/plugins/talk-plugin-featured-comments/client/containers/ModTag.js index 827bc1c9d..978700275 100644 --- a/plugins/talk-plugin-featured-comments/client/containers/ModTag.js +++ b/plugins/talk-plugin-featured-comments/client/containers/ModTag.js @@ -1,15 +1,4 @@ -import {gql} from 'react-apollo'; import ModTag from '../components/ModTag'; -import {withFragments} from 'plugin-api/beta/client/hocs'; +import {withTags} from 'plugin-api/beta/client/hocs'; -export default withFragments({ - comment: gql` - fragment TalkFeaturedComments_ModTab_comment on Comment { - tags { - tag { - name - } - } - } - ` -})(ModTag); +export default withTags('featured')(ModTag); From 4e5f1528f998d2bb0c9938853bfa3fb041f5a510 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 1 Aug 2017 18:27:02 -0300 Subject: [PATCH 4/8] BE Subscription for Featured Comments --- .../Moderation/containers/Moderation.js | 25 ++++++++++++++++++- graph/mutators/tag.js | 6 ++++- graph/resolvers/subscription.js | 3 +++ graph/subscriptions.js | 11 ++++++++ graph/typeDefs.graphql | 4 +++ perms/constants.js | 1 + perms/subscriptionReducer.js | 2 ++ 7 files changed, 50 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index 19cb0e8a1..6e849ac0e 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -103,7 +103,21 @@ class ModerationContainer extends Component { }, }); - this.subscriptions.push(sub1, sub2, sub3, sub4); + const sub5 = this.props.data.subscribeToMore({ + document: COMMENT_FEATURED_SUBSCRIPTION, + variables, + updateQuery: (prev, {subscriptionData: {data: {commentFeatured: comment}}}) => { + const sort = this.props.moderation.sortOrder; + const notify = { + activeQueue: this.activeTab, + text: 'New Featured Comment', + anyQueue: true, + }; + return handleCommentChange(prev, comment, sort, notify); + }, + }); + + this.subscriptions.push(sub1, sub2, sub3, sub4, sub5); } unsubscribe() { @@ -217,6 +231,15 @@ class ModerationContainer extends Component { } } +const COMMENT_FEATURED_SUBSCRIPTION = gql` + subscription CommentFeatured($asset_id: ID){ + commentFeatured(asset_id: $asset_id){ + ...${getDefinitionName(Comment.fragments.comment)} + } + } + ${Comment.fragments.comment} +`; + const COMMENT_EDITED_SUBSCRIPTION = gql` subscription CommentEdited($asset_id: ID){ commentEdited(asset_id: $asset_id){ diff --git a/graph/mutators/tag.js b/graph/mutators/tag.js index dc2f96a68..b37c79a11 100644 --- a/graph/mutators/tag.js +++ b/graph/mutators/tag.js @@ -5,7 +5,11 @@ const {ADD_COMMENT_TAG, REMOVE_COMMENT_TAG} = require('../../perms/constants'); /** * Modifies the targeted model with the specified operation to add/remove a tag. */ -const modify = async ({user, loaders: {Tags}}, operation, {name, id, item_type, asset_id}) => { +const modify = async ({user, loaders: {Tags}, pubsub}, operation, {name, id, item_type, asset_id}) => { + + if (operation.name === 'add') { + pubsub.publish('commentFeatured', {}); + } // Get the global list of tags from the dataloader. const tags = await Tags.getAll.load({id, item_type, asset_id}); diff --git a/graph/resolvers/subscription.js b/graph/resolvers/subscription.js index f6da9fb54..e86b9cea4 100644 --- a/graph/resolvers/subscription.js +++ b/graph/resolvers/subscription.js @@ -14,6 +14,9 @@ const Subscription = { commentFlagged(comment) { return comment; }, + commentFeatured(comment) { + return comment; + }, userBanned(user) { return user; }, diff --git a/graph/subscriptions.js b/graph/subscriptions.js index adac849e0..26199dc24 100644 --- a/graph/subscriptions.js +++ b/graph/subscriptions.js @@ -24,6 +24,7 @@ const { SUBSCRIBE_ALL_USER_SUSPENDED, SUBSCRIBE_ALL_USER_BANNED, SUBSCRIBE_ALL_USERNAME_REJECTED, + SUBSCRIBE_COMMENT_FEATURED } = require('../perms/constants'); /** @@ -86,6 +87,16 @@ const setupFunctions = plugins.get('server', 'setupFunctions').reduce((acc, {plu } }, }), + commentFeatured: (options, args) => ({ + commentFeatured: { + filter: (comment, context) => { + if (!args.asset_id && (!context.user || !context.user.can(SUBSCRIBE_COMMENT_FEATURED))) { + return false; + } + return !args.asset_id || comment.asset_id === args.asset_id; + } + }, + }), userSuspended: (options, args) => ({ userSuspended: { filter: (user, context) => { diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 999aea51c..dc34ffa40 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -1046,6 +1046,10 @@ type Subscription { # Requires the `ADMIN` or `MODERATOR` role. commentFlagged(asset_id: ID): Comment + # Get an update whenever a comment is featured. + # Requires the `ADMIN` or `MODERATOR` role. + commentFeatured(asset_id: ID): Comment + # Get an update whenever a comment has been accepted. # Requires the `ADMIN` or `MODERATOR` role. commentAccepted(asset_id: ID): Comment diff --git a/perms/constants.js b/perms/constants.js index 383f36daf..f05540d2b 100644 --- a/perms/constants.js +++ b/perms/constants.js @@ -37,4 +37,5 @@ module.exports = { SUBSCRIBE_ALL_USER_SUSPENDED: 'SUBSCRIBE_ALL_USER_SUSPENDED', SUBSCRIBE_ALL_USER_BANNED: 'SUBSCRIBE_ALL_USER_BANNED', SUBSCRIBE_ALL_USERNAME_REJECTED: 'SUBSCRIBE_ALL_USERNAME_REJECTED', + SUBSCRIBE_COMMENT_FEATURED: 'SUBSCRIBE_COMMENT_FEATURED' }; diff --git a/perms/subscriptionReducer.js b/perms/subscriptionReducer.js index e0f73526a..a88bec66c 100644 --- a/perms/subscriptionReducer.js +++ b/perms/subscriptionReducer.js @@ -19,6 +19,8 @@ module.exports = (user, perm) => { return check(user, ['ADMIN', 'MODERATOR']); case types.SUBSCRIBE_ALL_USERNAME_REJECTED: return check(user, ['ADMIN', 'MODERATOR']); + case types.SUBSCRIBE_COMMENT_FEATURED: + return check(user, ['ADMIN', 'MODERATOR']); default: break; } From b6b7570bcc57477226bbd6553c42e57f04db1a63 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 2 Aug 2017 18:14:52 +0700 Subject: [PATCH 5/8] Remove subscription code --- .../Moderation/containers/Moderation.js | 29 ++----------------- graph/mutators/tag.js | 4 --- graph/resolvers/subscription.js | 3 -- graph/subscriptions.js | 11 ------- graph/typeDefs.graphql | 4 --- perms/constants.js | 1 - perms/subscriptionReducer.js | 2 -- .../client/containers/ModTag.js | 1 + 8 files changed, 4 insertions(+), 51 deletions(-) diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index 41f95e2e0..f02c148cf 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -38,7 +38,7 @@ function prepareNotificationText(text) { class ModerationContainer extends Component { subscriptions = []; - get activeTab() { + get activeTab() { const {root: {asset, settings}, router, route} = this.props; @@ -47,7 +47,7 @@ class ModerationContainer extends Component { const queue = isPremod(premod) ? 'premod' : 'new'; const activeTab = route.path && route.path !== ':id' ? route.path : queue; - + return activeTab; } @@ -115,21 +115,7 @@ class ModerationContainer extends Component { }, }); - const sub5 = this.props.data.subscribeToMore({ - document: COMMENT_FEATURED_SUBSCRIPTION, - variables, - updateQuery: (prev, {subscriptionData: {data: {commentFeatured: comment}}}) => { - const sort = this.props.moderation.sortOrder; - const notify = { - activeQueue: this.activeTab, - text: 'New Featured Comment', - anyQueue: true, - }; - return handleCommentChange(prev, comment, sort, notify); - }, - }); - - this.subscriptions.push(sub1, sub2, sub3, sub4, sub5); + this.subscriptions.push(sub1, sub2, sub3, sub4); } unsubscribe() { @@ -248,15 +234,6 @@ class ModerationContainer extends Component { } } -const COMMENT_FEATURED_SUBSCRIPTION = gql` - subscription CommentFeatured($asset_id: ID){ - commentFeatured(asset_id: $asset_id){ - ...${getDefinitionName(Comment.fragments.comment)} - } - } - ${Comment.fragments.comment} -`; - const COMMENT_EDITED_SUBSCRIPTION = gql` subscription CommentEdited($asset_id: ID){ commentEdited(asset_id: $asset_id){ diff --git a/graph/mutators/tag.js b/graph/mutators/tag.js index b37c79a11..6ce43c772 100644 --- a/graph/mutators/tag.js +++ b/graph/mutators/tag.js @@ -7,10 +7,6 @@ const {ADD_COMMENT_TAG, REMOVE_COMMENT_TAG} = require('../../perms/constants'); */ const modify = async ({user, loaders: {Tags}, pubsub}, operation, {name, id, item_type, asset_id}) => { - if (operation.name === 'add') { - pubsub.publish('commentFeatured', {}); - } - // Get the global list of tags from the dataloader. const tags = await Tags.getAll.load({id, item_type, asset_id}); diff --git a/graph/resolvers/subscription.js b/graph/resolvers/subscription.js index e86b9cea4..f6da9fb54 100644 --- a/graph/resolvers/subscription.js +++ b/graph/resolvers/subscription.js @@ -14,9 +14,6 @@ const Subscription = { commentFlagged(comment) { return comment; }, - commentFeatured(comment) { - return comment; - }, userBanned(user) { return user; }, diff --git a/graph/subscriptions.js b/graph/subscriptions.js index 26199dc24..adac849e0 100644 --- a/graph/subscriptions.js +++ b/graph/subscriptions.js @@ -24,7 +24,6 @@ const { SUBSCRIBE_ALL_USER_SUSPENDED, SUBSCRIBE_ALL_USER_BANNED, SUBSCRIBE_ALL_USERNAME_REJECTED, - SUBSCRIBE_COMMENT_FEATURED } = require('../perms/constants'); /** @@ -87,16 +86,6 @@ const setupFunctions = plugins.get('server', 'setupFunctions').reduce((acc, {plu } }, }), - commentFeatured: (options, args) => ({ - commentFeatured: { - filter: (comment, context) => { - if (!args.asset_id && (!context.user || !context.user.can(SUBSCRIBE_COMMENT_FEATURED))) { - return false; - } - return !args.asset_id || comment.asset_id === args.asset_id; - } - }, - }), userSuspended: (options, args) => ({ userSuspended: { filter: (user, context) => { diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index dc34ffa40..999aea51c 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -1046,10 +1046,6 @@ type Subscription { # Requires the `ADMIN` or `MODERATOR` role. commentFlagged(asset_id: ID): Comment - # Get an update whenever a comment is featured. - # Requires the `ADMIN` or `MODERATOR` role. - commentFeatured(asset_id: ID): Comment - # Get an update whenever a comment has been accepted. # Requires the `ADMIN` or `MODERATOR` role. commentAccepted(asset_id: ID): Comment diff --git a/perms/constants.js b/perms/constants.js index f05540d2b..383f36daf 100644 --- a/perms/constants.js +++ b/perms/constants.js @@ -37,5 +37,4 @@ module.exports = { SUBSCRIBE_ALL_USER_SUSPENDED: 'SUBSCRIBE_ALL_USER_SUSPENDED', SUBSCRIBE_ALL_USER_BANNED: 'SUBSCRIBE_ALL_USER_BANNED', SUBSCRIBE_ALL_USERNAME_REJECTED: 'SUBSCRIBE_ALL_USERNAME_REJECTED', - SUBSCRIBE_COMMENT_FEATURED: 'SUBSCRIBE_COMMENT_FEATURED' }; diff --git a/perms/subscriptionReducer.js b/perms/subscriptionReducer.js index a88bec66c..e0f73526a 100644 --- a/perms/subscriptionReducer.js +++ b/perms/subscriptionReducer.js @@ -19,8 +19,6 @@ module.exports = (user, perm) => { return check(user, ['ADMIN', 'MODERATOR']); case types.SUBSCRIBE_ALL_USERNAME_REJECTED: return check(user, ['ADMIN', 'MODERATOR']); - case types.SUBSCRIBE_COMMENT_FEATURED: - return check(user, ['ADMIN', 'MODERATOR']); default: break; } diff --git a/plugins/talk-plugin-featured-comments/client/containers/ModTag.js b/plugins/talk-plugin-featured-comments/client/containers/ModTag.js index 978700275..3b564d127 100644 --- a/plugins/talk-plugin-featured-comments/client/containers/ModTag.js +++ b/plugins/talk-plugin-featured-comments/client/containers/ModTag.js @@ -2,3 +2,4 @@ import ModTag from '../components/ModTag'; import {withTags} from 'plugin-api/beta/client/hocs'; export default withTags('featured')(ModTag); + From da0d0349ddd7715a875f7a433e2977256ee40f34 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 2 Aug 2017 21:34:48 +0700 Subject: [PATCH 6/8] Implement approve and live updates --- .../Moderation/components/Moderation.js | 11 ++- client/coral-framework/hocs/withMutation.js | 2 +- graph/mutators/comment.js | 10 --- graph/resolvers/root_mutation.js | 14 +++- .../client/containers/ModSubscription.js | 70 +++++++++++++++++++ .../client/index.js | 4 +- .../client/translations.yml | 3 +- .../talk-plugin-featured-comments/index.js | 44 ++++++++++++ 8 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js diff --git a/client/coral-admin/src/routes/Moderation/components/Moderation.js b/client/coral-admin/src/routes/Moderation/components/Moderation.js index a69100f9d..ce0c31e33 100644 --- a/client/coral-admin/src/routes/Moderation/components/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/components/Moderation.js @@ -7,6 +7,7 @@ import ModerationMenu from './ModerationMenu'; import ModerationHeader from './ModerationHeader'; import ModerationKeysModal from '../../../components/ModerationKeysModal'; import StorySearch from '../containers/StorySearch'; +import Slot from 'coral-framework/components/Slot'; export default class Moderation extends Component { constructor() { @@ -100,7 +101,7 @@ export default class Moderation extends Component { } render () { - const {root, moderation, settings, viewUserDetail, hideUserDetail, activeTab, getModPath, premodEnabled, ...props} = this.props; + const {root, data, moderation, settings, viewUserDetail, hideUserDetail, activeTab, getModPath, premodEnabled, ...props} = this.props; const assetId = this.props.params.id; const {asset} = root; @@ -184,6 +185,14 @@ export default class Moderation extends Component { closeSearch={this.closeSearch} storySearchChange={this.props.storySearchChange} /> + + ); } diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index 4488b0e7f..c9108c0fe 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -129,7 +129,7 @@ export default (document, config = {}) => (WrappedComponent) => { }) .catch((error) => { this.context.eventEmitter.emit(`mutation.${name}.error`, {variables, error}); - throw new error; + throw error; }); }; return config.props({...data, mutate}); diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 539bd674e..533032412 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -350,16 +350,6 @@ const setStatus = async ({user, loaders: {Comments}, pubsub}, {id, status}) => { // adjust the affected user's karma in the next tick. process.nextTick(adjustKarma(Comments, id, status)); - if (status === 'ACCEPTED') { - - // Publish the comment status change via the subscription. - pubsub.publish('commentAccepted', comment); - } else if (status === 'REJECTED') { - - // Publish the comment status change via the subscription. - pubsub.publish('commentRejected', comment); - } - return comment; }; diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js index dec0553a4..685c26a17 100644 --- a/graph/resolvers/root_mutation.js +++ b/graph/resolvers/root_mutation.js @@ -31,8 +31,18 @@ const RootMutation = { stopIgnoringUser(_, {id}, {mutators: {User}}) { return wrapResponse(null)(User.stopIgnoringUser({id})); }, - setCommentStatus(_, {id, status}, {mutators: {Comment}}) { - return wrapResponse(null)(Comment.setStatus({id, status})); + async setCommentStatus(_, {id, status}, {mutators: {Comment}, pubsub}) { + const comment = await Comment.setStatus({id, status}); + if (status === 'ACCEPTED') { + + // Publish the comment status change via the subscription. + pubsub.publish('commentAccepted', comment); + } else if (status === 'REJECTED') { + + // Publish the comment status change via the subscription. + pubsub.publish('commentRejected', comment); + } + return wrapResponse(null)(comment); }, addTag(_, {tag}, {mutators: {Tag}}) { return wrapResponse(null)(Tag.add(tag)); diff --git a/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js b/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js new file mode 100644 index 000000000..c65415c91 --- /dev/null +++ b/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js @@ -0,0 +1,70 @@ +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 { + unsubscribe = null; + + componentWillMount() { + this.unsubscribe = this.props.data.subscribeToMore({ + document: COMMENT_FEATURED_SUBSCRIPTION, + variables: { + assetId: this.props.data.variables.asset_id, + }, + updateQuery: (prev, {subscriptionData: {data: {commentFeatured: comment}}}) => { + const sort = this.props.data.variables.sort; + const user = comment.status_history[comment.status_history.length - 1].assigned_by; + const notify = { + activeQueue: this.props.activeTab, + text: t( + 'talk-plugin-featured-comments.notify_featured', + user.username, + prepareNotificationText(comment.body) + ), + anyQueue: true, + }; + return handleCommentChange(prev, comment, sort, notify); + }, + }); + } + + componentWillUnmount() { + this.unsubscribe(); + } + + render() { + return null; + } +} + +const COMMENT_FEATURED_SUBSCRIPTION = gql` + subscription CommentFeatured($assetId: ID){ + commentFeatured(asset_id: $assetId){ + ...${getDefinitionName(Comment.fragments.comment)} + status_history { + type + created_at + assigned_by { + id + username + } + } + } + } + ${Comment.fragments.comment} +`; + +const mapStateToProps = (state) => ({ + sortOrder: state.moderation.toJS().sortOrder, +}); + +export default connect(mapStateToProps, null)(ModSubscription); diff --git a/plugins/talk-plugin-featured-comments/client/index.js b/plugins/talk-plugin-featured-comments/client/index.js index a1a6c35a7..82cba8b91 100644 --- a/plugins/talk-plugin-featured-comments/client/index.js +++ b/plugins/talk-plugin-featured-comments/client/index.js @@ -6,6 +6,7 @@ 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'; @@ -18,7 +19,8 @@ export default { streamTabPanes: [TabPane], commentInfoBar: [Tag], commentReactions: [Button], - adminCommentInfoBar: [ModTag] + adminModeration: [ModSubscription], + adminCommentInfoBar: [ModTag], }, mutations: { IgnoreUser: ({variables}) => ({ diff --git a/plugins/talk-plugin-featured-comments/client/translations.yml b/plugins/talk-plugin-featured-comments/client/translations.yml index 4505782f8..953518452 100644 --- a/plugins/talk-plugin-featured-comments/client/translations.yml +++ b/plugins/talk-plugin-featured-comments/client/translations.yml @@ -6,6 +6,7 @@ en: featured_comments: Featured Comments go_to_conversation: Go to conversation tooltip_description: Comments selected by our team as worth reading + notify_featured: '{0} featured and approved comment "{1}"' es: talk-plugin-featured-comments: un_feature: Desmarcar @@ -13,4 +14,4 @@ es: 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 \ No newline at end of file + tooltip_description: Comentarios seleccionados por nuestro equipo que valen la pena ser leidos diff --git a/plugins/talk-plugin-featured-comments/index.js b/plugins/talk-plugin-featured-comments/index.js index 88f8344ae..6018cbb0d 100644 --- a/plugins/talk-plugin-featured-comments/index.js +++ b/plugins/talk-plugin-featured-comments/index.js @@ -1,4 +1,48 @@ +const {check} = require('perms/utils'); + module.exports = { + typeDefs: ` + type Subscription { + + # Subscribe to featured comments. + commentFeatured(asset_id: ID): Comment + } + `, + resolvers: { + Subscription: { + commentFeatured: ({comment}) => { + return 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; + }, + }, + }), + }, + hooks: { + RootMutation: { + addTag: { + async post(obj, {tag: {name, id, item_type}}, {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}); + } + return result; + } + return result; + }, + }, + }, + }, tags: [ { name: 'FEATURED', From 2e974ac82fc09783b31ba88bb94f92a1a15d023b Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 2 Aug 2017 22:29:01 +0700 Subject: [PATCH 7/8] Live update unfeatured --- .../client/containers/ModSubscription.js | 100 ++++++++++++------ .../client/translations.yml | 1 + .../talk-plugin-featured-comments/index.js | 49 ++++++++- 3 files changed, 115 insertions(+), 35 deletions(-) diff --git a/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js b/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js index c65415c91..647c7ecfd 100644 --- a/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js +++ b/plugins/talk-plugin-featured-comments/client/containers/ModSubscription.js @@ -12,33 +12,60 @@ function prepareNotificationText(text) { } class ModSubscription extends React.Component { - unsubscribe = null; + subscriptions = null; componentWillMount() { - this.unsubscribe = this.props.data.subscribeToMore({ - document: COMMENT_FEATURED_SUBSCRIPTION, - variables: { - assetId: this.props.data.variables.asset_id, + 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); + }, }, - updateQuery: (prev, {subscriptionData: {data: {commentFeatured: comment}}}) => { - const sort = this.props.data.variables.sort; - const user = comment.status_history[comment.status_history.length - 1].assigned_by; - const notify = { - activeQueue: this.props.activeTab, - text: t( - 'talk-plugin-featured-comments.notify_featured', - user.username, - prepareNotificationText(comment.body) - ), - 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.unsubscribe(); + this.subscriptions.forEach((unsubscribe) => unsubscribe()); } render() { @@ -48,15 +75,28 @@ class ModSubscription extends React.Component { const COMMENT_FEATURED_SUBSCRIPTION = gql` subscription CommentFeatured($assetId: ID){ - commentFeatured(asset_id: $assetId){ - ...${getDefinitionName(Comment.fragments.comment)} - status_history { - type - created_at - assigned_by { - id - username - } + 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 } } } @@ -64,7 +104,7 @@ const COMMENT_FEATURED_SUBSCRIPTION = gql` `; const mapStateToProps = (state) => ({ - sortOrder: state.moderation.toJS().sortOrder, + user: state.auth.toJS().user, }); export default connect(mapStateToProps, null)(ModSubscription); diff --git a/plugins/talk-plugin-featured-comments/client/translations.yml b/plugins/talk-plugin-featured-comments/client/translations.yml index 953518452..69f38f8e1 100644 --- a/plugins/talk-plugin-featured-comments/client/translations.yml +++ b/plugins/talk-plugin-featured-comments/client/translations.yml @@ -7,6 +7,7 @@ en: go_to_conversation: Go to conversation tooltip_description: Comments selected by our team as worth reading notify_featured: '{0} featured and approved comment "{1}"' + notify_unfeatured: '{0} unfeatured comment "{1}"' es: talk-plugin-featured-comments: un_feature: Desmarcar diff --git a/plugins/talk-plugin-featured-comments/index.js b/plugins/talk-plugin-featured-comments/index.js index 6018cbb0d..46e3f97fa 100644 --- a/plugins/talk-plugin-featured-comments/index.js +++ b/plugins/talk-plugin-featured-comments/index.js @@ -2,16 +2,33 @@ 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): Comment + commentFeatured(asset_id: ID): CommentFeaturedData + + # Subscribe to featured comments. + commentUnfeatured(asset_id: ID): CommentUnfeaturedData } `, resolvers: { Subscription: { - commentFeatured: ({comment}) => { - return comment; + commentFeatured: ({user, comment}) => { + return {user, comment}; + }, + commentUnfeatured: ({user, comment}) => { + return {user, comment}; }, }, }, @@ -26,15 +43,37 @@ module.exports = { }, }, }), + 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}}, {mutators: {Comment}, pubsub}, info, result) { + 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}); + 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; } From e6b7fcbd9d079d0953330ce554994baf87781e8c Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 2 Aug 2017 23:41:26 +0700 Subject: [PATCH 8/8] Show success notification --- .../client/components/ModTag.js | 21 ++++++++++++++----- .../client/translations.yml | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/plugins/talk-plugin-featured-comments/client/components/ModTag.js b/plugins/talk-plugin-featured-comments/client/components/ModTag.js index f4dbcd614..a80c3a8a6 100644 --- a/plugins/talk-plugin-featured-comments/client/components/ModTag.js +++ b/plugins/talk-plugin-featured-comments/client/components/ModTag.js @@ -3,6 +3,7 @@ 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() { @@ -27,9 +28,19 @@ export default class ModTag extends React.Component { 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, postTag} = this.props; + const {alreadyTagged, deleteTag} = this.props; return alreadyTagged ? ( - {!this.state.on ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.un_feature')} + {!this.state.on ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.un_feature')} ) : ( + onClick={this.postTag} > - {alreadyTagged ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.feature')} + {alreadyTagged ? t('talk-plugin-featured-comments.featured') : t('talk-plugin-featured-comments.feature')} ); } diff --git a/plugins/talk-plugin-featured-comments/client/translations.yml b/plugins/talk-plugin-featured-comments/client/translations.yml index 69f38f8e1..ea504cc9e 100644 --- a/plugins/talk-plugin-featured-comments/client/translations.yml +++ b/plugins/talk-plugin-featured-comments/client/translations.yml @@ -6,6 +6,7 @@ en: 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: