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`] = `
+
+
+
+ check_circle
+
+
+ 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