mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
[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:
@@ -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 commenter’s 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"
|
||||
>
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user