Skip to content

Commit

Permalink
Merge pull request #4356 from coralproject/feat/dsa-launch-pad
Browse files Browse the repository at this point in the history
[EPIC BRANCH] DSA basis branch for separated work
  • Loading branch information
nick-funk authored Nov 22, 2023
2 parents b85002d + 575a5f1 commit 855d69f
Show file tree
Hide file tree
Showing 227 changed files with 11,836 additions and 560 deletions.
14 changes: 14 additions & 0 deletions INDEXES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ The goal of this document is to date-mark the indexes you add to support the cha

If you are releasing, you can use this readme to check all the indexes prior to the release you are deploying and have a good idea of what indexes you might need to deploy to Mongo along with your release of a new Coral Docker image to kubernetes.

## 2023-10-18

```
db.notifications.createIndex({ tenantID: 1, id: 1 }, { unique: true });
```

- This index creates the uniqueness constraint for the `tenantID` and `id` fields on the notifications collection

```
db.notifications.createIndex({ tenantID: 1, ownerID: 1, createdAt: 1 });
```

- This index speeds up the retrieval of notifications by `tenantID`, `ownerID`, and `createdAt` which is the most common way of retrieving notifications for pagination in the notifications tab on the stream.

## 2023-03-28

```
Expand Down
4 changes: 2 additions & 2 deletions client/scripts/precommitLint.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ sgf((err, results) => {

const eslintFiles = [];
for (const item of results) {
const { filename } = item;
const { filename, status } = item;

// only include valid, filtered extensions
// this is primarily to keep eslint rampaging
// over non-source files
if (!matchesExtension(extensions, filename)) {
if (!matchesExtension(extensions, filename) || status === "Deleted") {
continue;
}

Expand Down
5 changes: 3 additions & 2 deletions client/src/core/client/admin/App/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ import styles from "./Main.css";
interface Props {
viewer: PropTypesOf<typeof UserMenuContainer>["viewer"] &
PropTypesOf<typeof NavigationContainer>["viewer"];
settings: PropTypesOf<typeof NavigationContainer>["settings"];
children: React.ReactNode;
}

const Main: FunctionComponent<Props> = ({ children, viewer }) => (
const Main: FunctionComponent<Props> = ({ children, viewer, settings }) => (
<div className={styles.root}>
<AppBar gutterBegin gutterEnd>
<Begin itemGutter="double">
<div className={styles.logoContainer}>
<LogoHorizontal />
</div>
<NavigationContainer viewer={viewer} />
<NavigationContainer viewer={viewer} settings={settings} />
</Begin>
<End>
<DecisionHistoryButton />
Expand Down
10 changes: 9 additions & 1 deletion client/src/core/client/admin/App/MainRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ const MainRoute: React.FunctionComponent<Props> = (props) => {
return (
<>
{ErrorReporterSetUser}
<Main viewer={props.data && props.data.viewer}>{props.children}</Main>
<Main
viewer={props.data && props.data.viewer}
settings={props.data && props.data.settings}
>
{props.children}
</Main>
</>
);
};
Expand All @@ -33,6 +38,9 @@ const enhanced = withRouteConfig<Props>({
...UserMenuContainer_viewer
...NavigationContainer_viewer
}
settings {
...NavigationContainer_settings
}
}
`,
})(MainRoute);
Expand Down
6 changes: 6 additions & 0 deletions client/src/core/client/admin/App/Navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import NavigationLink from "./NavigationLink";
interface Props {
showConfigure: boolean;
showDashboard: boolean;
showReports: boolean;
}

const Navigation: FunctionComponent<Props> = (props) => (
Expand All @@ -31,6 +32,11 @@ const Navigation: FunctionComponent<Props> = (props) => (
<NavigationLink to="/admin/dashboard">Dashboard</NavigationLink>
</Localized>
)}
{props.showReports && (
<Localized id="navigation-reports">
<NavigationLink to="/admin/reports">DSA Reports</NavigationLink>
</Localized>
)}
</AppBarNavigation>
);

Expand Down
64 changes: 47 additions & 17 deletions client/src/core/client/admin/App/Navigation/NavigationContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,73 @@
import React from "react";
import React, { FunctionComponent } from "react";
import { graphql } from "react-relay";

import { Ability, can } from "coral-admin/permissions/tenant";
import { withFragmentContainer } from "coral-framework/lib/relay";
import { GQLUSER_ROLE } from "coral-common/client/src/core/client/framework/schema/__generated__/types";
import { useLocal, withFragmentContainer } from "coral-framework/lib/relay";
import {
SignOutMutation,
withSignOutMutation,
} from "coral-framework/mutations";

