[CORL-404] Recent Comment History (#2354)

* feat: initial support for auto pre-moderation

* chore: refactor collection access

* fix: linting

* fix: rebasing issue

* fix: exported helpers

* feat: added extensions, lintd

* fix: rebase fix

* feat: renamed automaticPreModeration to recentCommentHistory

* feat: initial implementation of admin config

* feat: support recent history markers

* feat: rename visible to published

* feat: reworked history drawer

* chore: extracted tooltip

* feat: implemented user drawer

* fix: fixed translation key

* fix: resolved issue with NaN
This commit is contained in:
Wyatt Johnson
2019-08-08 18:18:18 +00:00
committed by GitHub
parent ff964b54a3
commit 4c65d43954
103 changed files with 2856 additions and 1917 deletions
@@ -3,7 +3,7 @@ import { createFarceRouter } from "found";
import { Resolver } from "found-relay";
import React, { FunctionComponent } from "react";
import UserHistoryDrawerContainer from "coral-admin/components/UserHistoryDrawer/UserHistoryDrawerContainer";
import UserHistoryDrawer from "coral-admin/components/UserHistoryDrawer";
import { CoralContextConsumer } from "coral-framework/lib/bootstrap/CoralContext";
import { makeRouteConfig, Route } from "found";
import { ConnectedRouter } from "found";
@@ -21,7 +21,7 @@ const harnessRouter = (userID: string): ConnectedRouter => {
routeConfig,
renderReady: ({ elements }) => (
<div data-testid="test-container">
<UserHistoryDrawerContainer
<UserHistoryDrawer
userID={userID}
open
onClose={() => {
@@ -116,6 +116,178 @@ exports[`renders configure moderation 1`] = `
className="Box-root HorizontalGutter-root HorizontalGutter-double"
data-testid="configure-moderationContainer"
>
<fieldset
className="FieldSet-root Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
>
<legend
className="Box-root Typography-root Typography-heading1 Typography-colorTextPrimary Header-root"
>
Recent comment history
</legend>
<fieldset
className="FieldSet-root Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<legend
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Recent comment history timeframe
</legend>
<p
className="Box-root Typography-root Typography-detail Typography-colorTextSecondary"
>
Time period over which a commenter's rejection rate is calcualted
and submitted comments are counted.
</p>
<div
className="Box-root Flex-root Flex-flex Flex-itemGutter gutter"
>
<div
className="TextField-root DurationField-value"
>
<input
aria-label="value"
autoCapitalize="off"
autoComplete="off"
autoCorrect="off"
className="TextField-input TextField-colorRegular TextField-textAlignCenter"
disabled={false}
name="recentCommentHistory.timeFrame-value"
onChange={[Function]}
placeholder=""
spellCheck={false}
type="text"
value="7"
/>
<div
className="TextField-adornment"
>
<p
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
Days
</p>
</div>
</div>
</div>
</fieldset>
<fieldset
className="FieldSet-root Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<legend
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Recent comment history filter
</legend>
<p
className="Box-root Typography-root Typography-detail Typography-colorTextSecondary"
>
Prevents repeat offenders from publishing comments without approval.
After a commenter's rejection rate rises above the defined threshold
below, their next submitted comments are
<strong>
sent to Pending for
moderator approval.
</strong>
The filter is removed when their rejection rate
falls below the threshold.
</p>
<div>
<div
className="Box-root Flex-root RadioButton-root Flex-flex Flex-alignCenter"
>
<input
checked={false}
className="RadioButton-input"
disabled={false}
id="recentCommentHistory.enabled-true"
name="recentCommentHistory.enabled"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
type="radio"
value={true}
/>
<label
className="RadioButton-label"
htmlFor="recentCommentHistory.enabled-true"
>
<span>
On
</span>
</label>
</div>
<div
className="Box-root Flex-root RadioButton-root Flex-flex Flex-alignCenter"
>
<input
checked={true}
className="RadioButton-input"
disabled={false}
id="recentCommentHistory.enabled-false"
name="recentCommentHistory.enabled"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
type="radio"
value={false}
/>
<label
className="RadioButton-label"
htmlFor="recentCommentHistory.enabled-false"
>
<span>
Off
</span>
</label>
</div>
</div>
</fieldset>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Rejection rate threshold
</label>
<p
className="Box-root Typography-root Typography-detail Typography-colorTextSecondary"
>
Calculated by the number of rejected comments divided by the sum of
a commenters rejected and published comments, over the recent
comment history timeframe (does not include comments pending for
toxicity, spam or pre-moderation.)
</p>
<div
className="TextField-root RecentCommentHistoryConfig-thresholdTextField"
>
<input
autoCapitalize="off"
autoComplete="off"
autoCorrect="off"
className="TextField-input TextField-colorRegular TextField-textAlignCenter"
disabled={false}
name="recentCommentHistory.triggerRejectionRate"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder=""
spellCheck={false}
type="text"
value="30"
/>
<div
className="TextField-adornment"
>
<p
className="Box-root Typography-root Typography-bodyCopy Typography-colorTextPrimary"
>
%
</p>
</div>
</div>
</div>
</fieldset>
<fieldset
className="FieldSet-root Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
>
+22 -1
View File
@@ -61,6 +61,14 @@ export const settings = createFixture<GQLSettings>({
url: "https://test.com/",
contactEmail: "coral@test.com",
},
recentCommentHistory: {
enabled: false,
// 7 days in seconds.
timeFrame: 604800,
// Rejection rate defaulting to 30%, once exceeded, comments will be
// pre-moderated.
triggerRejectionRate: 0.3,
},
integrations: {
akismet: {
enabled: false,
@@ -292,6 +300,16 @@ export const baseUser = createFixture<GQLUser>({
},
});
const recentCommentHistory = {
statuses: {
APPROVED: 0,
REJECTED: 0,
NONE: 0,
PREMOD: 0,
SYSTEM_WITHHELD: 0,
},
};
export const users = {
admins: createFixtures<GQLUser>(
[
@@ -337,6 +355,7 @@ export const users = {
email: "isabelle@test.com",
role: GQLUSER_ROLE.COMMENTER,
ignoreable: true,
recentCommentHistory,
},
{
id: "user-commenter-1",
@@ -344,6 +363,7 @@ export const users = {
email: "ngoc@test.com",
role: GQLUSER_ROLE.COMMENTER,
ignoreable: true,
recentCommentHistory,
},
{
id: "user-commenter-2",
@@ -351,6 +371,7 @@ export const users = {
email: "max@test.com",
role: GQLUSER_ROLE.COMMENTER,
ignoreable: true,
recentCommentHistory,
},
],
baseUser
@@ -458,7 +479,7 @@ export const baseComment = createFixture<GQLComment>({
reasons: {
COMMENT_DETECTED_TOXIC: 0,
COMMENT_DETECTED_SPAM: 0,
COMMENT_DETECTED_TRUST: 0,
COMMENT_DETECTED_RECENT_HISTORY: 0,
COMMENT_DETECTED_LINKS: 0,
COMMENT_DETECTED_BANNED_WORD: 0,
COMMENT_DETECTED_SUSPECT_WORD: 0,
@@ -149,6 +149,7 @@ it("rejects single comment", async () => {
comment: {
id: comment.id,
status: GQLCOMMENT_STATUS.REJECTED,
author: comment.author,
statusHistory: {
edges: [
{