mirror of
https://github.com/wassname/talk.git
synced 2026-08-06 13:41:02 +08:00
[CORL-664] Threshold moderate new commenters (#2752)
* add new commenters config * fix specs and fixtures, add translation strings * save whether a commenter is new * fix specs and snaps * add admin role to new config options * Update copy * remvoe unused ref * feat: initial impl * Create preliminary comment moderation slices CORL-688 * Move slices logic into stacks CORL-688 * Create user comment counts CORL-688 * Create naive mutation that initializes user comment counts CORL-688 * Use bulk updates in user counts migration CORL-688 * fix: review * fix: fixed issue with aggregation * Migrate creating comment into stacks CORL-688 * Migrate editing a comment to the stacks CORL-688 * Break publishing comment status out of updateAllCounts CORL-688 * review: removed variable scoping in favor of export * revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7 * review: simplification of stacks implementation This simplifies the stacks implementation to better reuse code related to count management and event publishing. This can be used to great effect with the upcomming events PR #2738. * Remove un-necessary isNew flags on users CORL-664 * review: removed variable scoping in favor of export * revert: feb8e8196cd448f5cd24f1ca2eb0b91fe9bd43c7 * review: simplification of stacks implementation This simplifies the stacks implementation to better reuse code related to count management and event publishing. This can be used to great effect with the upcomming events PR #2738. * fix: check if authorID is null before update user counts CORL-688 * fix: addressed bug in shared count retrival Co-authored-by: Tessa Thornton <tessathornton@gmail.com> Co-authored-by: Wyatt Johnson <accounts+github@wyattjoh.ca>
This commit is contained in:
co-authored by
Tessa Thornton
Wyatt Johnson
parent
e3e2e0f52e
commit
d26e331a4f
@@ -28,6 +28,7 @@ it("renders all markers", () => {
|
||||
COMMENT_DETECTED_SUSPECT_WORD: 1,
|
||||
COMMENT_REPORTED_OFFENSIVE: 2,
|
||||
COMMENT_REPORTED_SPAM: 3,
|
||||
COMMENT_DETECTED_NEW_COMMENTER: 0,
|
||||
COMMENT_DETECTED_REPEAT_POST: 1,
|
||||
},
|
||||
},
|
||||
@@ -72,6 +73,7 @@ it("renders some markers", () => {
|
||||
COMMENT_DETECTED_SUSPECT_WORD: 0,
|
||||
COMMENT_REPORTED_OFFENSIVE: 2,
|
||||
COMMENT_REPORTED_SPAM: 0,
|
||||
COMMENT_DETECTED_NEW_COMMENTER: 0,
|
||||
COMMENT_DETECTED_REPEAT_POST: 0,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -120,6 +120,14 @@ const markers: Array<
|
||||
</Marker>
|
||||
)) ||
|
||||
null,
|
||||
c =>
|
||||
(c.revision &&
|
||||
c.revision.actionCounts.flag.reasons.COMMENT_DETECTED_NEW_COMMENTER && (
|
||||
<Localized id="moderate-marker-newCommenter" key={keyCounter++}>
|
||||
<Marker color="reported">New commenter</Marker>
|
||||
</Localized>
|
||||
)) ||
|
||||
null,
|
||||
];
|
||||
|
||||
export class MarkersContainer extends React.Component<MarkersContainerProps> {
|
||||
@@ -170,6 +178,7 @@ const enhanced = withFragmentContainer<MarkersContainerProps>({
|
||||
COMMENT_DETECTED_SUSPECT_WORD
|
||||
COMMENT_REPORTED_OFFENSIVE
|
||||
COMMENT_REPORTED_SPAM
|
||||
COMMENT_DETECTED_NEW_COMMENTER
|
||||
COMMENT_DETECTED_REPEAT_POST
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -15,6 +15,7 @@ exports[`renders all markers 1`] = `
|
||||
"reasons": Object {
|
||||
"COMMENT_DETECTED_BANNED_WORD": 1,
|
||||
"COMMENT_DETECTED_LINKS": 1,
|
||||
"COMMENT_DETECTED_NEW_COMMENTER": 0,
|
||||
"COMMENT_DETECTED_RECENT_HISTORY": 1,
|
||||
"COMMENT_DETECTED_REPEAT_POST": 1,
|
||||
"COMMENT_DETECTED_SPAM": 1,
|
||||
@@ -170,6 +171,7 @@ exports[`renders some markers 1`] = `
|
||||
"reasons": Object {
|
||||
"COMMENT_DETECTED_BANNED_WORD": 1,
|
||||
"COMMENT_DETECTED_LINKS": 0,
|
||||
"COMMENT_DETECTED_NEW_COMMENTER": 0,
|
||||
"COMMENT_DETECTED_RECENT_HISTORY": 1,
|
||||
"COMMENT_DETECTED_REPEAT_POST": 0,
|
||||
"COMMENT_DETECTED_SPAM": 0,
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ import { HorizontalGutter } from "coral-ui/components";
|
||||
import { ModerationConfigContainer_settings as SettingsData } from "coral-admin/__generated__/ModerationConfigContainer_settings.graphql";
|
||||
|
||||
import AkismetConfig from "./AkismetConfig";
|
||||
import NewCommentersConfig from "./NewCommentersConfig";
|
||||
import PerspectiveConfig from "./PerspectiveConfig";
|
||||
import PreModerationConfig from "./PreModerationConfig";
|
||||
import RecentCommentHistoryConfig from "./RecentCommentHistoryConfig";
|
||||
@@ -29,6 +30,7 @@ export const ModerationConfigContainer: React.FunctionComponent<Props> = ({
|
||||
return (
|
||||
<HorizontalGutter size="double" data-testid="configure-moderationContainer">
|
||||
<PreModerationConfig disabled={submitting} />
|
||||
<NewCommentersConfig disabled={submitting} />
|
||||
<RecentCommentHistoryConfig disabled={submitting} />
|
||||
<PerspectiveConfig disabled={submitting} />
|
||||
<AkismetConfig disabled={submitting} />
|
||||
@@ -43,6 +45,7 @@ const enhanced = withFragmentContainer<Props>({
|
||||
...PerspectiveConfig_formValues @relay(mask: false)
|
||||
...PreModerationConfig_formValues @relay(mask: false)
|
||||
...RecentCommentHistoryConfig_formValues @relay(mask: false)
|
||||
...NewCommentersConfigContainer_settings @relay(mask: false)
|
||||
}
|
||||
`,
|
||||
})(ModerationConfigContainer);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.thresholdTextField {
|
||||
width: calc(6 * var(--mini-unit));
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { Field } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { ValidationMessage } from "coral-framework/lib/form";
|
||||
import {
|
||||
composeValidators,
|
||||
required,
|
||||
validateWholeNumberGreaterThan,
|
||||
} from "coral-framework/lib/validation";
|
||||
import {
|
||||
FieldSet,
|
||||
FormField,
|
||||
FormFieldDescription,
|
||||
Label,
|
||||
TextField,
|
||||
} from "coral-ui/components/v2";
|
||||
|
||||
import ConfigBox from "../../ConfigBox";
|
||||
import Header from "../../Header";
|
||||
import OnOffField from "../../OnOffField";
|
||||
|
||||
import styles from "./NewCommentersConfig.css";
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
graphql`
|
||||
fragment NewCommentersConfigContainer_settings on Settings {
|
||||
newCommenters {
|
||||
premodEnabled
|
||||
approvedCommentsThreshold
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const NewCommentersConfig: FunctionComponent<Props> = ({ disabled }) => {
|
||||
return (
|
||||
<ConfigBox
|
||||
title={
|
||||
<Localized id="configure-moderation-newCommenters-title">
|
||||
<Header container="legend">New commenter approval</Header>
|
||||
</Localized>
|
||||
}
|
||||
>
|
||||
<Localized id="configure-moderation-newCommenters-description">
|
||||
<FormFieldDescription>
|
||||
When this is active, initial comments by a new commenter will be sent
|
||||
to Pending for moderator approval before publication.
|
||||
</FormFieldDescription>
|
||||
</Localized>
|
||||
<FormField container={<FieldSet />}>
|
||||
<Localized id="configure-moderation-newCommenters-enable">
|
||||
<Label component="legend">Enable new commenter approval</Label>
|
||||
</Localized>
|
||||
<OnOffField name="newCommenters.premodEnabled" disabled={disabled} />
|
||||
</FormField>
|
||||
<FormField>
|
||||
<Localized id="configure-moderation-newCommenters-approvedCommentsThreshold">
|
||||
<Label>Number of first comments sent for approval</Label>
|
||||
</Localized>
|
||||
<Field
|
||||
name="newCommenters.approvedCommentsThreshold"
|
||||
validate={composeValidators(
|
||||
required,
|
||||
validateWholeNumberGreaterThan(1)
|
||||
)}
|
||||
>
|
||||
{({ input, meta }) => (
|
||||
<>
|
||||
<TextField
|
||||
classes={{
|
||||
input: styles.thresholdTextField,
|
||||
}}
|
||||
disabled={disabled}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck={false}
|
||||
textAlignCenter
|
||||
adornment={
|
||||
<Localized id="configure-moderation-newCommenters-comments">
|
||||
comments
|
||||
</Localized>
|
||||
}
|
||||
{...input}
|
||||
/>
|
||||
<ValidationMessage meta={meta} />
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
</ConfigBox>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewCommentersConfig;
|
||||
@@ -275,6 +275,128 @@ approved by a moderator.
|
||||
</fieldset>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div
|
||||
className="Box-root ConfigBox-root"
|
||||
>
|
||||
<div
|
||||
className="Box-root Flex-root ConfigBox-title Flex-flex Flex-justifySpaceBetween"
|
||||
>
|
||||
<div>
|
||||
<legend
|
||||
className="Header-root"
|
||||
>
|
||||
New commenter approval
|
||||
</legend>
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
className="ConfigBox-content"
|
||||
>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root HorizontalGutter-spacing-4"
|
||||
>
|
||||
<p
|
||||
className="FormFieldDescription-root"
|
||||
>
|
||||
When this is active, initial comments by a new commenter will be sent to Pending
|
||||
for moderator approval before publication.
|
||||
</p>
|
||||
<fieldset
|
||||
className="FieldSet-root Box-root HorizontalGutter-root FormField-root HorizontalGutter-spacing-2"
|
||||
>
|
||||
<legend
|
||||
className="Label-root"
|
||||
>
|
||||
Enable new commenter approval
|
||||
</legend>
|
||||
<div>
|
||||
<div
|
||||
className="Box-root Flex-root RadioButton-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
className="RadioButton-input"
|
||||
disabled={false}
|
||||
id="newCommenters.premodEnabled-true"
|
||||
name="newCommenters.premodEnabled"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="radio"
|
||||
value="true"
|
||||
/>
|
||||
<label
|
||||
className="RadioButton-label"
|
||||
htmlFor="newCommenters.premodEnabled-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="newCommenters.premodEnabled-false"
|
||||
name="newCommenters.premodEnabled"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
type="radio"
|
||||
value="false"
|
||||
/>
|
||||
<label
|
||||
className="RadioButton-label"
|
||||
htmlFor="newCommenters.premodEnabled-false"
|
||||
>
|
||||
<span>
|
||||
Off
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div
|
||||
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-spacing-2"
|
||||
>
|
||||
<label
|
||||
className="Label-root"
|
||||
>
|
||||
Number of first comments sent for approval
|
||||
</label>
|
||||
<div
|
||||
className="TextField-root"
|
||||
>
|
||||
<input
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
className="TextField-input NewCommentersConfig-thresholdTextField TextField-colorRegular TextField-textAlignCenter"
|
||||
disabled={false}
|
||||
name="newCommenters.approvedCommentsThreshold"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder=""
|
||||
spellCheck={false}
|
||||
type="text"
|
||||
value={2}
|
||||
/>
|
||||
<div
|
||||
className="TextField-adornment"
|
||||
>
|
||||
comments
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset
|
||||
className="FieldSet-root Box-root ConfigBox-root"
|
||||
>
|
||||
|
||||
@@ -161,6 +161,10 @@ export const settings = createFixture<GQLSettings>({
|
||||
changeUsername: true,
|
||||
deleteAccount: true,
|
||||
},
|
||||
newCommenters: {
|
||||
premodEnabled: false,
|
||||
approvedCommentsThreshold: 2,
|
||||
},
|
||||
slack: {
|
||||
channels: [],
|
||||
},
|
||||
@@ -532,6 +536,7 @@ export const baseComment = createFixture<GQLComment>({
|
||||
COMMENT_DETECTED_SUSPECT_WORD: 0,
|
||||
COMMENT_REPORTED_OFFENSIVE: 0,
|
||||
COMMENT_REPORTED_SPAM: 0,
|
||||
COMMENT_DETECTED_NEW_COMMENTER: 0,
|
||||
COMMENT_DETECTED_REPEAT_POST: 0,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user