import { NavigationContainer_settings as SettingsData } from "coral-admin/__generated__/NavigationContainer_settings.graphql";
import { NavigationContainer_viewer as ViewerData } from "coral-admin/__generated__/NavigationContainer_viewer.graphql";
import { NavigationContainerLocal } from "coral-admin/__generated__/NavigationContainerLocal.graphql";

import Navigation from "./Navigation";

interface Props {
signOut: SignOutMutation;
viewer: ViewerData | null;
settings: SettingsData | null;
}

class NavigationContainer extends React.Component<Props> {
public render() {
return (
<Navigation
showDashboard={
!!this.props.viewer && can(this.props.viewer, Ability.VIEW_STATISTICS)
}
showConfigure={
!!this.props.viewer &&
can(this.props.viewer, Ability.CHANGE_CONFIGURATION)
}
/>
);
}
}
const NavigationContainer: FunctionComponent<Props> = ({
viewer,
settings,
}) => {
const [{ dsaFeaturesEnabled }] = useLocal<NavigationContainerLocal>(
graphql`
fragment NavigationContainerLocal on Local {
dsaFeaturesEnabled
}
`
);
return (
<Navigation
showDashboard={!!viewer && can(viewer, Ability.VIEW_STATISTICS)}
showConfigure={!!viewer && can(viewer, Ability.CHANGE_CONFIGURATION)}
showReports={
!!dsaFeaturesEnabled &&
!!viewer &&
can(viewer, Ability.MODERATE_DSA_REPORTS) &&
// Exclude single-site moderators
!(
settings?.multisite &&
viewer.role === GQLUSER_ROLE.MODERATOR &&
viewer.moderationScopes &&
viewer.moderationScopes.sites &&
viewer.moderationScopes.sites.length === 1
)
}
/>
);
};

const enhanced = withSignOutMutation(
withFragmentContainer<Props>({
viewer: graphql`
fragment NavigationContainer_viewer on User {
role
moderationScopes {
sites {
id
}
}
}
`,
settings: graphql`
fragment NavigationContainer_settings on Settings {
multisite
}
`,
})(NavigationContainer)
Expand Down
12 changes: 9 additions & 3 deletions client/src/core/client/admin/components/Comment/InReplyTo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Localized } from "@fluent/react/compat";
import cn from "classnames";
import React, { FunctionComponent } from "react";

import { EmailActionReplyIcon, SvgIcon } from "coral-ui/components/icons";
Expand All @@ -7,19 +8,24 @@ import { BaseButton, Flex } from "coral-ui/components/v2";
import styles from "./InReplyTo.css";

interface Props {
className?: string;
children: string;
onUsernameClick: () => void;
}

