diff --git a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx index 32885225d..c2f8d79de 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx +++ b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx @@ -44,12 +44,16 @@ const ModerationActionsContainer: FunctionComponent = ({ } approve({ commentID: comment.id, commentRevisionID: comment.revision.id }); }, [approve, comment]); - const onReject = useCallback(() => { + const onReject = useCallback(async () => { if (!comment.revision) { return; } - reject({ commentID: comment.id, commentRevisionID: comment.revision.id }); - }, [approve, comment]); + await reject({ + commentID: comment.id, + commentRevisionID: comment.revision.id, + storyID: story.id, + }); + }, [approve, comment, story]); const onFeature = useCallback(() => { if (!comment.revision) { return; diff --git a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.tsx index e44cdd689..bcccaa9a6 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.tsx +++ b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.tsx @@ -47,7 +47,11 @@ const ModerationDropdownContainer: FunctionComponent = ({ /> ) : ( - + )} ); @@ -76,6 +80,7 @@ const enhanced = withFragmentContainer({ fragment ModerationDropdownContainer_story on Story { id ...ModerationActionsContainer_story + ...UserBanPopoverContainer_story } `, viewer: graphql` diff --git a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/RejectCommentMutation.ts b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/RejectCommentMutation.ts index d7144f99c..7c3abe602 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/RejectCommentMutation.ts +++ b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/RejectCommentMutation.ts @@ -13,33 +13,54 @@ let clientMutationId = 0; const RejectCommentMutation = createMutation( "rejectComment", - (environment: Environment, input: MutationInput) => + ( + environment: Environment, + input: MutationInput & { storyID: string } + ) => commitMutationPromiseNormalized(environment, { mutation: graphql` mutation RejectCommentMutation($input: RejectCommentInput!) { rejectComment(input: $input) { comment { status + tags { + code + } + story { + commentCounts { + tags { + FEATURED + } + } + } } clientMutationId } } `, + variables: { + input: { + commentID: input.commentID, + commentRevisionID: input.commentRevisionID, + clientMutationId: (clientMutationId++).toString(), + }, + }, optimisticResponse: { rejectComment: { comment: { id: input.commentID, status: GQLCOMMENT_STATUS.REJECTED, + story: { + commentCounts: { + tags: { + FEATURED: 0, + }, + }, + }, }, clientMutationId: clientMutationId.toString(), }, }, - variables: { - input: { - ...input, - clientMutationId: (clientMutationId++).toString(), - }, - }, updater: store => { store.get(input.commentID)!.setValue("REJECT", "lastViewerAction"); }, diff --git a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx index 8d5c40065..d34bf90cc 100644 --- a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx +++ b/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx @@ -7,6 +7,7 @@ import { useCoralContext } from "coral-framework/lib/bootstrap"; import { getMessage } from "coral-framework/lib/i18n"; import { useMutation, withFragmentContainer } from "coral-framework/lib/relay"; import { UserBanPopoverContainer_comment } from "coral-stream/__generated__/UserBanPopoverContainer_comment.graphql"; +import { UserBanPopoverContainer_story } from "coral-stream/__generated__/UserBanPopoverContainer_story.graphql"; import CLASSES from "coral-stream/classes"; import { Box, Button, Flex, Typography } from "coral-ui/components"; @@ -18,10 +19,12 @@ import styles from "./UserBanPopoverContainer.css"; interface Props { onDismiss: () => void; comment: UserBanPopoverContainer_comment; + story: UserBanPopoverContainer_story; } const UserBanPopoverContainer: FunctionComponent = ({ comment, + story, onDismiss, }) => { const user = comment.author!; @@ -41,10 +44,14 @@ const UserBanPopoverContainer: FunctionComponent = ({ ), }); if (!rejected && comment.revision) { - reject({ commentID: comment.id, commentRevisionID: comment.revision.id }); + reject({ + commentID: comment.id, + commentRevisionID: comment.revision.id, + storyID: story.id, + }); } onDismiss(); - }, [user, banUser, onDismiss, localeBundles]); + }, [user, banUser, onDismiss, localeBundles, comment, story]); return ( @@ -98,6 +105,11 @@ const enhanced = withFragmentContainer({ } } `, + story: graphql` + fragment UserBanPopoverContainer_story on Story { + id + } + `, })(UserBanPopoverContainer); export default enhanced; diff --git a/src/core/client/stream/test/comments/stream/moderation.spec.tsx b/src/core/client/stream/test/comments/stream/moderation.spec.tsx index c7db3184f..ebe46f16a 100644 --- a/src/core/client/stream/test/comments/stream/moderation.spec.tsx +++ b/src/core/client/stream/test/comments/stream/moderation.spec.tsx @@ -12,7 +12,18 @@ import { import { featuredTag, moderators, settings, stories } from "../../fixtures"; import create from "./create"; -const story = stories[0]; +function createStory() { + const base = stories[0]; + + for (const edge of base.comments.edges) { + const node = edge.node; + node.story = base; + } + + return base; +} + +const story = createStory(); const firstComment = story.comments.edges[0].node; const viewer = moderators[0]; @@ -33,6 +44,7 @@ async function createTestRenderer( ), initLocalState: (localRecord, source, environment) => { localRecord.setValue(story.id, "storyID"); + if (params.initLocalState) { params.initLocalState(localRecord, source, environment); } diff --git a/src/core/server/graph/tenant/mutators/Actions.ts b/src/core/server/graph/tenant/mutators/Actions.ts index a0570fe3c..fd63c3acc 100644 --- a/src/core/server/graph/tenant/mutators/Actions.ts +++ b/src/core/server/graph/tenant/mutators/Actions.ts @@ -1,8 +1,11 @@ import TenantContext from "coral-server/graph/tenant/context"; +import { hasTag } from "coral-server/models/comment"; +import { removeTag } from "coral-server/services/comments"; import { approve, reject } from "coral-server/services/comments/moderation"; import { GQLApproveCommentInput, GQLRejectCommentInput, + GQLTAG, } from "../schema/__generated__/types"; export const Actions = (ctx: TenantContext) => ({ @@ -17,5 +20,12 @@ export const Actions = (ctx: TenantContext) => ({ commentID: input.commentID, commentRevisionID: input.commentRevisionID, moderatorID: ctx.user!.id, - }), + }).then(comment => + // if a comment was previously featured + // and is now rejected, we need to remove the + // featured tag + hasTag(comment, GQLTAG.FEATURED) + ? removeTag(ctx.mongo, ctx.tenant, comment.id, GQLTAG.FEATURED) + : comment + ), }); diff --git a/src/core/server/models/comment/helpers.ts b/src/core/server/models/comment/helpers.ts index e687ee39c..46d13adba 100644 --- a/src/core/server/models/comment/helpers.ts +++ b/src/core/server/models/comment/helpers.ts @@ -1,4 +1,7 @@ -import { GQLCOMMENT_STATUS } from "coral-server/graph/tenant/schema/__generated__/types"; +import { + GQLCOMMENT_STATUS, + GQLTAG, +} from "coral-server/graph/tenant/schema/__generated__/types"; import { Comment } from "./comment"; import { MODERATOR_STATUSES, PUBLISHED_STATUSES } from "./constants"; @@ -73,3 +76,7 @@ export function calculateRejectionRate(counts: CommentStatusCounts): number { return rejected / (published + rejected); } + +export function hasTag(comment: Pick, tag: GQLTAG) { + return comment.tags.some(v => v.type === tag); +}