diff --git a/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx index 925ac3fcc..476932000 100644 --- a/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx +++ b/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx @@ -243,11 +243,7 @@ export class CommentContainer extends Component { )} {showCaret && ( - + )} } @@ -333,7 +329,6 @@ const enhanced = withSetCommentIDMutation( ...UsernameWithPopoverContainer_viewer ...ReactionButtonContainer_viewer ...ReportButtonContainer_viewer - ...CaretContainer_viewer } `, story: graphql` diff --git a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/CaretContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/CaretContainer.tsx index ee1a91a03..b244f5310 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/CaretContainer.tsx +++ b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/CaretContainer.tsx @@ -5,7 +5,6 @@ import { graphql } from "react-relay"; import { withFragmentContainer } from "coral-framework/lib/relay"; import { CaretContainer_comment } from "coral-stream/__generated__/CaretContainer_comment.graphql"; import { CaretContainer_story } from "coral-stream/__generated__/CaretContainer_story.graphql"; -import { CaretContainer_viewer } from "coral-stream/__generated__/CaretContainer_viewer.graphql"; import { Button, ClickOutside, Icon, Popover } from "coral-ui/components"; import ModerationDropdownContainer from "./ModerationDropdownContainer"; @@ -15,7 +14,6 @@ import styles from "./CaretContainer.css"; interface Props { comment: CaretContainer_comment; story: CaretContainer_story; - viewer: CaretContainer_viewer | null; } const CaretContainer: FunctionComponent = props => { @@ -29,14 +27,12 @@ const CaretContainer: FunctionComponent = props => { id={popoverID} placement="bottom-end" description="A popover menu to moderate the comment" - body={({ toggleVisibility, scheduleUpdate }) => ( + body={({ toggleVisibility }) => ( )} @@ -76,11 +72,6 @@ const enhanced = withFragmentContainer({ ...ModerationDropdownContainer_story } `, - viewer: graphql` - fragment CaretContainer_viewer on User { - ...ModerationDropdownContainer_viewer - } - `, })(CaretContainer); export default enhanced; diff --git a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx deleted file mode 100644 index 75ccd58c2..000000000 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import cn from "classnames"; -import { Localized } from "fluent-react/compat"; -import React, { FunctionComponent, useCallback } from "react"; -import { graphql } from "react-relay"; - -import { useMutation, withFragmentContainer } from "coral-framework/lib/relay"; -import { ModerationActionsContainer_comment } from "coral-stream/__generated__/ModerationActionsContainer_comment.graphql"; -import { ModerationActionsContainer_story } from "coral-stream/__generated__/ModerationActionsContainer_story.graphql"; -import { ModerationActionsContainer_viewer } from "coral-stream/__generated__/ModerationActionsContainer_viewer.graphql"; -import { DropdownButton, DropdownDivider, Icon } from "coral-ui/components"; - -import ApproveCommentMutation from "./ApproveCommentMutation"; -import FeatureCommentMutation from "./FeatureCommentMutation"; -import RejectCommentMutation from "./RejectCommentMutation"; -import UnfeatureCommentMutation from "./UnfeatureCommentMutation"; - -import styles from "./ModerationActionsContainer.css"; - -interface Props { - comment: ModerationActionsContainer_comment; - story: ModerationActionsContainer_story; - viewer: ModerationActionsContainer_viewer | null; - onDismiss: () => void; - onBan: () => void; -} - -const ModerationActionsContainer: FunctionComponent = ({ - comment, - story, - viewer, - onDismiss, - onBan, -}) => { - const approve = useMutation(ApproveCommentMutation); - const feature = useMutation(FeatureCommentMutation); - const unfeature = useMutation(UnfeatureCommentMutation); - const reject = useMutation(RejectCommentMutation); - - const onApprove = useCallback(() => { - approve({ commentID: comment.id, commentRevisionID: comment.revision.id }); - }, [approve, comment]); - const onReject = useCallback( - () => - reject({ commentID: comment.id, commentRevisionID: comment.revision.id }), - [approve, comment] - ); - const onFeature = useCallback(() => { - feature({ - storyID: story.id, - commentID: comment.id, - commentRevisionID: comment.revision.id, - }); - onDismiss(); - }, [feature, onDismiss, story, comment]); - const onUnfeature = useCallback(() => { - unfeature({ - commentID: comment.id, - storyID: story.id, - }); - onDismiss(); - }, [unfeature, onDismiss, story, comment]); - const approved = comment.status === "APPROVED"; - const rejected = comment.status === "REJECTED"; - const featured = comment.tags.some(t => t.code === "FEATURED"); - const banned = comment.author!.status.ban.active; - const showBanOption = - viewer === null ? false : comment.author!.id !== viewer.id; - - return ( - <> - {featured ? ( - - - star - - } - className={styles.featured} - onClick={onUnfeature} - > - Un-Feature - - - ) : ( - - star_border} - onClick={onFeature} - > - Feature - - - )} - {approved ? ( - - - check - - } - className={styles.approved} - disabled - > - Approved - - - ) : ( - - check} - onClick={onApprove} - > - Approve - - - )} - {rejected ? ( - - - close - - } - className={styles.rejected} - disabled - > - Rejected - - - ) : ( - - close} - onClick={onReject} - > - Reject - - - )} - {showBanOption && ( - <> - - {banned ? ( - - - block - - } - className={styles.banned} - disabled - > - Banned - - - ) : ( - - - block - - } - onClick={onBan} - > - Ban User - - - )} - - )} - - - - Go to Moderate - - - - ); -}; - -const enhanced = withFragmentContainer({ - comment: graphql` - fragment ModerationActionsContainer_comment on Comment { - id - author { - id - status { - ban { - active - } - } - } - revision { - id - } - status - tags { - code - } - } - `, - story: graphql` - fragment ModerationActionsContainer_story on Story { - id - } - `, - viewer: graphql` - fragment ModerationActionsContainer_viewer on User { - id - } - `, -})(ModerationActionsContainer); - -export default enhanced; diff --git a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.css b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.css similarity index 61% rename from src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.css rename to src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.css index bbd380b84..7662868a2 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationActionsContainer.css +++ b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.css @@ -10,13 +10,3 @@ color: var(--palette-primary-dark); font-weight: var(--font-weight-bold); } -.banned { - color: var(--palette-error-dark); - font-weight: var(--font-weight-bold); -} -.banIcon { - width: 18px; - height: 14px; - text-align: center; - line-height: 12px; -} \ No newline at end of file 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 0c60a7f77..f83850f6e 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.tsx +++ b/src/core/client/stream/tabs/Comments/Comment/ModerationDropdown/ModerationDropdownContainer.tsx @@ -1,54 +1,153 @@ -import React, { FunctionComponent, useCallback, useState } from "react"; +import { Localized } from "fluent-react/compat"; +import React, { FunctionComponent, useCallback } from "react"; import { graphql } from "react-relay"; -import { withFragmentContainer } from "coral-framework/lib/relay"; +import { useMutation, withFragmentContainer } from "coral-framework/lib/relay"; import { ModerationDropdownContainer_comment } from "coral-stream/__generated__/ModerationDropdownContainer_comment.graphql"; import { ModerationDropdownContainer_story } from "coral-stream/__generated__/ModerationDropdownContainer_story.graphql"; -import { ModerationDropdownContainer_viewer } from "coral-stream/__generated__/ModerationDropdownContainer_viewer.graphql"; -import { Dropdown } from "coral-ui/components"; +import { + Dropdown, + DropdownButton, + DropdownDivider, + Icon, +} from "coral-ui/components"; -import UserBanPopoverContainer from "../UserBanPopover/UserBanPopoverContainer"; -import ModerationActionsContainer from "./ModerationActionsContainer"; +import ApproveCommentMutation from "./ApproveCommentMutation"; +import FeatureCommentMutation from "./FeatureCommentMutation"; +import RejectCommentMutation from "./RejectCommentMutation"; +import UnfeatureCommentMutation from "./UnfeatureCommentMutation"; -type View = "MODERATE" | "BAN"; +import styles from "./ModerationDropdownContainer.css"; interface Props { comment: ModerationDropdownContainer_comment; story: ModerationDropdownContainer_story; - viewer: ModerationDropdownContainer_viewer | null; onDismiss: () => void; - scheduleUpdate: () => void; } const ModerationDropdownContainer: FunctionComponent = ({ comment, story, - viewer, onDismiss, - scheduleUpdate, }) => { - const [view, setView] = useState("MODERATE"); - const onBan = useCallback(() => { - setView("BAN"); - scheduleUpdate(); - }, [setView, scheduleUpdate]); + const approve = useMutation(ApproveCommentMutation); + const feature = useMutation(FeatureCommentMutation); + const unfeature = useMutation(UnfeatureCommentMutation); + const reject = useMutation(RejectCommentMutation); + + const onApprove = useCallback(() => { + approve({ commentID: comment.id, commentRevisionID: comment.revision.id }); + }, [approve, comment]); + const onReject = useCallback( + () => + reject({ commentID: comment.id, commentRevisionID: comment.revision.id }), + [approve, comment] + ); + const onFeature = useCallback(() => { + feature({ + storyID: story.id, + commentID: comment.id, + commentRevisionID: comment.revision.id, + }); + onDismiss(); + }, [feature, comment]); + const onUnfeature = useCallback(() => { + unfeature({ + commentID: comment.id, + storyID: story.id, + }); + onDismiss(); + }, [unfeature, comment]); + + const approved = comment.status === "APPROVED"; + const rejected = comment.status === "REJECTED"; + const featured = comment.tags.some(t => t.code === "FEATURED"); return ( -
- {view === "MODERATE" ? ( - - - + + {featured ? ( + + + star + + } + className={styles.featured} + onClick={onUnfeature} + > + Un-Feature + + ) : ( - + + star_border} + onClick={onFeature} + > + Feature + + )} -
+ {approved ? ( + + + check + + } + className={styles.approved} + disabled + > + Approved + + + ) : ( + + check} + onClick={onApprove} + > + Approve + + + )} + {rejected ? ( + + + close + + } + className={styles.rejected} + disabled + > + Rejected + + + ) : ( + + close} + onClick={onReject} + > + Reject + + + )} + + + + Go to Moderate + + + ); }; @@ -56,11 +155,6 @@ const enhanced = withFragmentContainer({ comment: graphql` fragment ModerationDropdownContainer_comment on Comment { id - author { - id - username - ...UserBanPopoverContainer_user - } revision { id } @@ -68,18 +162,11 @@ const enhanced = withFragmentContainer({ tags { code } - ...ModerationActionsContainer_comment } `, story: graphql` fragment ModerationDropdownContainer_story on Story { id - ...ModerationActionsContainer_story - } - `, - viewer: graphql` - fragment ModerationDropdownContainer_viewer on User { - ...ModerationActionsContainer_viewer } `, })(ModerationDropdownContainer); diff --git a/src/core/client/stream/tabs/Comments/Comment/ReplyCommentForm/CreateCommentReplyMutation.ts b/src/core/client/stream/tabs/Comments/Comment/ReplyCommentForm/CreateCommentReplyMutation.ts index a7a5e8a66..b91c26e44 100644 --- a/src/core/client/stream/tabs/Comments/Comment/ReplyCommentForm/CreateCommentReplyMutation.ts +++ b/src/core/client/stream/tabs/Comments/Comment/ReplyCommentForm/CreateCommentReplyMutation.ts @@ -180,12 +180,6 @@ function commit( username: viewer.username, createdAt: viewer.createdAt, ignoreable: false, - status: { - current: viewer.status.current, - ban: { - active: viewer.status.ban.active, - }, - }, }, body: input.body, revision: { diff --git a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/BanUserMutation.ts b/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/BanUserMutation.ts deleted file mode 100644 index 66af746ab..000000000 --- a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/BanUserMutation.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { graphql } from "react-relay"; -import { Environment } from "relay-runtime"; - -import { - commitMutationPromiseNormalized, - createMutation, - lookup, - MutationInput, -} from "coral-framework/lib/relay"; -import { GQLUser, GQLUSER_STATUS } from "coral-framework/schema"; -import { BanUserMutation } from "coral-stream/__generated__/BanUserMutation.graphql"; - -let clientMutationId = 0; - -const BanUserMutation = createMutation( - "banUser", - (environment: Environment, input: MutationInput) => { - return commitMutationPromiseNormalized(environment, { - mutation: graphql` - mutation BanUserMutation($input: BanUserInput!) { - banUser(input: $input) { - user { - id - status { - current - ban { - active - } - } - } - clientMutationId - } - } - `, - variables: { - input: { - ...input, - clientMutationId: clientMutationId.toString(), - }, - }, - optimisticResponse: { - banUser: { - user: { - id: input.userID, - status: { - current: lookup( - environment, - input.userID - )!.status.current.concat(GQLUSER_STATUS.BANNED), - ban: { - active: true, - }, - }, - }, - clientMutationId: (clientMutationId++).toString(), - }, - }, - }); - } -); - -export default BanUserMutation; diff --git a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.css b/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.css deleted file mode 100644 index 362844dbd..000000000 --- a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.css +++ /dev/null @@ -1,4 +0,0 @@ -.root { - width: 280px; - max-width: 80vw; -} \ No newline at end of file diff --git a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx b/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx deleted file mode 100644 index 889af23f0..000000000 --- a/src/core/client/stream/tabs/Comments/Comment/UserBanPopover/UserBanPopoverContainer.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Localized } from "fluent-react/compat"; -import React, { FunctionComponent, useCallback } from "react"; -import { graphql } from "react-relay"; - -import { useMutation, withFragmentContainer } from "coral-framework/lib/relay"; -import { UserBanPopoverContainer_user as UserData } from "coral-stream/__generated__/UserBanPopoverContainer_user.graphql"; -import { Box, Button, Flex, Typography } from "coral-ui/components"; - -import BanUserMutation from "./BanUserMutation"; - -import styles from "./UserBanPopoverContainer.css"; - -interface Props { - onDismiss: () => void; - user: UserData; -} - -const UserBanPopoverContainer: FunctionComponent = ({ - user, - onDismiss, -}) => { - const banUser = useMutation(BanUserMutation); - const onBan = useCallback(() => { - banUser({ userID: user.id }); - onDismiss(); - }, [user, banUser, onDismiss]); - return ( - - - - Ban {user.username}? - - - - - Once banned, this user will no longer be able to comment, use - reactions, or report comments. - - - - - - - - - - - - ); -}; - -const enhanced = withFragmentContainer({ - user: graphql` - fragment UserBanPopoverContainer_user on User { - id - username - status { - current - ban { - active - } - } - } - `, -})(UserBanPopoverContainer); - -export default enhanced; diff --git a/src/core/client/stream/tabs/Comments/Stream/PostCommentForm/CreateCommentMutation.ts b/src/core/client/stream/tabs/Comments/Stream/PostCommentForm/CreateCommentMutation.ts index 6de116a39..cc4e951af 100644 --- a/src/core/client/stream/tabs/Comments/Stream/PostCommentForm/CreateCommentMutation.ts +++ b/src/core/client/stream/tabs/Comments/Stream/PostCommentForm/CreateCommentMutation.ts @@ -150,12 +150,6 @@ function commit( username: viewer.username, createdAt: viewer.createdAt, ignoreable: false, - status: { - current: viewer.status.current, - ban: { - active: viewer.status.ban.active, - }, - }, }, revision: { id: uuidGenerator(), diff --git a/src/core/client/stream/test/comments/stream/banned.spec.tsx b/src/core/client/stream/test/comments/stream/banned.spec.tsx index d3f26f0fb..04acf6621 100644 --- a/src/core/client/stream/test/comments/stream/banned.spec.tsx +++ b/src/core/client/stream/test/comments/stream/banned.spec.tsx @@ -1,29 +1,20 @@ import timekeeper from "timekeeper"; import { pureMerge } from "coral-common/utils"; -import { GQLResolver } from "coral-framework/schema"; +import { GQLResolver, GQLUSER_STATUS } from "coral-framework/schema"; import { createResolversStub, CreateTestRendererParams, waitForElement, within, } from "coral-framework/testHelpers"; -import { - createComment, - createStory, - createUser, - createUserStatus, -} from "coral-stream/test/helpers/fixture"; -import { settings } from "../../fixtures"; +import { comments, settings, stories } from "../../fixtures"; import create from "./create"; -const bannedUser = createUser(); -bannedUser.status = createUserStatus(true); - -const story = createStory(); +const story = stories[0]; const firstComment = story.comments.edges[0].node; -const reactedComment = createComment(); +const viewer = firstComment.author!; async function createTestRenderer( params: CreateTestRendererParams = {} @@ -34,17 +25,22 @@ async function createTestRenderer( createResolversStub({ Query: { settings: () => settings, - viewer: () => bannedUser, + viewer: () => + pureMerge(viewer, { + status: { + current: [GQLUSER_STATUS.BANNED], + }, + }), story: () => pureMerge(story, { comments: { edges: [ ...story.comments.edges, { - node: pureMerge(reactedComment, { + node: pureMerge(comments[2], { actionCounts: { reaction: { total: 1 } }, }), - cursor: reactedComment.createdAt, + cursor: comments[2].createdAt, }, ], }, diff --git a/src/core/client/stream/test/fixtures.ts b/src/core/client/stream/test/fixtures.ts index 1dc7b6757..0cf7e44df 100644 --- a/src/core/client/stream/test/fixtures.ts +++ b/src/core/client/stream/test/fixtures.ts @@ -90,15 +90,6 @@ export const baseUser = createFixture({ createdAt: "2018-02-06T18:24:00.000Z", status: { current: [GQLUSER_STATUS.ACTIVE], - ban: { - active: false, - history: [], - }, - suspension: { - active: false, - until: null, - history: [], - }, }, ignoredUsers: [], comments: { diff --git a/src/core/client/stream/test/helpers/fixture.ts b/src/core/client/stream/test/helpers/fixture.ts deleted file mode 100644 index 05dda4cf2..000000000 --- a/src/core/client/stream/test/helpers/fixture.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { - GQLComment, - GQLCOMMENT_STATUS, - GQLMODERATION_MODE, - GQLStory, - GQLUser, - GQLUSER_ROLE, - GQLUSER_STATUS, -} from "coral-framework/schema"; -import { - createFixture, - denormalizeComment, - denormalizeStory, -} from "coral-framework/testHelpers"; -import uuid from "uuid/v4"; - -export function createDateInRange(start: Date, end: Date) { - return new Date( - start.getTime() + Math.random() * (end.getTime() - start.getTime()) - ); -} - -export function randomDate() { - return createDateInRange(new Date(2000, 0, 1), new Date()); -} - -export function createUserStatus(banned: boolean = false) { - return { - current: [banned ? GQLUSER_STATUS.BANNED : GQLUSER_STATUS.ACTIVE], - ban: { - active: banned, - history: [], - }, - suspension: { - active: false, - until: null, - history: [], - }, - }; -} - -export function createUser() { - return createFixture({ - id: uuid(), - username: uuid(), - role: GQLUSER_ROLE.COMMENTER, - createdAt: randomDate().toISOString(), - status: createUserStatus(), - ignoredUsers: [], - comments: { - edges: [], - pageInfo: { - hasNextPage: false, - }, - }, - ignoreable: true, - }); -} - -export function createComment() { - const revision = uuid(); - const createdAt = randomDate(); - const editableUntil = new Date(createdAt.getTime() + 30 * 60000); - const author = createUser(); - author.createdAt = new Date(createdAt.getTime() - 60 * 60000).toISOString(); - - return denormalizeComment( - createFixture({ - id: uuid(), - author, - body: uuid(), - revision: { - id: revision, - }, - status: GQLCOMMENT_STATUS.NONE, - createdAt: createdAt.toISOString(), - replies: { edges: [], pageInfo: { endCursor: null, hasNextPage: false } }, - replyCount: 0, - editing: { - edited: false, - editableUntil: editableUntil.toISOString(), - }, - actionCounts: { - reaction: { - total: 0, - }, - }, - tags: [], - }) - ); -} - -export function createStory(createComments: boolean = true) { - const id = uuid(); - const comments = [createComment(), createComment()]; - - return denormalizeStory( - createFixture({ - id, - url: `http://localhost/stories/story-${id}`, - comments: { - edges: [ - { node: comments[0], cursor: comments[0].createdAt }, - { node: comments[1], cursor: comments[1].createdAt }, - ], - pageInfo: { - hasNextPage: false, - }, - }, - metadata: { - title: uuid(), - }, - isClosed: false, - commentCounts: { - totalVisible: 0, - tags: { - FEATURED: 0, - }, - }, - settings: { - moderation: GQLMODERATION_MODE.POST, - premodLinksEnable: false, - messageBox: { - enabled: false, - }, - }, - }) - ); -} diff --git a/src/locales/en-US/stream.ftl b/src/locales/en-US/stream.ftl index 7c64e1658..3a027de4d 100644 --- a/src/locales/en-US/stream.ftl +++ b/src/locales/en-US/stream.ftl @@ -119,13 +119,6 @@ comments-userIgnorePopover-description = comments-userIgnorePopover-ignore = Ignore comments-userIgnorePopover-cancel = Cancel -comments-userBanPopover-title = Ban {$username}? -comments-userBanPopover-description = - Once banned, this user will no longer be able - to comment, use reactions, or report comments. -comments-userBanPopover-cancel = Cancel -comments-userBanPopover-ban = Ban - comments-moderationDropdown-popover = .description = A popover menu to moderate the comment comments-moderationDropdown-feature = Feature @@ -134,8 +127,6 @@ comments-moderationDropdown-approve = Approve comments-moderationDropdown-approved = Approved comments-moderationDropdown-reject = Reject comments-moderationDropdown-rejected = Rejected -comments-moderationDropdown-ban = Ban User -comments-moderationDropdown-banned = Banned comments-moderationDropdown-goToModerate = Go to Moderate comments-moderationDropdown-caretButton = .aria-label = Moderate