From dce60c5eab7ef57c961134cf6fafa5309630538b Mon Sep 17 00:00:00 2001 From: Kiwi Date: Thu, 23 May 2019 01:48:10 +0200 Subject: [PATCH] [CORL-349] Show banned user info (#2319) * feat: stream banned state ui * fix: button color * fix: banned permissions * test: add tests and hide edit * fix: show reaction button and adapt snapshots * fix: snapshot --- .../framework/testHelpers/denormalize.ts | 4 +- .../tabs/comments/components/BannedInfo.tsx | 23 ++ .../comments/components/ReactionButton.css | 5 + .../comments/components/ReactionButton.tsx | 14 +- .../tabs/comments/components/Stream.tsx | 10 +- .../comments/containers/CommentContainer.tsx | 38 ++- .../containers/ReactionButtonContainer.tsx | 15 +- .../comments/containers/StreamContainer.tsx | 8 + .../CommentContainer.spec.tsx.snap | 236 +++++++++--------- .../StreamContainer.spec.tsx.snap | 4 + .../tabs/profile/queries/ProfileQuery.tsx | 14 +- .../__snapshots__/editComment.spec.tsx.snap | 116 ++++----- .../__snapshots__/loadMore.spec.tsx.snap | 58 ++--- .../__snapshots__/permalinkView.spec.tsx.snap | 145 +++++------ ...permalinkViewLoadMoreParents.spec.tsx.snap | 116 ++++----- .../__snapshots__/postComment.spec.tsx.snap | 29 +-- .../postLocalReply.spec.tsx.snap | 232 ++++++++--------- .../__snapshots__/postReply.spec.tsx.snap | 58 ++--- .../__snapshots__/reaction.spec.tsx.snap | 2 + .../renderCommunityGuidelines.spec.tsx.snap | 58 ++--- .../__snapshots__/renderReplies.spec.tsx.snap | 116 ++++----- .../__snapshots__/renderStream.spec.tsx.snap | 58 ++--- .../showAllReplies.spec.tsx.snap | 29 +-- .../showConversation.spec.tsx.snap | 29 +-- .../__snapshots__/sortStream.spec.tsx.snap | 58 ++--- .../stream/test/comments/banned.spec.tsx | 97 +++++++ src/core/client/stream/test/fixtures.ts | 106 ++++---- .../client/ui/components/CallOut/CallOut.css | 2 +- .../server/graph/tenant/schema/schema.graphql | 23 +- 29 files changed, 982 insertions(+), 721 deletions(-) create mode 100644 src/core/client/stream/tabs/comments/components/BannedInfo.tsx create mode 100644 src/core/client/stream/tabs/comments/components/ReactionButton.css create mode 100644 src/core/client/stream/test/comments/banned.spec.tsx diff --git a/src/core/client/framework/testHelpers/denormalize.ts b/src/core/client/framework/testHelpers/denormalize.ts index fc0da0136..4b97c292d 100644 --- a/src/core/client/framework/testHelpers/denormalize.ts +++ b/src/core/client/framework/testHelpers/denormalize.ts @@ -52,13 +52,13 @@ export function denormalizeStory(story: Fixture) { endCursor: null, hasNextPage: false, }; - return { + return createFixture({ ...story, comments: { edges: commentNodes, pageInfo: commentsPageInfo }, commentCounts: { totalVisible: commentNodes.length, }, - }; + }); } export function denormalizeStories(storyList: Array>) { diff --git a/src/core/client/stream/tabs/comments/components/BannedInfo.tsx b/src/core/client/stream/tabs/comments/components/BannedInfo.tsx new file mode 100644 index 000000000..e2f19cf74 --- /dev/null +++ b/src/core/client/stream/tabs/comments/components/BannedInfo.tsx @@ -0,0 +1,23 @@ +import React, { FunctionComponent } from "react"; + +import { CallOut, HorizontalGutter, Typography } from "coral-ui/components"; + +const BannedInfo: FunctionComponent = props => { + return ( + + + + Your account has been banned from commenting. + + + Someone with access to your account has violated our community + guidelines. As a result, your account has been banned. You will no + longer be able to comment, respect or report comments. if you think + this has been done in error, please contact our community team. + + + + ); +}; + +export default BannedInfo; diff --git a/src/core/client/stream/tabs/comments/components/ReactionButton.css b/src/core/client/stream/tabs/comments/components/ReactionButton.css new file mode 100644 index 000000000..e027c4445 --- /dev/null +++ b/src/core/client/stream/tabs/comments/components/ReactionButton.css @@ -0,0 +1,5 @@ +.readOnly { + cursor: not-allowed !important; + color: var(--palette-text-primary) !important; + opacity: 1 !important; +} diff --git a/src/core/client/stream/tabs/comments/components/ReactionButton.tsx b/src/core/client/stream/tabs/comments/components/ReactionButton.tsx index 28acc8fef..82be0cd4f 100644 --- a/src/core/client/stream/tabs/comments/components/ReactionButton.tsx +++ b/src/core/client/stream/tabs/comments/components/ReactionButton.tsx @@ -1,7 +1,10 @@ +import cn from "classnames"; import React from "react"; import { Button, ButtonIcon, MatchMedia } from "coral-ui/components"; +import styles from "./ReactionButton.css"; + interface ReactionButtonProps { onClick: () => void; totalReactions: number; @@ -10,14 +13,21 @@ interface ReactionButtonProps { labelActive: string; icon: string; iconActive: string | null; + readOnly?: boolean; // color: string; } class ReactionButton extends React.Component { public render() { - const { totalReactions, reacted } = this.props; + const { totalReactions, reacted, readOnly } = this.props; return ( -
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -459,20 +475,6 @@ exports[`post a reply: optimistic response 1`] = `
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
= {} +) { + const { testRenderer, context } = create({ + ...params, + resolvers: pureMerge( + createResolversStub({ + Query: { + settings: () => settings, + viewer: () => + pureMerge(viewer, { + status: { + current: [GQLUSER_STATUS.BANNED], + }, + }), + story: () => + pureMerge(story, { + comments: { + edges: [ + ...story.comments.edges, + { + node: pureMerge(comments[2], { + actionCounts: { reaction: { total: 1 } }, + }), + cursor: comments[2].createdAt, + }, + ], + }, + }), + }, + }), + params.resolvers + ), + initLocalState: (localRecord, source, environment) => { + localRecord.setValue(story.id, "storyID"); + localRecord.setValue(true, "loggedIn"); + if (params.initLocalState) { + params.initLocalState(localRecord, source, environment); + } + }, + }); + + const tabPane = await waitForElement(() => + within(testRenderer.root).getByTestID("current-tab-pane") + ); + + return { + testRenderer, + context, + tabPane, + }; +} + +afterAll(() => { + timekeeper.reset(); +}); + +it("disables comment stream", async () => { + timekeeper.freeze(firstComment.createdAt); + const { testRenderer, tabPane } = await createTestRenderer(); + await waitForElement(() => + within(testRenderer.root).getByTestID("comments-stream-log") + ); + within(tabPane).getAllByText("Your account has been banned", { + exact: false, + }); + expect( + within(tabPane).queryByText("Reply", { selector: "button" }) + ).toBeNull(); + expect( + within(tabPane).queryByText("Report", { selector: "button" }) + ).toBeNull(); + expect( + within(tabPane).queryByText("Edit", { selector: "button" }) + ).toBeNull(); + expect( + within(tabPane).getByText("Respect", { selector: "button" }).props.disabled + ).toBe(true); +}); diff --git a/src/core/client/stream/test/fixtures.ts b/src/core/client/stream/test/fixtures.ts index 601295909..8b6703c2f 100644 --- a/src/core/client/stream/test/fixtures.ts +++ b/src/core/client/stream/test/fixtures.ts @@ -6,6 +6,7 @@ import { GQLStory, GQLUser, GQLUSER_ROLE, + GQLUSER_STATUS, } from "coral-framework/schema"; import { createFixture, @@ -83,28 +84,37 @@ export const settings = createFixture({ }, }); -export const commenters = createFixtures([ - { - id: "user-0", - username: "Markus", - role: GQLUSER_ROLE.COMMENTER, +export const baseUser = createFixture({ + status: { + current: [GQLUSER_STATUS.ACTIVE], }, - { - id: "user-1", - username: "Lukas", - role: GQLUSER_ROLE.COMMENTER, - }, - { - id: "user-2", - username: "Isabelle", - role: GQLUSER_ROLE.COMMENTER, - }, - { - id: "user-3", - username: "Markus", - role: GQLUSER_ROLE.COMMENTER, - }, -]); +}); + +export const commenters = createFixtures( + [ + { + id: "user-0", + username: "Markus", + role: GQLUSER_ROLE.COMMENTER, + }, + { + id: "user-1", + username: "Lukas", + role: GQLUSER_ROLE.COMMENTER, + }, + { + id: "user-2", + username: "Isabelle", + role: GQLUSER_ROLE.COMMENTER, + }, + { + id: "user-3", + username: "Markus", + role: GQLUSER_ROLE.COMMENTER, + }, + ], + baseUser +); export const baseComment = createFixture({ author: commenters[0], @@ -330,13 +340,16 @@ export const baseStory = createFixture({ }, }); -export const moderators = createFixtures([ - { - id: "me-as-moderator", - username: "Moderator", - role: GQLUSER_ROLE.MODERATOR, - }, -]); +export const moderators = createFixtures( + [ + { + id: "me-as-moderator", + username: "Moderator", + role: GQLUSER_ROLE.MODERATOR, + }, + ], + baseUser +); export const commentsFromStaff = denormalizeComments( createFixtures( @@ -480,23 +493,26 @@ export const storyWithDeepestReplies = denormalizeStory( ) ); -export const viewerWithComments = createFixture({ - id: "me-with-comments", - username: "Markus", - role: GQLUSER_ROLE.COMMENTER, - comments: { - edges: [ - { - node: { ...stories[0].comments.edges[0].node, story: stories[0] }, - cursor: comments[0].createdAt, +export const viewerWithComments = createFixture( + { + id: "me-with-comments", + username: "Markus", + role: GQLUSER_ROLE.COMMENTER, + comments: { + edges: [ + { + node: { ...stories[0].comments.edges[0].node, story: stories[0] }, + cursor: comments[0].createdAt, + }, + { + node: { ...stories[1].comments.edges[0].node, story: stories[1] }, + cursor: comments[1].createdAt, + }, + ], + pageInfo: { + hasNextPage: false, }, - { - node: { ...stories[1].comments.edges[0].node, story: stories[1] }, - cursor: comments[1].createdAt, - }, - ], - pageInfo: { - hasNextPage: false, }, }, -}); + baseUser +); diff --git a/src/core/client/ui/components/CallOut/CallOut.css b/src/core/client/ui/components/CallOut/CallOut.css index 8c538ee84..49c1857af 100644 --- a/src/core/client/ui/components/CallOut/CallOut.css +++ b/src/core/client/ui/components/CallOut/CallOut.css @@ -4,7 +4,7 @@ display: inline-flex; justify-content: center; align-items: center; - padding: var(--mini-unit); + padding: calc(1.5 * var(--mini-unit)); box-sizing: border-box; border-width: 1px; border-style: solid; diff --git a/src/core/server/graph/tenant/schema/schema.graphql b/src/core/server/graph/tenant/schema/schema.graphql index 824c6f173..9ba3d0263 100644 --- a/src/core/server/graph/tenant/schema/schema.graphql +++ b/src/core/server/graph/tenant/schema/schema.graphql @@ -1390,7 +1390,12 @@ type User { emailVerified when true indicates that the given email address has been verified. """ - emailVerified: Boolean @auth(roles: [ADMIN, MODERATOR], userIDField: "id") + emailVerified: Boolean + @auth( + roles: [ADMIN, MODERATOR] + userIDField: "id" + permit: [SUSPENDED, BANNED] + ) """ profiles is the array of profiles assigned to the user. @@ -1419,7 +1424,12 @@ type User { first: Int = 10 orderBy: COMMENT_SORT = CREATED_AT_DESC after: Cursor - ): CommentsConnection! @auth(roles: [ADMIN, MODERATOR], userIDField: "id") + ): CommentsConnection! + @auth( + roles: [ADMIN, MODERATOR] + userIDField: "id" + permit: [SUSPENDED, BANNED] + ) """ commentModerationActionHistory returns a CommentModerationActionConnection @@ -1438,7 +1448,8 @@ type User { """ tokens lists the access tokens associated with the account. """ - tokens: [Token!]! @auth(roles: [ADMIN], userIDField: "id") + tokens: [Token!]! + @auth(roles: [ADMIN], userIDField: "id", permit: [SUSPENDED, BANNED]) """ createdAt is the time that the User was created at. @@ -1658,7 +1669,11 @@ type Comment { edit last. """ revisionHistory: [CommentRevision!]! - @auth(roles: [MODERATOR, ADMIN], userIDField: "author_id") + @auth( + roles: [MODERATOR, ADMIN] + userIDField: "author_id" + permit: [SUSPENDED, BANNED] + ) """ createdAt is the date in which the Comment was created.