Skip to content

Commit

Permalink
Merge pull request #4439 from coralproject/feat/CORL-2978-rejection-r…
Browse files Browse the repository at this point in the history
…easons-add-comment-card-user-drawer

[CORL-2978]: Add rejection reasons to comment card and user drawer
  • Loading branch information
kabeaty committed Dec 4, 2023
2 parents a12d931 + db71ace commit 11c5cc2
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.wrapper {
background-color: var(--palette-grey-200);
font-family: var(--font-family-primary);
}

.full {
width: 100%;
}

.label {
font-size: var(--font-size-1);
font-weight: var(--font-weight-primary-semi-bold);
text-transform: uppercase;
color: var(--palette-grey-500);
}

.rejected {
color: var(--palette-text-000);
background-color: var(--palette-error-500);
text-transform: uppercase;
padding: var(--spacing-1) var(--spacing-2);
width: fit-content;
font-size: var(--font-size-1);
font-weight: var(--font-weight-primary-semi-bold);
}

.info {
font-size: var(--font-size-2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Localized } from "@fluent/react/compat";
import React, { FunctionComponent } from "react";
import { graphql } from "react-relay";

import { withFragmentContainer } from "coral-framework/lib/relay";
import { Flex, HorizontalGutter, Timestamp } from "coral-ui/components/v2";

import { DecisionDetailsContainer_comment } from "coral-admin/__generated__/DecisionDetailsContainer_comment.graphql";

import styles from "./DecisionDetailsContainer.css";

import { unsnake } from "../ModerationReason/formatting";

interface Props {
comment: DecisionDetailsContainer_comment;
}

const DecisionDetailsContainer: FunctionComponent<Props> = ({ comment }) => {
const statusHistory = comment.statusHistory.edges[0].node;
const { rejectionReason, createdAt } = statusHistory;

return (
<HorizontalGutter className={styles.wrapper} padding={3}>
<Flex>
<Flex direction="column" className={styles.full}>
<Localized id="moderate-decisionDetails-decisionLabel">
<div className={styles.label}>Decision</div>
</Localized>
<Localized id="moderate-decisionDetails-rejected">
<div className={styles.rejected}>Rejected</div>
</Localized>
</Flex>
{rejectionReason && rejectionReason.code && (
<Flex direction="column" className={styles.full}>
<Localized id="moderate-decisionDetails-reasonLabel">
<div className={styles.label}>Reason</div>
</Localized>
<Localized
id={`common-moderationReason-rejectionReason-${rejectionReason.code}`}
>
<div className={styles.info}>{unsnake(rejectionReason.code)}</div>
</Localized>
</Flex>
)}
</Flex>
{rejectionReason?.legalGrounds && (
<Flex direction="column">
<Localized id="moderate-decisionDetails-lawBrokenLabel">
<div className={styles.label}>Law broken</div>
</Localized>
<div className={styles.info}>{rejectionReason?.legalGrounds}</div>
</Flex>
)}
{rejectionReason?.customReason && (
<Flex direction="column">
<Localized id="moderate-decisionDetails-customReasonLabel">
<div className={styles.label}>Custom reason</div>
</Localized>
<div className={styles.info}>{rejectionReason?.customReason}</div>
</Flex>
)}
{rejectionReason?.detailedExplanation && (
<Flex direction="column">
<Localized id="moderate-decisionDetails-detailedExplanationLabel">
<div className={styles.label}>Detailed explanation</div>
</Localized>
<div className={styles.info}>
{rejectionReason?.detailedExplanation}
</div>
</Flex>
)}
<Flex>
<div className={styles.label}>
<Timestamp>{createdAt}</Timestamp>
</div>
</Flex>
</HorizontalGutter>
);
};

const enhanced = withFragmentContainer<Props>({
comment: graphql`
fragment DecisionDetailsContainer_comment on Comment {
id
statusHistory(first: 1) {
edges {
node {
createdAt
rejectionReason {
code
legalGrounds
detailedExplanation
customReason
}
}
}
}
}
`,
})(DecisionDetailsContainer);

export default enhanced;
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@
text-transform: uppercase;
font-size: var(--font-size-2);
}

.decisionIcon {
height: 1.125rem;
width: 1.75rem;
margin: 0 var(--spacing-1) 0 0 !important;
}

