[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.
This commit is contained in:
Tessa Thornton
2020-02-19 16:37:51 -05:00
committed by GitHub
parent d883ab029c
commit 33556044a8
9 changed files with 217 additions and 2 deletions
@@ -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,
@@ -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)
+10
View File
@@ -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}
/>
<Route path="approved" {...ApprovedQueueRoute.routeConfig} />
<Route path="rejected" {...RejectedQueueRoute.routeConfig} />
<Route
path="rejected/stories/:storyID"
@@ -87,6 +89,14 @@ export default makeRouteConfig(
path="rejected/sites/:siteID"
{...RejectedQueueRoute.routeConfig}
/>
<Route
path="approved/stories/:storyID"
{...ApprovedQueueRoute.routeConfig}
/>
<Route
path="approved/sites/:siteID"
{...ApprovedQueueRoute.routeConfig}
/>
<Redirect
from="stories/:storyID"
to="/admin/moderate/reported/stories/:storyID"
@@ -34,6 +34,7 @@ const Navigation: FunctionComponent<Props> = ({
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<Props> = ({
)}
</NavigationLink>
<NavigationLink to={moderationLinks[3]}>
<Icon>check_circle</Icon>
<Localized id="moderate-navigation-approved">
<span>Approved</span>
</Localized>
</NavigationLink>
<NavigationLink to={moderationLinks[4]}>
<Icon>cancel</Icon>
<Localized id="moderate-navigation-rejected">
<span>Rejected</span>
@@ -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 (
<IntersectionProvider>
<Queue
viewer={this.props.query.viewer!}
settings={this.props.query.settings}
comments={comments}
onLoadMore={this.loadMore}
hasLoadMore={this.props.relay.hasMore()}
disableLoadMore={this.state.disableLoadMore}
danglingLogic={danglingLogic}
emptyElement={
<Localized id="moderate-emptyQueue-approved">
<EmptyMessage>There are no approved comments.</EmptyMessage>
</Localized>
}
allStories={!this.props.storyID}
/>
</IntersectionProvider>
);
}
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 <Component query={props} storyID={match.params.storyID} />;
}
return <LoadingQueue />;
},
};
export default enhanced;
@@ -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";
@@ -101,6 +101,25 @@ exports[`tab bar renders tab bar (empty queues) 1`] = `
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
<a
className="NavigationItem-anchor"
href="/admin/moderate/approved"
onClick={[Function]}
>
<i
aria-hidden="true"
className="Icon-root Icon-sm"
>
check_circle
</i>
<span>
approved
</span>
</a>
</li>
<li
className="NavigationItem-root"
>
@@ -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;
+2
View File
@@ -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 <Username></Username>