From 33556044a8f8ec6b731c19ee09753feb8745486c Mon Sep 17 00:00:00 2001 From: Tessa Thornton Date: Wed, 19 Feb 2020 16:37:51 -0500 Subject: [PATCH] [CORL-919] approved queue (#2848) * show comment counts for stories in story table * remove debug code * add 'approved' moderation queue to moderation tabs * add site/story specific routes * Revert "remove debug code" This reverts commit ed3a44304d225c79336423f34d0bde57a998cd69. * Revert "show comment counts for stories in story table" This reverts commit 61eb00c70c873fd2d10f1476601c7a69ae12651d. --- .../admin/helpers/getQueueConnection.ts | 9 +- .../admin/mutations/RejectCommentMutation.ts | 1 + src/core/client/admin/routeConfig.tsx | 10 ++ .../ModerateNavigation/Navigation.tsx | 7 + .../Moderate/Queue/ApprovedQueueRoute.tsx | 163 ++++++++++++++++++ .../admin/routes/Moderate/Queue/index.ts | 1 + .../__snapshots__/tabBar.spec.tsx.snap | 19 ++ .../framework/helpers/getModerationLink.ts | 7 +- src/locales/en-US/admin.ftl | 2 + 9 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 src/core/client/admin/routes/Moderate/Queue/ApprovedQueueRoute.tsx diff --git a/src/core/client/admin/helpers/getQueueConnection.ts b/src/core/client/admin/helpers/getQueueConnection.ts index bded7865a..6c06de126 100644 --- a/src/core/client/admin/helpers/getQueueConnection.ts +++ b/src/core/client/admin/helpers/getQueueConnection.ts @@ -12,7 +12,7 @@ import { export default function getQueueConnection( store: RecordSourceSelectorProxy | RecordSourceProxy, - queue: GQLMODERATION_QUEUE_RL | "REJECTED", + queue: GQLMODERATION_QUEUE_RL | "REJECTED" | "APPROVED", storyID?: string | null, siteID?: string | null ): RecordProxy | null | undefined { @@ -24,6 +24,13 @@ export default function getQueueConnection( siteID, }); } + if (queue === "APPROVED") { + return ConnectionHandler.getConnection(root, "ApprovedQueue_comments", { + status: GQLCOMMENT_STATUS.APPROVED, + storyID, + siteID, + }); + } const queuesRecord = root.getLinkedRecord("moderationQueues", { storyID, siteID, diff --git a/src/core/client/admin/mutations/RejectCommentMutation.ts b/src/core/client/admin/mutations/RejectCommentMutation.ts index 5e4deaef1..369d7d788 100644 --- a/src/core/client/admin/mutations/RejectCommentMutation.ts +++ b/src/core/client/admin/mutations/RejectCommentMutation.ts @@ -81,6 +81,7 @@ const RejectCommentMutation = createMutation( getQueueConnection(store, "REPORTED", input.storyID), getQueueConnection(store, "PENDING", input.storyID), getQueueConnection(store, "UNMODERATED", input.storyID), + getQueueConnection(store, "APPROVED", input.storyID), ].filter(c => c); connections.forEach(con => ConnectionHandler.deleteNode(con!, input.commentID) diff --git a/src/core/client/admin/routeConfig.tsx b/src/core/client/admin/routeConfig.tsx index 48e6ef332..2896c3793 100644 --- a/src/core/client/admin/routeConfig.tsx +++ b/src/core/client/admin/routeConfig.tsx @@ -30,6 +30,7 @@ import InviteRoute from "./routes/Invite"; import LoginRoute from "./routes/Login"; import ModerateRoute from "./routes/Moderate"; import { + ApprovedQueueRoute, PendingQueueRoute, RejectedQueueRoute, ReportedQueueRoute, @@ -78,6 +79,7 @@ export default makeRouteConfig( path="unmoderated/sites/:siteID" {...UnmoderatedQueueRoute.routeConfig} /> + + + = ({ getModerationLink({ queue: "reported", storyID, siteID }), getModerationLink({ queue: "pending", storyID, siteID }), getModerationLink({ queue: "unmoderated", storyID, siteID }), + getModerationLink({ queue: "approved", storyID, siteID }), getModerationLink({ queue: "rejected", storyID, siteID }), ]; }, [storyID, siteID]); @@ -114,6 +115,12 @@ const Navigation: FunctionComponent = ({ )} + check_circle + + Approved + + + cancel Rejected diff --git a/src/core/client/admin/routes/Moderate/Queue/ApprovedQueueRoute.tsx b/src/core/client/admin/routes/Moderate/Queue/ApprovedQueueRoute.tsx new file mode 100644 index 000000000..176553289 --- /dev/null +++ b/src/core/client/admin/routes/Moderate/Queue/ApprovedQueueRoute.tsx @@ -0,0 +1,163 @@ +import { Localized } from "@fluent/react/compat"; +import { RouteProps } from "found"; +import React from "react"; +import { graphql, RelayPaginationProp } from "react-relay"; + +import { IntersectionProvider } from "coral-framework/lib/intersection"; +import { withPaginationContainer } from "coral-framework/lib/relay"; + +import { ApprovedQueueRoute_query } from "coral-admin/__generated__/ApprovedQueueRoute_query.graphql"; +import { ApprovedQueueRoutePaginationQueryVariables } from "coral-admin/__generated__/ApprovedQueueRoutePaginationQuery.graphql"; + +import EmptyMessage from "./EmptyMessage"; +import LoadingQueue from "./LoadingQueue"; +import Queue from "./Queue"; + +interface ApprovedQueueRouteProps { + query: ApprovedQueueRoute_query; + relay: RelayPaginationProp; + storyID?: string; +} + +// TODO: use generated types +const danglingLogic = (status: string) => ["REJECTED"].includes(status); + +export class ApprovedQueueRoute extends React.Component< + ApprovedQueueRouteProps +> { + public static routeConfig: RouteProps; + + public state = { + disableLoadMore: false, + }; + + public render() { + const comments = this.props.query.comments.edges.map(edge => edge.node); + return ( + + + There are no approved comments. + + } + allStories={!this.props.storyID} + /> + + ); + } + + private loadMore = () => { + if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { + return; + } + this.setState({ disableLoadMore: true }); + this.props.relay.loadMore( + 10, // Fetch the next 10 feed items + error => { + this.setState({ disableLoadMore: false }); + if (error) { + // eslint-disable-next-line no-console + console.error(error); + } + } + ); + }; +} + +// TODO: (cvle) If this could be autogenerated.. +type FragmentVariables = ApprovedQueueRoutePaginationQueryVariables; + +const enhanced = (withPaginationContainer< + ApprovedQueueRouteProps, + ApprovedQueueRoutePaginationQueryVariables, + FragmentVariables +>( + { + query: graphql` + fragment ApprovedQueueRoute_query on Query + @argumentDefinitions( + count: { type: "Int!", defaultValue: 5 } + cursor: { type: "Cursor" } + storyID: { type: "ID" } + ) { + comments( + status: APPROVED + storyID: $storyID + first: $count + after: $cursor + ) @connection(key: "ApprovedQueue_comments") { + edges { + node { + id + ...ModerateCardContainer_comment + } + } + } + settings { + ...ModerateCardContainer_settings + } + viewer { + ...ModerateCardContainer_viewer + } + } + `, + }, + { + direction: "forward", + getConnectionFromProps(props) { + return props.query && props.query.comments; + }, + // This is also the default implementation of `getFragmentVariables` if it isn't provided. + getFragmentVariables(prevVars, totalCount) { + return { + ...prevVars, + count: totalCount, + }; + }, + getVariables(props, { count, cursor }, fragmentVariables) { + return { + ...fragmentVariables, + count, + cursor, + }; + }, + query: graphql` + # Pagination query to be fetched upon calling 'loadMore'. + # Notice that we re-use our fragment, and the shape of this query matches our fragment spec. + query ApprovedQueueRoutePaginationQuery( + $storyID: ID + $count: Int! + $cursor: Cursor + ) { + ...ApprovedQueueRoute_query + @arguments(storyID: $storyID, count: $count, cursor: $cursor) + } + `, + } +)(ApprovedQueueRoute) as any) as typeof ApprovedQueueRoute; + +enhanced.routeConfig = { + Component: enhanced, + query: graphql` + query ApprovedQueueRouteQuery($storyID: ID) { + ...ApprovedQueueRoute_query @arguments(storyID: $storyID) + } + `, + cacheConfig: { force: true }, + render: function RejectedRouteRender({ Component, props, match }) { + if (Component && props) { + return ; + } + return ; + }, +}; + +export default enhanced; diff --git a/src/core/client/admin/routes/Moderate/Queue/index.ts b/src/core/client/admin/routes/Moderate/Queue/index.ts index e3aebfb76..edf8631dc 100644 --- a/src/core/client/admin/routes/Moderate/Queue/index.ts +++ b/src/core/client/admin/routes/Moderate/Queue/index.ts @@ -4,5 +4,6 @@ export { ReportedQueueRoute, } from "./QueueRoute"; export { default as RejectedQueueRoute } from "./RejectedQueueRoute"; +export { default as ApprovedQueueRoute } from "./ApprovedQueueRoute"; export { default as LoadingQueue } from "./LoadingQueue"; export { default as Queue } from "./Queue"; diff --git a/src/core/client/admin/test/moderate/__snapshots__/tabBar.spec.tsx.snap b/src/core/client/admin/test/moderate/__snapshots__/tabBar.spec.tsx.snap index fd02836fa..4b5996715 100644 --- a/src/core/client/admin/test/moderate/__snapshots__/tabBar.spec.tsx.snap +++ b/src/core/client/admin/test/moderate/__snapshots__/tabBar.spec.tsx.snap @@ -101,6 +101,25 @@ exports[`tab bar renders tab bar (empty queues) 1`] = ` +
  • + + + + approved + + +
  • diff --git a/src/core/client/framework/helpers/getModerationLink.ts b/src/core/client/framework/helpers/getModerationLink.ts index 240b27811..c283195a4 100644 --- a/src/core/client/framework/helpers/getModerationLink.ts +++ b/src/core/client/framework/helpers/getModerationLink.ts @@ -1,6 +1,11 @@ import urls from "./urls"; -export type QUEUE_NAME = "reported" | "pending" | "unmoderated" | "rejected"; +export type QUEUE_NAME = + | "reported" + | "pending" + | "unmoderated" + | "rejected" + | "approved"; interface Options { queue?: QUEUE_NAME; diff --git a/src/locales/en-US/admin.ftl b/src/locales/en-US/admin.ftl index 6c9608106..155d9ad33 100644 --- a/src/locales/en-US/admin.ftl +++ b/src/locales/en-US/admin.ftl @@ -560,6 +560,7 @@ moderate-navigation-reported = reported moderate-navigation-pending = Pending moderate-navigation-unmoderated = unmoderated moderate-navigation-rejected = rejected +moderate-navigation-approved = approved moderate-navigation-comment-count = { SHORT_NUMBER($count) } moderate-marker-preMod = Pre-mod @@ -592,6 +593,7 @@ moderate-emptyQueue-pending = Nicely done! There are no more pending comments to moderate-emptyQueue-reported = Nicely done! There are no more reported comments to moderate. moderate-emptyQueue-unmoderated = Nicely done! All comments have been moderated. moderate-emptyQueue-rejected = There are no rejected comments. +moderate-emptyQueue-approved = There are no approved comments. moderate-comment-edited = (edited) moderate-comment-inReplyTo = Reply to