.decisionIcon svg {
height: 1.125rem;
width: 1.75rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import React, {
import { graphql } from "react-relay";

import { withFragmentContainer } from "coral-framework/lib/relay";
import { GQLCOMMENT_STATUS } from "coral-framework/schema";
import {
CheckDoubleIcon,
LikeIcon,
ListBulletsIcon,
ModerationDecisionIcon,
PencilIcon,
SvgIcon,
} from "coral-ui/components/icons";
Expand All @@ -22,6 +24,7 @@ import { ModerateCardDetailsContainer_settings } from "coral-admin/__generated__

import AutomatedActionsContainer from "./AutomatedActionsContainer";
import CommentRevisionContainer from "./CommentRevisionContainer";
import DecisionDetailsContainer from "./DecisionDetailsContainer";
import FlagDetailsContainer from "./FlagDetailsContainer";
import LinkDetailsContainer from "./LinkDetailsContainer";
import ReactionDetailsQuery from "./ReactionDetailsQuery";
Expand All @@ -34,7 +37,12 @@ interface Props {
onUsernameClick: (id?: string) => void;
}

type DetailsTabs = "INFO" | "REACTIONS" | "HISTORY" | "EXTERNAL_MOD";
type DetailsTabs =
| "INFO"
| "REACTIONS"
| "HISTORY"
| "EXTERNAL_MOD"
| "DECISION";

function hasFlagDetails(c: ModerateCardDetailsContainer_comment) {
return c.revision
Expand All @@ -52,7 +60,15 @@ const ModerateCardDetailsContainer: FunctionComponent<Props> = ({
onUsernameClick,
settings,
}) => {
const [activeTab, setActiveTab] = useState<DetailsTabs>("INFO");
const hasDecision =
comment.status === GQLCOMMENT_STATUS.REJECTED &&
comment.statusHistory.edges[0] &&
comment.statusHistory.edges[0].node.rejectionReason &&
comment.statusHistory.edges[0].node.rejectionReason.code;

const [activeTab, setActiveTab] = useState<DetailsTabs>(
hasDecision ? "DECISION" : "INFO"
);

const onTabClick = useCallback(
(id: string) => setActiveTab(id as DetailsTabs),
Expand All @@ -79,6 +95,19 @@ const ModerateCardDetailsContainer: FunctionComponent<Props> = ({
return (
<HorizontalGutter>
<TabBar variant="default" activeTab={activeTab} onTabClick={onTabClick}>
{hasDecision && (
<Tab tabID="DECISION" classes={styles}>
<Flex alignItems="center" itemGutter>
<SvgIcon
className={styles.decisionIcon}
Icon={ModerationDecisionIcon}
/>
<Localized id="moderateCardDetails-tab-decision">
<span>Decision</span>
</Localized>
</Flex>
</Tab>
)}
<Tab tabID="INFO" classes={styles}>
<Flex alignItems="center" itemGutter>
<SvgIcon Icon={ListBulletsIcon} />
Expand Down Expand Up @@ -118,6 +147,9 @@ const ModerateCardDetailsContainer: FunctionComponent<Props> = ({
</Tab>
)}
</TabBar>
{activeTab === "DECISION" && (
<DecisionDetailsContainer comment={comment} />
)}
{activeTab === "INFO" && (
<>
<LinkDetailsContainer comment={comment} settings={settings} />
Expand Down Expand Up @@ -156,6 +188,15 @@ const enhanced = withFragmentContainer<Props>({
editing {
edited
}
statusHistory(first: 1) {
edges {
node {
rejectionReason {
code
}
}
}
}
revision {
actionCounts {
flag {
Expand Down Expand Up @@ -193,6 +234,7 @@ const enhanced = withFragmentContainer<Props>({
...CommentRevisionContainer_comment
...LinkDetailsContainer_comment
...AutomatedActionsContainer_comment
...DecisionDetailsContainer_comment
}
`,
settings: graphql`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ it("approves comment in reported queue", async () => {
edges: [
{
node: {
createdAt: "2023-06-01T14:21:21.890Z",
id: "mod-action",
status: GQLCOMMENT_STATUS.APPROVED,
moderator: {
Expand Down Expand Up @@ -727,6 +728,7 @@ it("rejects comment in reported queue", async () => {
edges: [
{
node: {
createdAt: "2023-06-01T14:21:21.890Z",
id: "mod-action",
status: GQLCOMMENT_STATUS.REJECTED,
moderator: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ it("approves comment in rejected queue", async () => {
edges: [
{
node: {
createdAt: "2023-06-01T14:21:21.890Z",
id: "mod-action",
status: GQLCOMMENT_STATUS.APPROVED,
moderator: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ it("approves single comment", async () => {
edges: [
{
node: {
createdAt: "2023-06-01T14:21:21.890Z",
id: "mod-action",
status: GQLCOMMENT_STATUS.APPROVED,
moderator: {
Expand Down Expand Up @@ -163,6 +164,7 @@ it("rejects single comment", async () => {
edges: [
{
node: {
createdAt: "2023-06-01T14:21:21.890Z",
id: "mod-action",
status: GQLCOMMENT_STATUS.REJECTED,
author: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { FunctionComponent } from "react";

const ModerationDecisionIcon: FunctionComponent = () => {
return (
<svg viewBox="0 0 30 18" xmlns="http://www.w3.org/2000/svg">
<path
stroke="currentColor"
d="M19.5218 9.10378L20.6125 10.0736L23.1824 8.06207"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M28.0738 4.4889L18.9097 1.04258C18.9097 1.04258 18.2988 0.812827 18.069 1.42377L13.9334 12.4207C13.9334 12.4207 13.7037 13.0316 14.3146 13.2613L23.4787 16.7077C23.4787 16.7077 24.0896 16.9374 24.3194 16.3265L28.455 5.32959C28.455 5.32959 28.6847 4.71865 28.0738 4.4889Z"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
<path
d="M12.1194 14.6012L6.51917 16.7069C6.43887 16.7372 6.35336 16.7515 6.26755 16.7487C6.18173 16.746 6.0973 16.7264 6.01909 16.691C5.94087 16.6556 5.87041 16.6051 5.81174 16.5424C5.75307 16.4797 5.70734 16.4061 5.67718 16.3257L1.54159 5.32879C1.51142 5.24856 1.49735 5.16317 1.50018 5.0775C1.503 4.99183 1.52268 4.90756 1.55807 4.82949C1.59347 4.75143 1.64389 4.6811 1.70647 4.62252C1.76905 4.56394 1.84255 4.51826 1.92278 4.48809L11.0869 1.04177C11.2489 0.980839 11.4285 0.986767 11.5861 1.05825C11.7438 1.12974 11.8666 1.26093 11.9275 1.42296L13.9092 6.68251"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
<path
d="M9.75537 6.77246L7.84945 10.9759"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
<path
d="M6.70059 7.92139L10.9041 9.82731"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
/>
</svg>
);
};

export default ModerationDecisionIcon;
1 change: 1 addition & 0 deletions client/src/core/client/ui/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export { default as ListBulletsIcon } from "./ListBulletsIcon";
export { default as LockIcon } from "./LockIcon";
export { default as MessagesBubbleSquareIcon } from "./MessagesBubbleSquareIcon";
export { default as MessagesBubbleSquareWarningIcon } from "./MessagesBubbleSquareWarningIcon";
export { default as ModerationDecisionIcon } from "./ModerationDecisionIcon";
export { default as MultipleActionsChatIcon } from "./MultipleActionsChatIcon";
export { default as MultipleNeutralIcon } from "./MultipleNeutralIcon";
export { default as NavigationMenuHorizontalIcon } from "./NavigationMenuHorizontalIcon";
Expand Down
8 changes: 8 additions & 0 deletions locales/en-US/admin.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,13 @@ moderate-linkDetails-label = Copy link to this comment
moderate-in-stream-link-copy = In Stream
moderate-in-moderation-link-copy = In Moderation
moderate-decisionDetails-decisionLabel = Decision
moderate-decisionDetails-rejected = Rejected
moderate-decisionDetails-reasonLabel = Reason
moderate-decisionDetails-lawBrokenLabel = Law broken
moderate-decisionDetails-customReasonLabel = Custom reason
moderate-decisionDetails-detailedExplanationLabel = Detailed explanation
moderate-emptyQueue-pending = Nicely done! There are no more pending comments to moderate.
moderate-emptyQueue-reported = Nicely done! There are no more reported comments to moderate.
moderate-emptyQueue-unmoderated = Nicely done! All comments have been moderated.
Expand Down Expand Up @@ -1108,6 +1115,7 @@ moderate-searchBar-goTo = Go to
moderate-searchBar-seeAllResults = See all results
moderateCardDetails-tab-info = Info
moderateCardDetails-tab-decision = Decision
moderateCardDetails-tab-edits = Edit history
moderateCardDetails-tab-automatedActions = Automated actions
moderateCardDetails-tab-reactions = Reactions
Expand Down
7 changes: 6 additions & 1 deletion server/src/core/server/graph/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3493,12 +3493,17 @@ type CommentModerationAction {
"""
reason is the reason the comment was rejected, if it was rejected
"""
reason: RejectionReason
rejectionReason: RejectionReason

"""
createdAt is the time that the CommentModerationAction was created.
"""
createdAt: Time!

"""
customReason is a reason provided for rejection when the Other rejection code is selected.
"""
customReason: String
}

type CommentModerationActionEdge {
Expand Down

0 comments on commit 11c5cc2

Please sign in to comment.