[CORL-1035] Moderated By Bug (#2935)

* fix: improved moderated by component

* fix: uncommented line
This commit is contained in:
Wyatt Johnson
2020-04-14 21:55:40 +00:00
committed by GitHub
parent 518056f382
commit 257830a6c5
21 changed files with 142 additions and 137 deletions
@@ -22,7 +22,6 @@ import {
ModerateCardContainer_comment,
} from "coral-admin/__generated__/ModerateCardContainer_comment.graphql";
import { ModerateCardContainer_settings } from "coral-admin/__generated__/ModerateCardContainer_settings.graphql";
import { ModerateCardContainer_viewer } from "coral-admin/__generated__/ModerateCardContainer_viewer.graphql";
import BanCommentUserMutation from "./BanCommentUserMutation";
import FeatureCommentMutation from "./FeatureCommentMutation";
@@ -32,7 +31,6 @@ import UnfeatureCommentMutation from "./UnfeatureCommentMutation";
interface Props {
comment: ModerateCardContainer_comment;
viewer: ModerateCardContainer_viewer;
settings: ModerateCardContainer_settings;
approveComment: MutationProp<typeof ApproveCommentMutation>;
rejectComment: MutationProp<typeof RejectCommentMutation>;
@@ -74,7 +72,6 @@ const ModerateCardContainer: FunctionComponent<Props> = ({
settings,
danglingLogic,
showStoryInfo,
viewer,
match,
router,
approveComment,
@@ -258,7 +255,6 @@ const ModerateCardContainer: FunctionComponent<Props> = ({
moderatedBy={
<ModeratedByContainer
onUsernameClicked={onUsernameClicked}
viewer={viewer}
comment={comment}
/>
}
@@ -365,11 +361,6 @@ const enhanced = withFragmentContainer<Props>({
...MarkersContainer_settings
}
`,
viewer: graphql`
fragment ModerateCardContainer_viewer on User {
...ModeratedByContainer_viewer
}
`,
})(
withRouter(
withMutation(BanCommentUserMutation)(
@@ -24,10 +24,6 @@ it("viewer's username shows on moderation cards moderated by viewer", () => {
],
},
},
viewer: {
id: "viewer",
username: "viewer",
},
onUsernameClicked: (id?: string | null) => {
return;
},
@@ -1,55 +1,88 @@
import { Localized } from "@fluent/react/compat";
import React, { useCallback } from "react";
import React, { useCallback, useMemo } from "react";
import { graphql } from "react-relay";
import { withFragmentContainer } from "coral-framework/lib/relay";
import { BaseButton } from "coral-ui/components/v2";
import { ModeratedByContainer_comment } from "coral-admin/__generated__/ModeratedByContainer_comment.graphql";
import { ModeratedByContainer_viewer } from "coral-admin/__generated__/ModeratedByContainer_viewer.graphql";
import styles from "./ModeratedByContainer.css";
interface Props {
viewer: ModeratedByContainer_viewer;
comment: ModeratedByContainer_comment;
onUsernameClicked: (id?: string | null) => void;
}
const MODERATION_STATUS = ["APPROVED", "REJECTED"];
interface ModeratedBy {
id?: string;
username?: string | null;
system?: boolean;
}
const system: ModeratedBy = { system: true };
const ModeratedByContainer: React.FunctionComponent<Props> = ({
comment,
viewer,
onUsernameClicked,
}) => {
let moderatedBy: React.ReactElement | null;
let id: string | null = null;
if (comment.statusHistory.edges.length === 0) {
moderatedBy = null;
} else if (comment.statusHistory.edges[0].node.moderator === null) {
moderatedBy = (
<Localized id="moderate-comment-moderatedBySystem">System</Localized>
);
} else if (viewer.id === comment.statusHistory.edges[0].node.moderator.id) {
moderatedBy = viewer.username ? <>{viewer.username}</> : null;
} else {
moderatedBy = <>{comment.statusHistory.edges[0].node.moderator.username}</>;
id = comment.statusHistory.edges[0].node.moderator.id;
}
const moderatedBy: ModeratedBy | null = useMemo(() => {
// If the comment was just moderated by the viewer, don't display anything.
// This will update once the mutation returns.
if (comment.viewerDidModerate) {
return null;
}
// If the comment has not been approved or rejected, don't render anything.
if (!MODERATION_STATUS.includes(comment.status)) {
return null;
}
if (comment.statusHistory.edges.length === 0) {
return system;
}
// Get the node that was the last moderated by element.
const {
node: { status, moderator },
} = comment.statusHistory.edges[0];
if (!MODERATION_STATUS.includes(status)) {
return null;
}
if (!moderator) {
return system;
}
return moderator;
}, [comment]);
const onClick = useCallback(() => {
if (!moderatedBy || !moderatedBy.id) {
return;
}
onUsernameClicked(moderatedBy.id);
}, [onUsernameClicked, moderatedBy]);
if (!moderatedBy) {
return null;
}
const onClick = useCallback(() => {
onUsernameClicked(id);
}, [onUsernameClicked, comment]);
return (
<BaseButton onClick={onClick}>
<Localized id="moderate-comment-moderatedBy">
<div className={styles.moderatedBy}>Moderated By</div>
</Localized>
<div className={styles.moderatedByUsername}>{moderatedBy}</div>
<div className={styles.moderatedByUsername}>
{moderatedBy.system ? (
<Localized id="moderate-comment-moderatedBySystem">System</Localized>
) : (
moderatedBy.username
)}
</div>
</BaseButton>
);
};
@@ -58,10 +91,12 @@ const enhanced = withFragmentContainer<Props>({
comment: graphql`
fragment ModeratedByContainer_comment on Comment {
id
statusLiveUpdated
status
viewerDidModerate
statusHistory(first: 1) {
edges {
node {
status
moderator {
id
username
@@ -71,12 +106,6 @@ const enhanced = withFragmentContainer<Props>({
}
}
`,
viewer: graphql`
fragment ModeratedByContainer_viewer on User {
id
username
}
`,
})(ModeratedByContainer);
export default enhanced;
@@ -19,11 +19,5 @@ exports[`viewer's username shows on moderation cards moderated by viewer 1`] = `
}
}
onUsernameClicked={[Function]}
viewer={
Object {
"id": "viewer",
"username": "viewer",
}
}
/>
`;
@@ -11,21 +11,18 @@ import { Button, CallOut, Divider } from "coral-ui/components/v2";
import { UserHistoryDrawerAllComments_settings } from "coral-admin/__generated__/UserHistoryDrawerAllComments_settings.graphql";
import { UserHistoryDrawerAllComments_user } from "coral-admin/__generated__/UserHistoryDrawerAllComments_user.graphql";
import { UserHistoryDrawerAllComments_viewer } from "coral-admin/__generated__/UserHistoryDrawerAllComments_viewer.graphql";
import { UserHistoryDrawerAllCommentsPaginationQueryVariables } from "coral-admin/__generated__/UserHistoryDrawerAllCommentsPaginationQuery.graphql";
import styles from "./UserHistoryDrawerAllComments.css";
interface Props {
user: UserHistoryDrawerAllComments_user;
viewer: UserHistoryDrawerAllComments_viewer;
settings: UserHistoryDrawerAllComments_settings;
relay: RelayPaginationProp;
}
const UserHistoryDrawerAllComments: FunctionComponent<Props> = ({
user,
viewer,
settings,
relay,
}) => {
@@ -61,7 +58,6 @@ const UserHistoryDrawerAllComments: FunctionComponent<Props> = ({
<div key={c.id}>
<ModerateCardContainer
comment={c}
viewer={viewer}
settings={settings}
danglingLogic={status => false}
hideUsername
@@ -91,11 +87,6 @@ const enhanced = withPaginationContainer<
FragmentVariables
>(
{
viewer: graphql`
fragment UserHistoryDrawerAllComments_viewer on User {
...ModerateCardContainer_viewer
}
`,
settings: graphql`
fragment UserHistoryDrawerAllComments_settings on Settings {
...ModerateCardContainer_settings
@@ -24,9 +24,6 @@ const UserHistoryDrawerAllCommentsQuery: FunctionComponent<Props> = ({
user(id: $userID) {
...UserHistoryDrawerAllComments_user
}
viewer {
...UserHistoryDrawerAllComments_viewer
}
settings {
...UserHistoryDrawerAllComments_settings
}
@@ -57,8 +54,6 @@ const UserHistoryDrawerAllCommentsQuery: FunctionComponent<Props> = ({
return (
<UserHistoryDrawerAllComments
// We can never get to this part of the UI without being logged in.
viewer={props.viewer!}
settings={props.settings}
user={props.user}
/>
@@ -11,21 +11,18 @@ import { Button, CallOut, Divider } from "coral-ui/components/v2";
import { UserHistoryDrawerRejectedComments_settings } from "coral-admin/__generated__/UserHistoryDrawerRejectedComments_settings.graphql";
import { UserHistoryDrawerRejectedComments_user } from "coral-admin/__generated__/UserHistoryDrawerRejectedComments_user.graphql";
import { UserHistoryDrawerRejectedComments_viewer } from "coral-admin/__generated__/UserHistoryDrawerRejectedComments_viewer.graphql";
import { UserHistoryDrawerRejectedCommentsPaginationQueryVariables } from "coral-admin/__generated__/UserHistoryDrawerRejectedCommentsPaginationQuery.graphql";
import styles from "./UserHistoryDrawerRejectedComments.css";
interface Props {
user: UserHistoryDrawerRejectedComments_user;
viewer: UserHistoryDrawerRejectedComments_viewer;
settings: UserHistoryDrawerRejectedComments_settings;
relay: RelayPaginationProp;
}
const UserHistoryDrawerRejectedComments: FunctionComponent<Props> = ({
user,
viewer,
settings,
relay,
}) => {
@@ -63,7 +60,6 @@ const UserHistoryDrawerRejectedComments: FunctionComponent<Props> = ({
<div key={c.id}>
<ModerateCardContainer
comment={c}
viewer={viewer}
settings={settings}
danglingLogic={status => false}
hideUsername
@@ -93,11 +89,6 @@ const enhanced = withPaginationContainer<
FragmentVariables
>(
{
viewer: graphql`
fragment UserHistoryDrawerRejectedComments_viewer on User {
...ModerateCardContainer_viewer
}
`,
settings: graphql`
fragment UserHistoryDrawerRejectedComments_settings on Settings {
...ModerateCardContainer_settings
@@ -22,9 +22,6 @@ const UserHistoryDrawerRejectedCommentsQuery: FunctionComponent<Props> = ({
user(id: $userID) {
...UserHistoryDrawerRejectedComments_user
}
viewer {
...UserHistoryDrawerRejectedComments_viewer
}
settings {
...UserHistoryDrawerRejectedComments_settings
}
@@ -55,8 +52,6 @@ const UserHistoryDrawerRejectedCommentsQuery: FunctionComponent<Props> = ({
return (
<UserHistoryDrawerRejectedComments
// We can never get to this part of the UI without being logged in.
viewer={props.viewer!}
settings={props.settings}
user={props.user}
/>
@@ -17,6 +17,9 @@ extend type Comment {
# If true then Comment came in live.
enteredLive: Boolean
# If true then the Comment status was updated by the viewer.
viewerDidModerate: Boolean
}
extend type CommentsConnection {
@@ -40,15 +40,7 @@ const ApproveCommentMutation = createMutation(
}
}
}
statusHistory(first: 1) {
edges {
node {
moderator {
username
}
}
}
}
...ModeratedByContainer_comment
}
moderationQueues(storyID: $storyID) {
unmoderated {
@@ -74,7 +66,9 @@ const ApproveCommentMutation = createMutation(
storyID: input.storyID,
},
optimisticUpdater: store => {
store.get(input.commentID)!.setValue("APPROVED", "status");
const proxy = store.get(input.commentID)!;
proxy.setValue("APPROVED", "status");
proxy.setValue(true, "viewerDidModerate");
},
updater: store => {
const connections = [
@@ -40,15 +40,7 @@ const RejectCommentMutation = createMutation(
}
}
}
statusHistory(first: 1) {
edges {
node {
moderator {
username
}
}
}
}
...ModeratedByContainer_comment
}
moderationQueues(storyID: $storyID) {
unmoderated {
@@ -74,7 +66,9 @@ const RejectCommentMutation = createMutation(
storyID: input.storyID,
},
optimisticUpdater: store => {
store.get(input.commentID)!.setValue("REJECTED", "status");
const proxy = store.get(input.commentID)!;
proxy.setValue("REJECTED", "status");
proxy.setValue(true, "viewerDidModerate");
},
updater: store => {
const connections = [
@@ -37,7 +37,6 @@ export class ApprovedQueueRoute extends React.Component<
return (
<IntersectionProvider>
<Queue
viewer={this.props.query.viewer!}
settings={this.props.query.settings}
comments={comments}
onLoadMore={this.loadMore}
@@ -107,9 +106,6 @@ const enhanced = (withPaginationContainer<
settings {
...ModerateCardContainer_settings
}
viewer {
...ModerateCardContainer_viewer
}
}
`,
},
@@ -17,7 +17,6 @@ it("renders correctly with load more", () => {
hasLoadMore: true,
disableLoadMore: false,
danglingLogic: () => true,
viewer: { id: "me", username: "Mirai" },
onViewNew: noop,
};
const renderer = createRenderer();
@@ -33,7 +32,6 @@ it("renders correctly without load more", () => {
hasLoadMore: false,
disableLoadMore: false,
danglingLogic: () => true,
viewer: { id: "me", username: "Mirai" },
onViewNew: noop,
};
const renderer = createRenderer();
@@ -19,7 +19,6 @@ interface Props {
{ id: string } & PropTypesOf<typeof ModerateCardContainer>["comment"]
>;
settings: PropTypesOf<typeof ModerateCardContainer>["settings"];
viewer: PropTypesOf<typeof ModerateCardContainer>["viewer"];
onLoadMore: () => void;
onViewNew?: () => void;
hasLoadMore: boolean;
@@ -39,7 +38,6 @@ const Queue: FunctionComponent<Props> = ({
danglingLogic,
emptyElement,
allStories,
viewer,
viewNewCount,
onViewNew,
}) => {
@@ -136,7 +134,6 @@ const Queue: FunctionComponent<Props> = ({
<ModerateCardContainer
key={comment.id}
settings={settings}
viewer={viewer}
comment={comment}
danglingLogic={danglingLogic}
showStoryInfo={Boolean(allStories)}
@@ -15,7 +15,6 @@ import { GQLMODERATION_QUEUE } from "coral-framework/schema";
import { QueueRoute_queue } from "coral-admin/__generated__/QueueRoute_queue.graphql";
import { QueueRoute_settings } from "coral-admin/__generated__/QueueRoute_settings.graphql";
import { QueueRoute_viewer } from "coral-admin/__generated__/QueueRoute_viewer.graphql";
import { QueueRoutePaginationPendingQueryVariables } from "coral-admin/__generated__/QueueRoutePaginationPendingQuery.graphql";
import EmptyMessage from "./EmptyMessage";
@@ -30,7 +29,6 @@ interface Props {
queueName: GQLMODERATION_QUEUE;
queue: QueueRoute_queue | null;
settings: QueueRoute_settings | null;
viewer: QueueRoute_viewer | null;
relay: RelayPaginationProp;
emptyElement: React.ReactElement;
storyID?: string;
@@ -90,7 +88,6 @@ export const QueueRoute: FunctionComponent<Props> = props => {
<IntersectionProvider>
<Queue
comments={comments}
viewer={props.viewer!}
settings={props.settings!}
onLoadMore={loadMore}
hasLoadMore={props.relay.hasMore()}
@@ -128,7 +125,6 @@ const createQueueRoute = (
queueName={queueName}
queue={null}
settings={null}
viewer={null}
emptyElement={emptyElement}
storyID={match.params.storyID}
siteID={match.params.siteID}
@@ -143,7 +139,6 @@ const createQueueRoute = (
queueName={queueName}
queue={queue}
settings={data.settings}
viewer={data.viewer}
emptyElement={emptyElement}
storyID={match.params.storyID}
siteID={match.params.siteID}
@@ -183,11 +178,6 @@ const createQueueRoute = (
...ModerateCardContainer_settings
}
`,
viewer: graphql`
fragment QueueRoute_viewer on User {
...ModerateCardContainer_viewer
}
`,
},
{
direction: "forward",
@@ -228,9 +218,6 @@ export const PendingQueueRoute = createQueueRoute(
settings {
...QueueRoute_settings
}
viewer {
...QueueRoute_viewer
}
}
`,
graphql`
@@ -269,9 +256,6 @@ export const ReportedQueueRoute = createQueueRoute(
settings {
...QueueRoute_settings
}
viewer {
...QueueRoute_viewer
}
}
`,
graphql`
@@ -310,9 +294,6 @@ export const UnmoderatedQueueRoute = createQueueRoute(
settings {
...QueueRoute_settings
}
viewer {
...QueueRoute_viewer
}
}
`,
graphql`
@@ -37,7 +37,6 @@ export class RejectedQueueRoute extends React.Component<
return (
<IntersectionProvider>
<Queue
viewer={this.props.query.viewer!}
settings={this.props.query.settings}
comments={comments}
onLoadMore={this.loadMore}
@@ -107,9 +106,6 @@ const enhanced = (withPaginationContainer<
settings {
...ModerateCardContainer_settings
}
viewer {
...ModerateCardContainer_viewer
}
}
`,
},
@@ -38,7 +38,6 @@ const SingleModerateRoute: FunctionComponent<Props> = props => {
<Queue
comments={[props.comment]}
settings={props.settings}
viewer={props.viewer!}
onLoadMore={noop}
hasLoadMore={false}
disableLoadMore={false}
@@ -58,9 +57,6 @@ const enhanced = withRouteConfig<Props, SingleModerateRouteQueryResponse>({
settings {
...ModerateCardContainer_settings
}
viewer {
...ModerateCardContainer_viewer
}
}
`,
cacheConfig: { force: true },
@@ -524,6 +524,27 @@ exports[`renders rejected queue with comments 1`] = `
</i>
</button>
</div>
<button
className="BaseButton-root"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<div
className="ModeratedByContainer-moderatedBy"
>
Moderated By
</div>
<div
className="ModeratedByContainer-moderatedByUsername"
>
System
</div>
</button>
</div>
</div>
</div>
@@ -774,6 +795,27 @@ exports[`renders rejected queue with comments 1`] = `
</i>
</button>
</div>
<button
className="BaseButton-root"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<div
className="ModeratedByContainer-moderatedBy"
>
Moderated By
</div>
<div
className="ModeratedByContainer-moderatedByUsername"
>
System
</div>
</button>
</div>
</div>
</div>
@@ -1043,6 +1085,27 @@ exports[`renders rejected queue with comments and load more 1`] = `
</i>
</button>
</div>
<button
className="BaseButton-root"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
type="button"
>
<div
className="ModeratedByContainer-moderatedBy"
>
Moderated By
</div>
<div
className="ModeratedByContainer-moderatedByUsername"
>
System
</div>
</button>
</div>
</div>
</div>
@@ -382,7 +382,8 @@ it("approves comment in reported queue", async () => {
{
node: {
id: "mod-action",
author: {
status: GQLCOMMENT_STATUS.APPROVED,
moderator: {
id: viewer.id,
username: viewer.username,
},
@@ -479,7 +480,8 @@ it("rejects comment in reported queue", async () => {
{
node: {
id: "mod-action",
author: {
status: GQLCOMMENT_STATUS.REJECTED,
moderator: {
id: viewer.id,
username: viewer.username,
},
@@ -248,7 +248,8 @@ it("approves comment in rejected queue", async () => {
{
node: {
id: "mod-action",
author: {
status: GQLCOMMENT_STATUS.APPROVED,
moderator: {
id: viewer.id,
username: viewer.username,
},
@@ -102,7 +102,8 @@ it("approves single comment", async () => {
{
node: {
id: "mod-action",
author: {
status: GQLCOMMENT_STATUS.APPROVED,
moderator: {
id: viewer.id,
username: viewer.username,
},
@@ -155,6 +156,7 @@ it("rejects single comment", async () => {
{
node: {
id: "mod-action",
status: GQLCOMMENT_STATUS.REJECTED,
author: {
id: viewer.id,
username: viewer.username,