Skip to content

Commit

Permalink
Merge branch 'develop' into feat/CORL-2835-rejected-comments-sync-wit…
Browse files Browse the repository at this point in the history
…h-queue
  • Loading branch information
marcushaddon authored Jul 25, 2023
2 parents a3ea525 + 727beba commit 86bb2ee
Show file tree
Hide file tree
Showing 24 changed files with 1,027 additions and 280 deletions.
159 changes: 89 additions & 70 deletions src/core/client/admin/components/BanModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Localized } from "@fluent/react/compat";
import { FORM_ERROR } from "final-form";
import React, {
FunctionComponent,
useCallback,
Expand Down Expand Up @@ -221,28 +222,39 @@ const BanModal: FunctionComponent<Props> = ({
const onFormSubmit = useCallback(async () => {
switch (updateType) {
case UpdateType.ALL_SITES:
await banUser({
userID, // Should be defined because the modal shouldn't open if author is null
message: customizeMessage ? emailMessage : getDefaultMessage,
rejectExistingComments,
siteIDs: viewerIsScoped
? viewer?.moderationScopes?.sites?.map(({ id }) => id)
: [],
});
try {
await banUser({
userID, // Should be defined because the modal shouldn't open if author is null
message: customizeMessage ? emailMessage : getDefaultMessage,
rejectExistingComments,
siteIDs: viewerIsScoped
? viewer?.moderationScopes?.sites?.map(({ id }) => id)
: [],
});
} catch (err) {
return { [FORM_ERROR]: err.message };
}
break;
case UpdateType.SPECIFIC_SITES:
await updateUserBan({
userID,
message: customizeMessage ? emailMessage : getDefaultMessage,
banSiteIDs,
unbanSiteIDs,
rejectExistingComments,
});
try {
await updateUserBan({
userID,
message: customizeMessage ? emailMessage : getDefaultMessage,
banSiteIDs,
unbanSiteIDs,
});
} catch (err) {
return { [FORM_ERROR]: err.message };
}
break;
case UpdateType.NO_SITES:
await removeUserBan({
userID,
});
try {
await removeUserBan({
userID,
});
} catch (err) {
return { [FORM_ERROR]: err.message };
}
}
if (banDomain) {
void createDomainBan({
Expand Down Expand Up @@ -327,59 +339,66 @@ const BanModal: FunctionComponent<Props> = ({
>
{/* BAN FROM/REJECT COMMENTS */}
<Flex direction="column">
{/* ban from header */}
<Localized id="community-banModal-banFrom">
<Label className={styles.banFromHeader}>Ban from</Label>
</Localized>
<Flex
direction="row"
className={styles.sitesOptions}
justifyContent="flex-start"
spacing={5}
>
{/* sites options */}
{showAllSitesOption && (
<FormField>
<Localized id="community-banModal-allSites">
<RadioButton
checked={updateType === UpdateType.ALL_SITES}
onChange={() =>
setUpdateType(UpdateType.ALL_SITES)
}
disabled={userBanStatus?.active}
>
All sites
</RadioButton>
</Localized>
</FormField>
)}
<FormField>
<Localized id="community-banModal-specificSites">
<RadioButton
checked={updateType === UpdateType.SPECIFIC_SITES}
onChange={() =>
setUpdateType(UpdateType.SPECIFIC_SITES)
}
>
Specific Sites
</RadioButton>
{isMultisite && (
<>
<Localized id="community-banModal-banFrom">
<Label className={styles.banFromHeader}>
Ban from
</Label>
</Localized>
</FormField>
{!viewerIsScoped && userHasAnyBan && (
<FormField>
<Localized id="community-banModal-noSites">
<RadioButton
checked={updateType === UpdateType.NO_SITES}
onChange={() =>
setUpdateType(UpdateType.NO_SITES)
}
>
No Sites
</RadioButton>
</Localized>
</FormField>
)}
</Flex>
<Flex
direction="row"
className={styles.sitesOptions}
justifyContent="flex-start"
spacing={5}
>
{/* sites options */}
{showAllSitesOption && (
<FormField>
<Localized id="community-banModal-allSites">
<RadioButton
checked={updateType === UpdateType.ALL_SITES}
onChange={() =>
setUpdateType(UpdateType.ALL_SITES)
}
disabled={userBanStatus?.active}
>
All sites
</RadioButton>
</Localized>
</FormField>
)}
<FormField>
<Localized id="community-banModal-specificSites">
<RadioButton
checked={
updateType === UpdateType.SPECIFIC_SITES
}
onChange={() =>
setUpdateType(UpdateType.SPECIFIC_SITES)
}
>
Specific Sites
</RadioButton>
</Localized>
</FormField>
{!viewerIsScoped && userHasAnyBan && (
<FormField>
<Localized id="community-banModal-noSites">
<RadioButton
checked={updateType === UpdateType.NO_SITES}
onChange={() =>
setUpdateType(UpdateType.NO_SITES)
}
>
No Sites
</RadioButton>
</Localized>
</FormField>
)}
</Flex>
</>
)}
{/* reject comments option */}
{updateType !== UpdateType.NO_SITES && (
<Localized
Expand Down
29 changes: 29 additions & 0 deletions src/core/client/admin/test/community/banUser.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,35 @@ it("ban user across specific sites", async () => {
expect(resolvers.Mutation!.updateUserBan!.called).toBe(true);
});

it("displays limited options for single site tenants", async () => {
const resolvers = createResolversStub<GQLResolver>({
Query: {
settings: () => settings, // base settings has multisite: false
},
});

const { container } = await createTestRenderer({
resolvers,
});

const userRow = within(container).getByRole("row", {
name: "Isabelle isabelle@test.com 07/06/18, 06:24 PM Commenter Active",
});
userEvent.click(
within(userRow).getByRole("button", { name: "Change user status" })
);

const dropdown = within(userRow).getByLabelText(
"A dropdown to change the user status"
);
fireEvent.click(within(dropdown).getByRole("button", { name: "Manage Ban" }));

const modal = screen.getByLabelText("Are you sure you want to ban Isabelle?");
expect(modal).toBeInTheDocument();
expect(screen.queryByText("All sites")).not.toBeInTheDocument();
expect(screen.queryByText("Specific sites")).not.toBeInTheDocument();
});

it("site moderators can unban users on their sites but not sites out of their scope", async () => {
const user = users.siteBannedCommenter;
const resolvers = createResolversStub<GQLResolver>({
Expand Down
4 changes: 4 additions & 0 deletions src/core/client/stream/local/local.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ extend type Comment {
# Remember last viewer action that could have caused a status change.
lastViewerAction: COMMENT_VIEWER_ACTION

# If the comment was spam banned and rejected. Used for showing spam banned
# confirmation in moderation rejected tombstone container.
spamBanned: Boolean

# If true then Comment came in live.
enteredLive: Boolean

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ export const CommentContainer: FunctionComponent<Props> = ({
<ModerationRejectedTombstoneContainer
comment={comment}
settings={settings}
story={story}
viewer={viewer!}
/>
);
}
Expand Down Expand Up @@ -808,6 +810,7 @@ const enhanced = withShowAuthPopupMutation(
...ReportFlowContainer_viewer
...ReportButton_viewer
...CaretContainer_viewer
...ModerationRejectedTombstoneContainer_viewer
}
`,
story: graphql`
Expand All @@ -829,6 +832,7 @@ const enhanced = withShowAuthPopupMutation(
...PermalinkButtonContainer_story
...ReplyCommentFormContainer_story
...UserTagsContainer_story
...ModerationRejectedTombstoneContainer_story
}
`,
comment: graphql`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Localized } from "@fluent/react/compat";
import cn from "classnames";
import React, { FunctionComponent } from "react";
import React, { FunctionComponent, useCallback } from "react";
import { graphql } from "react-relay";

import { withFragmentContainer } from "coral-framework/lib/relay";
import { useMutation, withFragmentContainer } from "coral-framework/lib/relay";
import CLASSES from "coral-stream/classes";
import {
ArrowsDownIcon,
Expand All @@ -17,7 +17,10 @@ import { CaretContainer_settings } from "coral-stream/__generated__/CaretContain
import { CaretContainer_story } from "coral-stream/__generated__/CaretContainer_story.graphql";
import { CaretContainer_viewer } from "coral-stream/__generated__/CaretContainer_viewer.graphql";

import ModerationDropdownContainer from "./ModerationDropdownContainer";
import { SetSpamBanned } from "../setSpamBanned";
import ModerationDropdownContainer, {
ModerationDropdownView,
} from "./ModerationDropdownContainer";

import styles from "./CaretContainer.css";

Expand All @@ -26,28 +29,44 @@ interface Props {
story: CaretContainer_story;
viewer: CaretContainer_viewer;
settings: CaretContainer_settings;
view?: ModerationDropdownView;
open?: boolean;
}

const CaretContainer: FunctionComponent<Props> = (props) => {
const popoverID = `comments-moderationMenu-${props.comment.id}`;
const setSpamBanned = useMutation(SetSpamBanned);
const setSpamBannedOnClickOutside = useCallback(() => {
if (props.comment.spamBanned) {
void setSpamBanned({ commentID: props.comment.id, spamBanned: false });
}
}, [props.comment.spamBanned, setSpamBanned, props.comment.id]);

return (
<Localized
id="comments-moderationDropdown-popover"
attrs={{ description: true }}
>
<Popover
id={popoverID}
visible={props.open}
placement="bottom-end"
description="A popover menu to moderate the comment"
body={({ toggleVisibility, scheduleUpdate }) => (
<ClickOutside onClickOutside={toggleVisibility}>
<ClickOutside
onClickOutside={() => {
setSpamBannedOnClickOutside();
toggleVisibility();
}}
>
<ModerationDropdownContainer
comment={props.comment}
story={props.story}
viewer={props.viewer}
settings={props.settings}
onDismiss={toggleVisibility}
scheduleUpdate={scheduleUpdate}
view={props.view}
/>
</ClickOutside>
)}
Expand Down Expand Up @@ -81,6 +100,7 @@ const enhanced = withFragmentContainer<Props>({
comment: graphql`
fragment CaretContainer_comment on Comment {
id
spamBanned
...ModerationDropdownContainer_comment
}
`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ const ModerationActionBanButton: FunctionComponent<Props> = ({
showSpinner = false,
}) => {
const localizationId = allSiteBan
? "comments-moderationDropdown-ban"
? "comments-moderationDropdown-spam-ban"
: "comments-moderationDropdown-siteBan";
const defaultText = allSiteBan ? "Ban User" : "Site Ban";
const defaultText = allSiteBan ? "Spam ban" : "Site ban";
const icon = allSiteBan
? SingleNeutralActionsBlockIcon
: AppWindowDisableIcon;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,25 @@ const ModerationActionBanContainer: FunctionComponent<Props> = ({
<DropdownDivider />
{settings?.multisite ? (
<>
<ModerationActionBanButton
disabled={!user || !!userIsSiteBanned || userIsAllSiteBanned}
allSiteBan={false}
onClick={!user ? undefined : onSiteBan}
showSpinner={!user}
/>
{!viewerScoped && (
{viewerScoped && (
<ModerationActionBanButton
disabled={!user || userIsAllSiteBanned}
allSiteBan={true}
onClick={!user ? undefined : onBan}
disabled={!user || !!userIsSiteBanned || userIsAllSiteBanned}
allSiteBan={false}
onClick={!user ? undefined : onSiteBan}
showSpinner={!user}
/>
)}
<ModerationActionBanButton
disabled={!user}
allSiteBan={true}
onClick={!user ? undefined : onBan}
showSpinner={!user}
/>
</>
) : (
<>
<ModerationActionBanButton
disabled={!user || userIsAllSiteBanned}
disabled={!user}
allSiteBan={true}
onClick={!user ? undefined : onBan}
showSpinner={!user}
Expand Down
Loading

0 comments on commit 86bb2ee

Please sign in to comment.