const InReplyTo: FunctionComponent<Props> = ({ children, onUsernameClick }) => {
const InReplyTo: FunctionComponent<Props> = ({
className,
children,
onUsernameClick,
}) => {
const Username = () => (
<Localized
id="common-username"
attrs={{ "aria-label": true }}
vars={{ username: children }}
>
<BaseButton onClick={onUsernameClick} className={styles.usernameButton}>
<span className={styles.username}>{children}</span>
<span className={cn(styles.username, className)}>{children}</span>
</BaseButton>
</Localized>
);
Expand All @@ -29,7 +35,7 @@ const InReplyTo: FunctionComponent<Props> = ({ children, onUsernameClick }) => {
<SvgIcon
className={styles.icon}
size="xs"
filled
filled="currentColor"
Icon={EmailActionReplyIcon}
/>{" "}
<Localized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ import {
import { RemoveIcon, SvgIcon } from "coral-ui/components/icons";
import {
Button,
ClickOutside,
Dropdown,
Flex,
HorizontalGutter,
Popover,
Timestamp,
} from "coral-ui/components/v2";

Expand All @@ -32,6 +35,8 @@ import { CommentContent, InReplyTo, UsernameButton } from "../Comment";
import { Circle, Line } from "../Timeline";
import ConversationModalRepliesQuery from "./ConversationModalRepliesQuery";

import { RejectCommentReasonInput } from "coral-stream/__generated__/RejectCommentMutation.graphql";
import ModerationReason from "../ModerationReason/ModerationReason";
import styles from "./ConversationModalCommentContainer.css";

interface Props {
Expand All @@ -52,10 +57,11 @@ const ConversationModalCommentContainer: FunctionComponent<Props> = ({
const rejectComment = useMutation(RejectCommentMutation);
const { match } = useRouter();
const { storyID, siteID, section } = parseModerationOptions(match);
const [{ moderationQueueSort }] =
const [{ moderationQueueSort, dsaFeaturesEnabled }] =
useLocal<ConversationModalCommentContainerLocal>(graphql`
fragment ConversationModalCommentContainerLocal on Local {
moderationQueueSort
dsaFeaturesEnabled
}
`);
const commentAuthorClick = useCallback(() => {
Expand All @@ -72,28 +78,32 @@ const ConversationModalCommentContainer: FunctionComponent<Props> = ({
const onShowReplies = useCallback(() => {
setShowReplies(true);
}, []);
const onRejectComment = useCallback(async () => {
if (!comment.revision) {
return;
}
await rejectComment({
commentID: comment.id,
commentRevisionID: comment.revision.id,
const onRejectComment = useCallback(
async (reason?: RejectCommentReasonInput) => {
if (!comment.revision) {
return;
}
await rejectComment({
commentID: comment.id,
commentRevisionID: comment.revision.id,
storyID,
siteID,
section,
orderBy: moderationQueueSort,
reason,
});
},
[

Check warning on line 96 in client/src/core/client/admin/components/ConversationModal/ConversationModalCommentContainer.tsx

View workflow job for this annotation

GitHub Actions / Build and Test

React Hook useCallback has an unnecessary dependency: 'match'. Either exclude it or remove the dependency array

Check warning on line 96 in client/src/core/client/admin/components/ConversationModal/ConversationModalCommentContainer.tsx

View workflow job for this annotation

GitHub Actions / Build and Test

React Hook useCallback has an unnecessary dependency: 'match'. Either exclude it or remove the dependency array
comment.id,
comment.revision,
match,
moderationQueueSort,
rejectComment,
storyID,
siteID,
section,
orderBy: moderationQueueSort,
});
}, [
comment.id,
comment.revision,
match,
moderationQueueSort,
rejectComment,
storyID,
siteID,
section,
]);
]
);
const rejectButtonOptions = useMemo((): {
localization: string;
variant: "regular" | "outlined";
Expand Down Expand Up @@ -170,24 +180,54 @@ const ConversationModalCommentContainer: FunctionComponent<Props> = ({
</Flex>
</Flex>
<Flex>
<Localized
id={rejectButtonOptions.localization}
attrs={{ "aria-label": true }}
elems={{ icon: <SvgIcon size="xs" Icon={RemoveIcon} /> }}
<Popover
id={`reject-reason-${comment.id}`}
modifiers={{
arrow: { enabled: false },
offset: { offset: "0, 4" },
}}
placement="bottom-start"
body={({ toggleVisibility, visible }) => (
<ClickOutside onClickOutside={toggleVisibility}>
<Dropdown>
<ModerationReason
id={comment.id}
onCancel={toggleVisibility}
onReason={(reason) => {
void onRejectComment(reason);
toggleVisibility();
}}
/>
</Dropdown>
</ClickOutside>
)}
>
<Button
className={styles.rejectButton}
color="alert"
variant={rejectButtonOptions.variant}
iconLeft
disabled={rejectButtonOptions.disabled}
onClick={onRejectComment}
aria-label={rejectButtonOptions.ariaLabel}
>
<SvgIcon size="xs" Icon={RemoveIcon} />
{rejectButtonOptions.text}
</Button>
</Localized>
{({ toggleVisibility, ref }) => (
<Localized
id={rejectButtonOptions.localization}
attrs={{ "aria-label": true }}
elems={{ icon: <SvgIcon size="xs" Icon={RemoveIcon} /> }}
>
<Button
ref={ref}
className={styles.rejectButton}
color="alert"
variant={rejectButtonOptions.variant}
iconLeft
disabled={rejectButtonOptions.disabled}
onClick={
dsaFeaturesEnabled
? toggleVisibility
: () => onRejectComment()
}
aria-label={rejectButtonOptions.ariaLabel}
>
<SvgIcon size="xs" Icon={RemoveIcon} />
{rejectButtonOptions.text}
</Button>
</Localized>
)}
</Popover>
</Flex>
</Flex>
</HorizontalGutter>
Expand Down
Loading

0 comments on commit 855d69f

Please sign in to comment.