mirror of
https://github.com/wassname/talk.git
synced 2026-06-27 22:24:06 +08:00
[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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user