mirror of
https://github.com/wassname/talk.git
synced 2026-07-22 13:00:29 +08:00
[CORL-233] Create a user drawer with comment streams (#2395)
* Create preliminary user history drawer with user fragment CORL-233 * Set font size for the user details to match spec at 16 px CORL-233 * Update username font style/size, add horizontal rule for user history drawer CORL-233 * Set user detail min width to align the copy buttons in user history drawer CORL-233 * Set min width of the user history drawer to match spec CORL-233 * Remove min width for detail area and card to allow responsive sizing Allows the copy buttons to sit just to the right of the user details text. CORL-233 * Update snapshots to handle clickable username in moderation comment cards CORL-233 * Create initial pagination of all comments in user history drawer Shows all comments with a rubbish "load more" button as we haven't decided if we want to do a load more button or infinite scrolling solution. Flex layout helps it create a scroll bar when needed to traverse through the comment stream. CORL-233 * Create preliminary user history drawer with user fragment CORL-233 * Set font size for the user details to match spec at 16 px CORL-233 * Update username font style/size, add horizontal rule for user history drawer CORL-233 * Set user detail min width to align the copy buttons in user history drawer CORL-233 * Set min width of the user history drawer to match spec CORL-233 * Remove min width for detail area and card to allow responsive sizing Allows the copy buttons to sit just to the right of the user details text. CORL-233 * Update snapshots to handle clickable username in moderation comment cards CORL-233 * Let the user history drawer fill full height of page CORL-233 * Add a close button to the user history drawer CORL-233 * Preliminarily get all and rejected comments showing in the user drawer CORL-233 * Create preliminary user history drawer with user fragment CORL-233 * Set font size for the user details to match spec at 16 px CORL-233 * Update username font style/size, add horizontal rule for user history drawer CORL-233 * Set user detail min width to align the copy buttons in user history drawer CORL-233 * Set min width of the user history drawer to match spec CORL-233 * Remove min width for detail area and card to allow responsive sizing Allows the copy buttons to sit just to the right of the user details text. CORL-233 * Update snapshots to handle clickable username in moderation comment cards CORL-233 * Let the user history drawer fill full height of page CORL-233 * Add a close button to the user history drawer CORL-233 * Set the user history drawer to fill full height of window CORL-233 * Convert ModerateCardContainer to a FunctionComponent CORL-233 * Show spinner while all comments are loading in user history drawer CORL-233 * Style tabs within the user history drawer to be secondary CORL-233 * Center load more button in the comments for the user history drawer CORL-233 * Fix scroll bars on nested flexboxes in the user history drawer CORL-233 * Tweak styling for the user history drawer comments section Add margins where necessary. Style the Load More button to match the comment feed elsewhere. CORL-233 * Fix background of copy buttons to match the user history drawer CORL-233 * Preliminarily get rejected comments showing in the user history tabs Still need to consolidate any duplication between the all and rejected comments containers/queries, but this works correctly as is. CORL-233 * Show hover and active background color on moderate card usernames CORL-233 * Hide load more buttons on comment streams when no comments available CORL-233 * Handle when no comments are available in user history drawer streams CORL-233 * Actually show all comments in the user history all comments tab Previously was filtering out rejected and other pre-mod status-ed comments. CORL-233 * Style the user history drawer cards to match the spec CORL-233 * Add localizations around user history drawer tabs CORL-233 * Allow a mini mode for moderate cards that are shown in the user drawer CORL-233 * Style user drawer comment tabs to match the spec CORL-233 * Update snapshots to account for mini moderate cards Updates the fact that a separator on regular sized cards now has an additional class name to turn on the divider's visible border. CORL-233 * Add user drawer to the queue instead of childed to the moderate card Prevents the user drawer from prematurely closing when we approve all items in the moderate queue. CORL-233 * Update snapshots to account for user drawer on queues CORL-233 * Set mini and username defaults more cleanly on ModerateCardContainer CORL-233 * Rename usernameClicked to onUsernameClicked CORL-233 * Use callback on loadMore calls within user drawer queries CORL-233 * Localize the no comment text for the user drawer comment streams CORL-233 * Localize the user not found callout in the user drawer comment streams CORL-233 * Remove superfluous curly braces CORL-233 * Extract user history drawer internals into a distinct query component CORL-233 * Move comments conditional below useCallback initializations CORL-233 * feat: fixed consistency issues * Use a concrete tab type on user drawer comment stream tabs CORL-233 * Use <hr/> instead of <HorizontalRule> CORL-233 * Clarify logic for showing horizontal rule after comments in user drawer CORL-233 * Remover bottom border on user drawer tab bar CORL-233
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
import cn from "classnames";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import styles from "./HorizontalRule.css";
|
||||
|
||||
const HorizontalRule: FunctionComponent = ({ children }) => (
|
||||
<hr className={styles.root} />
|
||||
interface Props {
|
||||
className?: string;
|
||||
children?: any;
|
||||
}
|
||||
|
||||
const HorizontalRule: FunctionComponent<Props> = ({ children, className }) => (
|
||||
<hr className={cn(styles.root, className)} />
|
||||
);
|
||||
|
||||
export default HorizontalRule;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
.root {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 2px;
|
||||
|
||||
border: 1px solid var(--palette-grey-lighter);
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--round-corners);
|
||||
float: right;
|
||||
padding: 2px var(--spacing-2) 2px var(--spacing-2);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
.topBar {
|
||||
position: relative;
|
||||
margin-bottom: var(--mini-unit);
|
||||
}
|
||||
|
||||
.topBarMini {
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
|
||||
.username {
|
||||
margin-right: var(--mini-unit);
|
||||
padding: var(--spacing-1);
|
||||
margin-left: calc(-1 * var(--spacing-1));
|
||||
line-height: calc(16rem / var(--rem-base));
|
||||
|
||||
&:hover {
|
||||
background-color: var(--palette-grey-lightest);
|
||||
border-radius: 2px;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--palette-grey-lighter);
|
||||
border-radius: 2px;
|
||||
border-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -19,16 +39,32 @@
|
||||
}
|
||||
|
||||
.aside {
|
||||
padding-top: 25px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
box-sizing: border-box;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.asideWithoutReplyTo {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.asideMini {
|
||||
/*
|
||||
No reply to field, so we align below the fattest element
|
||||
which is the feature button.
|
||||
*/
|
||||
padding-top: calc((14rem / var(--rem-base)) + 7px + var(--spacing-4));
|
||||
}
|
||||
|
||||
.asideMiniWithReplyTo {
|
||||
/*
|
||||
We have reply to field, so we align below the fattest
|
||||
element plus the reply to field.
|
||||
*/
|
||||
padding-top: calc(2 * (14rem / var(--rem-base)) + 5px + var(--spacing-4));
|
||||
}
|
||||
|
||||
.decision {
|
||||
font-size: calc(14rem / var(--rem-base));
|
||||
font-weight: var(--font-weight-medium);
|
||||
@@ -42,10 +78,13 @@
|
||||
.separator {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
border-right: 1px solid var(--palette-divider);
|
||||
margin: 0 calc(2 * var(--mini-unit));
|
||||
}
|
||||
|
||||
.ruledSeparator {
|
||||
border-right: 1px solid var(--palette-divider);
|
||||
}
|
||||
|
||||
.root {
|
||||
transition: background 100ms, box-shadow 100ms;
|
||||
}
|
||||
@@ -103,3 +142,13 @@
|
||||
text-align: center;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.borderless {
|
||||
border-width: 0px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.miniButton {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ const baseProps: PropTypesOf<typeof ModerateCardN> = {
|
||||
onApprove: noop,
|
||||
onReject: noop,
|
||||
onFeature: noop,
|
||||
onUsernameClick: noop,
|
||||
showStory: false,
|
||||
moderatedBy: null,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,13 @@ import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { PropTypesOf } from "coral-framework/types";
|
||||
import { Card, Flex, HorizontalGutter, TextLink } from "coral-ui/components";
|
||||
import {
|
||||
BaseButton,
|
||||
Card,
|
||||
Flex,
|
||||
HorizontalGutter,
|
||||
TextLink,
|
||||
} from "coral-ui/components";
|
||||
|
||||
import ApproveButton from "./ApproveButton";
|
||||
import CommentContent from "./CommentContent";
|
||||
@@ -36,6 +42,9 @@ interface Props {
|
||||
onApprove: () => void;
|
||||
onReject: () => void;
|
||||
onFeature: () => void;
|
||||
onUsernameClick: () => void;
|
||||
mini?: boolean;
|
||||
hideUsername?: boolean;
|
||||
/**
|
||||
* If set to true, it means this comment is about to be removed
|
||||
* from the queue. This will trigger some styling changes to
|
||||
@@ -59,25 +68,40 @@ const ModerateCard: FunctionComponent<Props> = ({
|
||||
onApprove,
|
||||
onReject,
|
||||
onFeature,
|
||||
onUsernameClick,
|
||||
dangling,
|
||||
showStory,
|
||||
storyTitle,
|
||||
storyHref,
|
||||
onModerateStory,
|
||||
moderatedBy,
|
||||
mini = false,
|
||||
hideUsername = false,
|
||||
}) => (
|
||||
<Card
|
||||
className={cn(styles.root, { [styles.dangling]: dangling })}
|
||||
className={cn(
|
||||
styles.root,
|
||||
{ [styles.borderless]: mini },
|
||||
{ [styles.dangling]: dangling }
|
||||
)}
|
||||
data-testid={`moderate-comment-${id}`}
|
||||
>
|
||||
<Flex>
|
||||
<div className={styles.mainContainer}>
|
||||
<div className={styles.topBar}>
|
||||
<div>
|
||||
<Username className={styles.username}>{username}</Username>
|
||||
<div
|
||||
className={cn(styles.topBar, {
|
||||
[styles.topBarMini]: mini && !inReplyTo,
|
||||
})}
|
||||
>
|
||||
<Flex alignItems="center">
|
||||
{!hideUsername && (
|
||||
<BaseButton onClick={onUsernameClick} className={styles.username}>
|
||||
<Username>{username}</Username>
|
||||
</BaseButton>
|
||||
)}
|
||||
<Timestamp>{createdAt}</Timestamp>
|
||||
<FeatureButton featured={featured} onClick={onFeature} />
|
||||
</div>
|
||||
</Flex>
|
||||
{inReplyTo && (
|
||||
<div>
|
||||
<InReplyTo>{inReplyTo}</InReplyTo>
|
||||
@@ -126,28 +150,36 @@ const ModerateCard: FunctionComponent<Props> = ({
|
||||
</HorizontalGutter>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.separator} />
|
||||
<div
|
||||
className={cn(styles.separator, { [styles.ruledSeparator]: !mini })}
|
||||
/>
|
||||
<Flex
|
||||
className={cn(styles.aside, {
|
||||
[styles.asideWithoutReplyTo]: !inReplyTo,
|
||||
[styles.asideMini]: mini && !inReplyTo,
|
||||
[styles.asideMiniWithReplyTo]: mini && inReplyTo,
|
||||
})}
|
||||
alignItems="center"
|
||||
direction="column"
|
||||
itemGutter
|
||||
>
|
||||
<Localized id="moderate-comment-decision">
|
||||
<div className={styles.decision}>DECISION</div>
|
||||
</Localized>
|
||||
{!mini && (
|
||||
<Localized id="moderate-comment-decision">
|
||||
<div className={styles.decision}>DECISION</div>
|
||||
</Localized>
|
||||
)}
|
||||
<Flex itemGutter>
|
||||
<RejectButton
|
||||
onClick={onReject}
|
||||
invert={status === "rejected"}
|
||||
disabled={status === "rejected" || dangling}
|
||||
className={cn({ [styles.miniButton]: mini })}
|
||||
/>
|
||||
<ApproveButton
|
||||
onClick={onApprove}
|
||||
invert={status === "approved"}
|
||||
disabled={status === "approved" || dangling}
|
||||
className={cn({ [styles.miniButton]: mini })}
|
||||
/>
|
||||
</Flex>
|
||||
{moderatedBy}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Match, Router, withRouter } from "found";
|
||||
import React from "react";
|
||||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import {
|
||||
@@ -37,6 +37,9 @@ interface Props {
|
||||
match: Match;
|
||||
router: Router;
|
||||
showStoryInfo: boolean;
|
||||
mini?: boolean;
|
||||
hideUsername?: boolean;
|
||||
onUsernameClicked?: (userID: string) => void;
|
||||
}
|
||||
|
||||
function getStatus(comment: ModerateCardContainer_comment) {
|
||||
@@ -54,67 +57,82 @@ function isFeatured(comment: ModerateCardContainer_comment) {
|
||||
return comment.tags.some(t => t.code === GQLTAG.FEATURED);
|
||||
}
|
||||
|
||||
class ModerateCardContainer extends React.Component<Props> {
|
||||
private handleApprove = () => {
|
||||
this.props.approveComment({
|
||||
commentID: this.props.comment.id,
|
||||
commentRevisionID: this.props.comment.revision.id,
|
||||
storyID: this.props.match.params.storyID,
|
||||
const ModerateCardContainer: FunctionComponent<Props> = ({
|
||||
comment,
|
||||
settings,
|
||||
danglingLogic,
|
||||
showStoryInfo,
|
||||
viewer,
|
||||
match,
|
||||
router,
|
||||
approveComment,
|
||||
rejectComment,
|
||||
featureComment,
|
||||
unfeatureComment,
|
||||
mini,
|
||||
hideUsername,
|
||||
onUsernameClicked: usernameClicked,
|
||||
}) => {
|
||||
const handleApprove = useCallback(() => {
|
||||
approveComment({
|
||||
commentID: comment.id,
|
||||
commentRevisionID: comment.revision.id,
|
||||
storyID: match.params.storyID,
|
||||
});
|
||||
};
|
||||
}, [approveComment, comment, match]);
|
||||
|
||||
private handleReject = () => {
|
||||
this.props.rejectComment({
|
||||
commentID: this.props.comment.id,
|
||||
commentRevisionID: this.props.comment.revision.id,
|
||||
storyID: this.props.match.params.storyID,
|
||||
const handleReject = useCallback(() => {
|
||||
rejectComment({
|
||||
commentID: comment.id,
|
||||
commentRevisionID: comment.revision.id,
|
||||
storyID: match.params.storyID,
|
||||
});
|
||||
};
|
||||
}, [rejectComment, comment, match]);
|
||||
|
||||
private onFeature = () => {
|
||||
const featured = isFeatured(this.props.comment);
|
||||
const handleFeature = useCallback(() => {
|
||||
featureComment({
|
||||
commentID: comment.id,
|
||||
commentRevisionID: comment.revision.id,
|
||||
storyID: match.params.storyID,
|
||||
});
|
||||
}, [featureComment, comment, match]);
|
||||
|
||||
const handleUnfeature = useCallback(() => {
|
||||
unfeatureComment({
|
||||
commentID: comment.id,
|
||||
storyID: match.params.storyID,
|
||||
});
|
||||
}, [unfeatureComment, comment, match]);
|
||||
|
||||
const onFeature = useCallback(() => {
|
||||
const featured = isFeatured(comment);
|
||||
|
||||
if (featured) {
|
||||
this.handleUnfeature();
|
||||
handleUnfeature();
|
||||
} else {
|
||||
this.handleFeature();
|
||||
handleFeature();
|
||||
}
|
||||
};
|
||||
}, [comment]);
|
||||
|
||||
private handleFeature = () => {
|
||||
this.props.featureComment({
|
||||
commentID: this.props.comment.id,
|
||||
commentRevisionID: this.props.comment.revision.id,
|
||||
storyID: this.props.match.params.storyID,
|
||||
});
|
||||
};
|
||||
|
||||
private handleUnfeature = () => {
|
||||
this.props.unfeatureComment({
|
||||
commentID: this.props.comment.id,
|
||||
storyID: this.props.match.params.storyID,
|
||||
});
|
||||
};
|
||||
|
||||
private handleModerateStory = (e: React.MouseEvent) => {
|
||||
this.props.router.push(
|
||||
getModerationLink("default", this.props.comment.story.id)
|
||||
);
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
const onUsernameClicked = useCallback(() => {
|
||||
if (!usernameClicked) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
usernameClicked(comment.author!.id);
|
||||
}, [usernameClicked, comment]);
|
||||
|
||||
public render() {
|
||||
const {
|
||||
comment,
|
||||
settings,
|
||||
danglingLogic,
|
||||
showStoryInfo,
|
||||
viewer,
|
||||
} = this.props;
|
||||
const dangling = danglingLogic(comment.status);
|
||||
return (
|
||||
const handleModerateStory = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
router.push(getModerationLink("default", comment.story.id));
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
[router, comment]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FadeInTransition active={Boolean(comment.enteredLive)}>
|
||||
<ModerateCard
|
||||
id={comment.id}
|
||||
@@ -123,15 +141,16 @@ class ModerateCardContainer extends React.Component<Props> {
|
||||
body={comment.body!}
|
||||
inReplyTo={comment.parent && comment.parent.author!.username!}
|
||||
comment={comment}
|
||||
dangling={dangling}
|
||||
dangling={danglingLogic(comment.status)}
|
||||
status={getStatus(comment)}
|
||||
featured={isFeatured(comment)}
|
||||
viewContextHref={comment.permalink}
|
||||
suspectWords={settings.wordList.suspect}
|
||||
bannedWords={settings.wordList.banned}
|
||||
onApprove={this.handleApprove}
|
||||
onReject={this.handleReject}
|
||||
onFeature={this.onFeature}
|
||||
onApprove={handleApprove}
|
||||
onReject={handleReject}
|
||||
onFeature={onFeature}
|
||||
onUsernameClick={onUsernameClicked}
|
||||
moderatedBy={
|
||||
<ModeratedByContainer viewer={viewer} comment={comment} />
|
||||
}
|
||||
@@ -142,18 +161,21 @@ class ModerateCardContainer extends React.Component<Props> {
|
||||
)
|
||||
}
|
||||
storyHref={getModerationLink("default", comment.story.id)}
|
||||
onModerateStory={this.handleModerateStory}
|
||||
onModerateStory={handleModerateStory}
|
||||
mini={mini}
|
||||
hideUsername={hideUsername}
|
||||
/>
|
||||
</FadeInTransition>
|
||||
);
|
||||
}
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
comment: graphql`
|
||||
fragment ModerateCardContainer_comment on Comment {
|
||||
id
|
||||
author {
|
||||
id
|
||||
username
|
||||
}
|
||||
statusLiveUpdated
|
||||
|
||||
+78
-36
@@ -12,12 +12,17 @@ exports[`renders approved correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<Username
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-username"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Theon
|
||||
</Username>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
<Timestamp>
|
||||
2018-11-29T16:01:51.897Z
|
||||
</Timestamp>
|
||||
@@ -25,7 +30,7 @@ exports[`renders approved correctly 1`] = `
|
||||
featured={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<CommentContent
|
||||
bannedWords={
|
||||
@@ -66,7 +71,7 @@ exports[`renders approved correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
@@ -87,10 +92,12 @@ exports[`renders approved correctly 1`] = `
|
||||
itemGutter={true}
|
||||
>
|
||||
<RejectButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<ApproveButton
|
||||
className=""
|
||||
disabled={true}
|
||||
invert={true}
|
||||
onClick={[Function]}
|
||||
@@ -113,12 +120,17 @@ exports[`renders correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<Username
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-username"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Theon
|
||||
</Username>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
<Timestamp>
|
||||
2018-11-29T16:01:51.897Z
|
||||
</Timestamp>
|
||||
@@ -126,7 +138,7 @@ exports[`renders correctly 1`] = `
|
||||
featured={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<CommentContent
|
||||
bannedWords={
|
||||
@@ -167,7 +179,7 @@ exports[`renders correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
@@ -188,10 +200,12 @@ exports[`renders correctly 1`] = `
|
||||
itemGutter={true}
|
||||
>
|
||||
<RejectButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<ApproveButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
@@ -213,12 +227,17 @@ exports[`renders dangling correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<Username
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-username"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Theon
|
||||
</Username>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
<Timestamp>
|
||||
2018-11-29T16:01:51.897Z
|
||||
</Timestamp>
|
||||
@@ -226,7 +245,7 @@ exports[`renders dangling correctly 1`] = `
|
||||
featured={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<CommentContent
|
||||
bannedWords={
|
||||
@@ -267,7 +286,7 @@ exports[`renders dangling correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
@@ -288,11 +307,13 @@ exports[`renders dangling correctly 1`] = `
|
||||
itemGutter={true}
|
||||
>
|
||||
<RejectButton
|
||||
className=""
|
||||
disabled={true}
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<ApproveButton
|
||||
className=""
|
||||
disabled={true}
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
@@ -315,12 +336,17 @@ exports[`renders rejected correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<Username
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-username"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Theon
|
||||
</Username>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
<Timestamp>
|
||||
2018-11-29T16:01:51.897Z
|
||||
</Timestamp>
|
||||
@@ -328,7 +354,7 @@ exports[`renders rejected correctly 1`] = `
|
||||
featured={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<CommentContent
|
||||
bannedWords={
|
||||
@@ -369,7 +395,7 @@ exports[`renders rejected correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
@@ -390,11 +416,13 @@ exports[`renders rejected correctly 1`] = `
|
||||
itemGutter={true}
|
||||
>
|
||||
<RejectButton
|
||||
className=""
|
||||
disabled={true}
|
||||
invert={true}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<ApproveButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
@@ -416,12 +444,17 @@ exports[`renders reply correctly 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<Username
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-username"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Theon
|
||||
</Username>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
<Timestamp>
|
||||
2018-11-29T16:01:51.897Z
|
||||
</Timestamp>
|
||||
@@ -429,7 +462,7 @@ exports[`renders reply correctly 1`] = `
|
||||
featured={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</ForwardRef(forwardRef)>
|
||||
<div>
|
||||
<InReplyTo>
|
||||
Julian
|
||||
@@ -475,7 +508,7 @@ exports[`renders reply correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
@@ -496,10 +529,12 @@ exports[`renders reply correctly 1`] = `
|
||||
itemGutter={true}
|
||||
>
|
||||
<RejectButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<ApproveButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
@@ -521,12 +556,17 @@ exports[`renders story info 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<Username
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
>
|
||||
<ForwardRef(forwardRef)
|
||||
className="ModerateCard-username"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Theon
|
||||
</Username>
|
||||
<Username>
|
||||
Theon
|
||||
</Username>
|
||||
</ForwardRef(forwardRef)>
|
||||
<Timestamp>
|
||||
2018-11-29T16:01:51.897Z
|
||||
</Timestamp>
|
||||
@@ -534,7 +574,7 @@ exports[`renders story info 1`] = `
|
||||
featured={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</ForwardRef(forwardRef)>
|
||||
</div>
|
||||
<CommentContent
|
||||
bannedWords={
|
||||
@@ -604,7 +644,7 @@ exports[`renders story info 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<ForwardRef(forwardRef)
|
||||
alignItems="center"
|
||||
@@ -625,10 +665,12 @@ exports[`renders story info 1`] = `
|
||||
itemGutter={true}
|
||||
>
|
||||
<RejectButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<ApproveButton
|
||||
className=""
|
||||
invert={false}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||
|
||||
import AutoLoadMore from "coral-admin/components/AutoLoadMore";
|
||||
@@ -7,6 +7,7 @@ import { Button, Flex, HorizontalGutter } from "coral-ui/components";
|
||||
import { PropTypesOf } from "coral-ui/types";
|
||||
|
||||
import ModerateCardContainer from "../ModerateCard";
|
||||
import UserHistoryDrawerContainer from "../UserHistoryDrawer/UserHistoryDrawerContainer";
|
||||
|
||||
import styles from "./Queue.css";
|
||||
|
||||
@@ -38,53 +39,76 @@ const Queue: FunctionComponent<Props> = ({
|
||||
viewer,
|
||||
viewNewCount,
|
||||
onViewNew,
|
||||
}) => (
|
||||
<HorizontalGutter className={styles.root} size="double">
|
||||
{Boolean(viewNewCount && viewNewCount > 0) && (
|
||||
<Flex justifyContent="center" className={styles.viewNewButtonContainer}>
|
||||
<Localized id="moderate-queue-viewNew" $count={viewNewCount}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="filled"
|
||||
onClick={onViewNew}
|
||||
className={styles.viewNewButton}
|
||||
}) => {
|
||||
const [userDrawerVisible, setUserDrawerVisible] = useState(false);
|
||||
const [userDrawerId, setUserDrawerID] = useState("");
|
||||
|
||||
const onShowUserDrawer = useCallback(
|
||||
(userID: string) => {
|
||||
setUserDrawerID(userID);
|
||||
setUserDrawerVisible(true);
|
||||
},
|
||||
[setUserDrawerVisible, setUserDrawerID]
|
||||
);
|
||||
const onHideUserDrawer = useCallback(() => {
|
||||
setUserDrawerVisible(false);
|
||||
setUserDrawerID("");
|
||||
}, [setUserDrawerVisible, setUserDrawerID]);
|
||||
|
||||
return (
|
||||
<HorizontalGutter className={styles.root} size="double">
|
||||
{Boolean(viewNewCount && viewNewCount > 0) && (
|
||||
<Flex justifyContent="center" className={styles.viewNewButtonContainer}>
|
||||
<Localized id="moderate-queue-viewNew" $count={viewNewCount}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="filled"
|
||||
onClick={onViewNew}
|
||||
className={styles.viewNewButton}
|
||||
>
|
||||
View {viewNewCount} new comments
|
||||
</Button>
|
||||
</Localized>
|
||||
</Flex>
|
||||
)}
|
||||
<TransitionGroup component={null} appear={false} enter={false} exit>
|
||||
{comments.map(c => (
|
||||
<CSSTransition
|
||||
key={c.id}
|
||||
timeout={400}
|
||||
classNames={{
|
||||
exit: styles.exitTransition,
|
||||
exitActive: styles.exitTransitionActive,
|
||||
exitDone: styles.exitTransitionDone,
|
||||
}}
|
||||
>
|
||||
View {viewNewCount} new comments
|
||||
</Button>
|
||||
</Localized>
|
||||
</Flex>
|
||||
)}
|
||||
<TransitionGroup component={null} appear={false} enter={false} exit>
|
||||
{comments.map(c => (
|
||||
<CSSTransition
|
||||
key={c.id}
|
||||
timeout={400}
|
||||
classNames={{
|
||||
exit: styles.exitTransition,
|
||||
exitActive: styles.exitTransitionActive,
|
||||
exitDone: styles.exitTransitionDone,
|
||||
}}
|
||||
>
|
||||
<ModerateCardContainer
|
||||
settings={settings}
|
||||
viewer={viewer}
|
||||
comment={c}
|
||||
danglingLogic={danglingLogic}
|
||||
showStoryInfo={Boolean(allStories)}
|
||||
<ModerateCardContainer
|
||||
settings={settings}
|
||||
viewer={viewer}
|
||||
comment={c}
|
||||
danglingLogic={danglingLogic}
|
||||
showStoryInfo={Boolean(allStories)}
|
||||
onUsernameClicked={onShowUserDrawer}
|
||||
/>
|
||||
</CSSTransition>
|
||||
))}
|
||||
</TransitionGroup>
|
||||
{hasMore && (
|
||||
<Flex justifyContent="center">
|
||||
<AutoLoadMore
|
||||
disableLoadMore={disableLoadMore}
|
||||
onLoadMore={onLoadMore}
|
||||
/>
|
||||
</CSSTransition>
|
||||
))}
|
||||
</TransitionGroup>
|
||||
{hasMore && (
|
||||
<Flex justifyContent="center">
|
||||
<AutoLoadMore
|
||||
disableLoadMore={disableLoadMore}
|
||||
onLoadMore={onLoadMore}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{comments.length === 0 && emptyElement}
|
||||
</HorizontalGutter>
|
||||
);
|
||||
</Flex>
|
||||
)}
|
||||
{comments.length === 0 && emptyElement}
|
||||
<UserHistoryDrawerContainer
|
||||
open={userDrawerVisible}
|
||||
onClose={onHideUserDrawer}
|
||||
userID={userDrawerId}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
export default Queue;
|
||||
|
||||
@@ -19,6 +19,11 @@ exports[`renders correctly with load more 1`] = `
|
||||
onLoadMore={[Function]}
|
||||
/>
|
||||
</ForwardRef(forwardRef)>
|
||||
<UserHistoryDrawerContainer
|
||||
onClose={[Function]}
|
||||
open={false}
|
||||
userID=""
|
||||
/>
|
||||
</ForwardRef(forwardRef)>
|
||||
`;
|
||||
|
||||
@@ -33,5 +38,10 @@ exports[`renders correctly without load more 1`] = `
|
||||
enter={false}
|
||||
exit={true}
|
||||
/>
|
||||
<UserHistoryDrawerContainer
|
||||
onClose={[Function]}
|
||||
open={false}
|
||||
userID=""
|
||||
/>
|
||||
</ForwardRef(forwardRef)>
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
.footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loadMore {
|
||||
width: 100%;
|
||||
background-color: var(--palette-common-white);
|
||||
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--palette-grey-lighter);
|
||||
border-radius: 2px;
|
||||
|
||||
margin-top: var(--spacing-1);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
useLoadMore,
|
||||
withPaginationContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import { graphql, RelayPaginationProp } from "react-relay";
|
||||
|
||||
import { Button, CallOut, Typography } from "coral-ui/components/";
|
||||
|
||||
import { UserHistoryAllComments_settings } from "coral-admin/__generated__/UserHistoryAllComments_settings.graphql";
|
||||
import { UserHistoryAllComments_user } from "coral-admin/__generated__/UserHistoryAllComments_user.graphql";
|
||||
import { UserHistoryAllComments_viewer } from "coral-admin/__generated__/UserHistoryAllComments_viewer.graphql";
|
||||
import { UserHistoryAllCommentsPaginationQueryVariables } from "coral-admin/__generated__/UserHistoryAllCommentsPaginationQuery.graphql";
|
||||
|
||||
import { ModerateCardContainer } from "../ModerateCard";
|
||||
|
||||
import styles from "./UserHistoryAllComments.css";
|
||||
|
||||
interface Props {
|
||||
user: UserHistoryAllComments_user;
|
||||
viewer: UserHistoryAllComments_viewer;
|
||||
settings: UserHistoryAllComments_settings;
|
||||
relay: RelayPaginationProp;
|
||||
}
|
||||
|
||||
const UserHistoryAllComments: FunctionComponent<Props> = ({
|
||||
user,
|
||||
viewer,
|
||||
settings,
|
||||
relay,
|
||||
}) => {
|
||||
const [loadMore, isLoadingMore] = useLoadMore(relay, 5);
|
||||
|
||||
const onLoadMore = useCallback(() => {
|
||||
if (!loadMore || isLoadingMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadMore();
|
||||
}, [loadMore]);
|
||||
|
||||
const hasMore = relay.hasMore();
|
||||
const comments = user ? user.allComments.edges.map(edge => edge.node) : [];
|
||||
|
||||
if (comments.length === 0) {
|
||||
return (
|
||||
<CallOut fullWidth>
|
||||
<Localized
|
||||
id="moderate-user-drawer-rejected-no-comments"
|
||||
$username={user.username}
|
||||
>
|
||||
<Typography variant="bodyCopy">
|
||||
{user.username} has not submitted any comments.
|
||||
</Typography>
|
||||
</Localized>
|
||||
</CallOut>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{comments.map((c, index) => (
|
||||
<div key={c.id}>
|
||||
<ModerateCardContainer
|
||||
comment={c}
|
||||
viewer={viewer}
|
||||
settings={settings}
|
||||
danglingLogic={status => false}
|
||||
hideUsername
|
||||
showStoryInfo
|
||||
mini
|
||||
/>
|
||||
{// Don't show horizontal rule after last comment
|
||||
index !== comments.length - 1 && <hr />}
|
||||
</div>
|
||||
))}
|
||||
{hasMore && (
|
||||
<div className={styles.footer}>
|
||||
<Button className={styles.loadMore} onClick={onLoadMore}>
|
||||
<Localized id="moderate-user-drawer-load-more">Load More</Localized>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type FragmentVariables = UserHistoryAllCommentsPaginationQueryVariables;
|
||||
|
||||
const enhanced = withPaginationContainer<
|
||||
Props,
|
||||
UserHistoryAllCommentsPaginationQueryVariables,
|
||||
FragmentVariables
|
||||
>(
|
||||
{
|
||||
viewer: graphql`
|
||||
fragment UserHistoryAllComments_viewer on User {
|
||||
...ModerateCardContainer_viewer
|
||||
}
|
||||
`,
|
||||
settings: graphql`
|
||||
fragment UserHistoryAllComments_settings on Settings {
|
||||
...ModerateCardContainer_settings
|
||||
}
|
||||
`,
|
||||
user: graphql`
|
||||
fragment UserHistoryAllComments_user on User
|
||||
@argumentDefinitions(
|
||||
count: { type: "Int!", defaultValue: 5 }
|
||||
cursor: { type: "Cursor" }
|
||||
) {
|
||||
username
|
||||
allComments(first: $count, after: $cursor)
|
||||
@connection(key: "UserHistoryAll_allComments") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...ModerateCardContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
direction: "forward",
|
||||
getConnectionFromProps(props) {
|
||||
return props.user && props.user.allComments;
|
||||
},
|
||||
getFragmentVariables(prevVars, totalCount) {
|
||||
return {
|
||||
...prevVars,
|
||||
count: totalCount,
|
||||
};
|
||||
},
|
||||
getVariables(props, { count, cursor }, fragmentVariables) {
|
||||
return {
|
||||
count,
|
||||
cursor,
|
||||
userID: fragmentVariables.userID,
|
||||
};
|
||||
},
|
||||
query: graphql`
|
||||
query UserHistoryAllCommentsPaginationQuery(
|
||||
$userID: ID!
|
||||
$count: Int!
|
||||
$cursor: Cursor
|
||||
) {
|
||||
user(id: $userID) {
|
||||
...UserHistoryAllComments_user
|
||||
@arguments(count: $count, cursor: $cursor)
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
)(UserHistoryAllComments);
|
||||
|
||||
export default enhanced;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
.root {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.callout {
|
||||
width: 100%;
|
||||
font-family: var(--font-family-sans-serif);
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { ReadyState } from "react-relay";
|
||||
|
||||
import { CallOut, Spinner } from "coral-ui/components";
|
||||
|
||||
import { UserHistoryAllCommentsContainerQuery as QueryTypes } from "coral-admin/__generated__/UserHistoryAllCommentsContainerQuery.graphql";
|
||||
import { graphql, QueryRenderer } from "coral-framework/lib/relay";
|
||||
|
||||
import UserHistoryAllComments from "./UserHistoryAllComments";
|
||||
|
||||
import styles from "./UserHistoryAllCommentsContainer.css";
|
||||
|
||||
interface Props {
|
||||
userID: string;
|
||||
}
|
||||
|
||||
const UserHistoryAllCommentsContainer: FunctionComponent<Props> = ({
|
||||
userID,
|
||||
}) => {
|
||||
return (
|
||||
<QueryRenderer<QueryTypes>
|
||||
query={graphql`
|
||||
query UserHistoryAllCommentsContainerQuery($userID: ID!) {
|
||||
user(id: $userID) {
|
||||
...UserHistoryAllComments_user
|
||||
}
|
||||
viewer {
|
||||
...UserHistoryAllComments_viewer
|
||||
}
|
||||
settings {
|
||||
...UserHistoryAllComments_settings
|
||||
}
|
||||
}
|
||||
`}
|
||||
variables={{ userID }}
|
||||
cacheConfig={{ force: true }}
|
||||
render={({ error, props }: ReadyState<QueryTypes["response"]>) => {
|
||||
if (!props) {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.user) {
|
||||
return (
|
||||
<div className={styles.callout}>
|
||||
<CallOut>
|
||||
<Localized id="moderate-user-drawer-user-not-found ">
|
||||
User not found.
|
||||
</Localized>
|
||||
</CallOut>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<UserHistoryAllComments
|
||||
// We can never get to this part of the UI without being logged in.
|
||||
viewer={props.viewer!}
|
||||
settings={props.settings!}
|
||||
user={props.user}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserHistoryAllCommentsContainer;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
.root {
|
||||
position: fixed;
|
||||
top:0;
|
||||
right: 0;
|
||||
|
||||
width: 624px;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: var(--palette-common-white);
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import { Card, Modal } from "coral-ui/components";
|
||||
|
||||
import styles from "./UserHistoryDrawerContainer.css";
|
||||
import UserHistoryDrawerQuery from "./UserHistoryDrawerQuery";
|
||||
|
||||
interface UserHistoryDrawerContainerProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
userID?: string;
|
||||
}
|
||||
|
||||
const UserHistoryDrawerContainer: FunctionComponent<
|
||||
UserHistoryDrawerContainerProps
|
||||
> = ({ open, onClose, userID }) => {
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
{({ firstFocusableRef, lastFocusableRef }) => (
|
||||
<Card className={styles.root}>
|
||||
{userID && (
|
||||
<UserHistoryDrawerQuery
|
||||
userID={userID}
|
||||
onClose={onClose}
|
||||
firstFocusableRef={firstFocusableRef}
|
||||
lastFocusableRef={lastFocusableRef}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserHistoryDrawerContainer;
|
||||
@@ -0,0 +1,94 @@
|
||||
.root {
|
||||
position: fixed;
|
||||
top:0;
|
||||
right: 0;
|
||||
|
||||
width: 624px;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: var(--palette-common-white);
|
||||
}
|
||||
|
||||
.comments {
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
left: -40px;
|
||||
|
||||
padding: 0px;
|
||||
|
||||
background-color: var(--palette-common-white);
|
||||
|
||||
border-width: 2px;
|
||||
border-right-width: 0px;
|
||||
border-style: solid;
|
||||
border-color: var(--palette-grey-main);
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-family: var(--font-family-serif);
|
||||
font-size: calc(24rem / var(--rem-base));
|
||||
font-weight: 500;
|
||||
font-family: var(--font-family-serif);
|
||||
line-height: calc(36em / 24);
|
||||
letter-spacing: calc(0.2em / 24);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.userDetails {
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.userDetail {
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.userDetailValue {
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.copy {
|
||||
border: 1px solid var(--palette-primary-main);
|
||||
background-color: transparent;
|
||||
color: var(--palette-primary-main);
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
&:active {
|
||||
background-color: var(--palette-primary-lightest);
|
||||
border-color: var(--palette-primary-main);
|
||||
color: var(--palette-primary-main);
|
||||
}
|
||||
}
|
||||
|
||||
.callout {
|
||||
width: 100%;
|
||||
font-family: var(--font-family-sans-serif);
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--palette-divider);
|
||||
padding-top: 1px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid var(--palette-grey-lighter);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import { graphql, QueryRenderer } from "coral-framework/lib/relay";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { ReadyState } from "react-relay";
|
||||
|
||||
import { CopyButton } from "coral-framework/components";
|
||||
import {
|
||||
Button,
|
||||
CallOut,
|
||||
Flex,
|
||||
Icon,
|
||||
Spinner,
|
||||
Typography,
|
||||
} from "coral-ui/components";
|
||||
|
||||
import { UserHistoryDrawerQuery as QueryTypes } from "coral-admin/__generated__/UserHistoryDrawerQuery.graphql";
|
||||
|
||||
import UserHistoryTabs from "./UserHistoryTabs";
|
||||
|
||||
import styles from "./UserHistoryDrawerQuery.css";
|
||||
|
||||
interface Props {
|
||||
userID: string;
|
||||
onClose: () => void;
|
||||
firstFocusableRef: React.RefObject<any>;
|
||||
lastFocusableRef: React.RefObject<any>;
|
||||
}
|
||||
|
||||
const UserHistoryDrawerQuery: FunctionComponent<Props> = ({
|
||||
userID,
|
||||
onClose,
|
||||
}) => {
|
||||
return (
|
||||
<QueryRenderer<QueryTypes>
|
||||
query={graphql`
|
||||
query UserHistoryDrawerQuery($userID: ID!) {
|
||||
user(id: $userID) {
|
||||
id
|
||||
username
|
||||
email
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
`}
|
||||
variables={{ userID }}
|
||||
cacheConfig={{ force: true }}
|
||||
render={({ error, props }: ReadyState<QueryTypes["response"]>) => {
|
||||
if (!props) {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.user) {
|
||||
return (
|
||||
<div className={styles.callout}>
|
||||
<CallOut>
|
||||
<Localized id="moderate-user-drawer-user-not-found ">
|
||||
User not found.
|
||||
</Localized>
|
||||
</CallOut>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const user = props.user;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button className={styles.close} onClick={onClose}>
|
||||
<Icon size="md">close</Icon>
|
||||
</Button>
|
||||
<Flex className={styles.username}>
|
||||
<span>{user.username}</span>
|
||||
</Flex>
|
||||
<div className={styles.userDetails}>
|
||||
<Flex alignItems="center" className={styles.userDetail}>
|
||||
<Icon size="sm" className={styles.icon}>
|
||||
mail_outline
|
||||
</Icon>
|
||||
<Typography
|
||||
variant="bodyCopy"
|
||||
container="span"
|
||||
className={styles.userDetailValue}
|
||||
>
|
||||
{user.email}
|
||||
</Typography>
|
||||
<CopyButton
|
||||
text={user.email!}
|
||||
variant="regular"
|
||||
className={styles.copy}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems="center" className={styles.userDetail}>
|
||||
<Icon size="sm" className={styles.icon}>
|
||||
date_range
|
||||
</Icon>
|
||||
<Typography variant="bodyCopy" container="span">
|
||||
{new Date(user.createdAt).toLocaleDateString("en-us", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
})}
|
||||
</Typography>
|
||||
</Flex>
|
||||
<Flex alignItems="center" className={styles.userDetail}>
|
||||
<Icon size="sm" className={styles.icon}>
|
||||
people_outline
|
||||
</Icon>
|
||||
<Typography
|
||||
variant="bodyCopy"
|
||||
container="span"
|
||||
className={styles.userDetailValue}
|
||||
>
|
||||
{user.id}
|
||||
</Typography>
|
||||
<CopyButton
|
||||
text={user.id}
|
||||
variant="regular"
|
||||
className={styles.copy}
|
||||
/>
|
||||
</Flex>
|
||||
</div>
|
||||
<hr className={styles.divider} />
|
||||
<div className={styles.comments}>
|
||||
<UserHistoryTabs userID={user.id} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserHistoryDrawerQuery;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
.footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loadMore {
|
||||
width: 100%;
|
||||
background-color: var(--palette-common-white);
|
||||
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--palette-grey-lighter);
|
||||
border-radius: 2px;
|
||||
|
||||
margin-top: var(--spacing-1);
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
import {
|
||||
useLoadMore,
|
||||
withPaginationContainer,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, useCallback } from "react";
|
||||
import { graphql, RelayPaginationProp } from "react-relay";
|
||||
|
||||
import { Button, CallOut, Typography } from "coral-ui/components";
|
||||
|
||||
import { UserHistoryRejectedComments_settings } from "coral-admin/__generated__/UserHistoryRejectedComments_settings.graphql";
|
||||
import { UserHistoryRejectedComments_user } from "coral-admin/__generated__/UserHistoryRejectedComments_user.graphql";
|
||||
import { UserHistoryRejectedComments_viewer } from "coral-admin/__generated__/UserHistoryRejectedComments_viewer.graphql";
|
||||
import { UserHistoryRejectedCommentsPaginationQueryVariables } from "coral-admin/__generated__/UserHistoryRejectedCommentsPaginationQuery.graphql";
|
||||
|
||||
import { ModerateCardContainer } from "../ModerateCard";
|
||||
|
||||
import styles from "./UserHistoryRejectedComments.css";
|
||||
|
||||
interface Props {
|
||||
user: UserHistoryRejectedComments_user;
|
||||
viewer: UserHistoryRejectedComments_viewer;
|
||||
settings: UserHistoryRejectedComments_settings;
|
||||
relay: RelayPaginationProp;
|
||||
}
|
||||
|
||||
const UserHistoryRejectedComments: FunctionComponent<Props> = ({
|
||||
user,
|
||||
viewer,
|
||||
settings,
|
||||
relay,
|
||||
}) => {
|
||||
const [loadMore, isLoadingMore] = useLoadMore(relay, 5);
|
||||
|
||||
const onLoadMore = useCallback(() => {
|
||||
if (!loadMore || isLoadingMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadMore();
|
||||
}, [loadMore]);
|
||||
|
||||
const hasMore = relay.hasMore();
|
||||
const comments = user
|
||||
? user.rejectedComments.edges.map(edge => edge.node)
|
||||
: [];
|
||||
|
||||
if (comments.length === 0) {
|
||||
return (
|
||||
<CallOut fullWidth>
|
||||
<Localized
|
||||
id="moderate-user-drawer-rejected-no-comments"
|
||||
$username={user.username}
|
||||
>
|
||||
<Typography variant="bodyCopy">
|
||||
{user.username} does not have any rejected comments.
|
||||
</Typography>
|
||||
</Localized>
|
||||
</CallOut>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{comments.map((c, index) => (
|
||||
<div key={c.id}>
|
||||
<ModerateCardContainer
|
||||
comment={c}
|
||||
viewer={viewer}
|
||||
settings={settings}
|
||||
danglingLogic={status => false}
|
||||
hideUsername
|
||||
showStoryInfo
|
||||
mini
|
||||
/>
|
||||
{// Don't show horizontal rule after last comment
|
||||
index !== comments.length - 1 && <hr />}
|
||||
</div>
|
||||
))}
|
||||
{hasMore && (
|
||||
<div className={styles.footer}>
|
||||
<Button className={styles.loadMore} onClick={onLoadMore}>
|
||||
<Localized id="moderate-user-drawer-load-more">Load More</Localized>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
type FragmentVariables = UserHistoryRejectedCommentsPaginationQueryVariables;
|
||||
|
||||
const enhanced = withPaginationContainer<
|
||||
Props,
|
||||
UserHistoryRejectedCommentsPaginationQueryVariables,
|
||||
FragmentVariables
|
||||
>(
|
||||
{
|
||||
viewer: graphql`
|
||||
fragment UserHistoryRejectedComments_viewer on User {
|
||||
...ModerateCardContainer_viewer
|
||||
}
|
||||
`,
|
||||
settings: graphql`
|
||||
fragment UserHistoryRejectedComments_settings on Settings {
|
||||
...ModerateCardContainer_settings
|
||||
}
|
||||
`,
|
||||
user: graphql`
|
||||
fragment UserHistoryRejectedComments_user on User
|
||||
@argumentDefinitions(
|
||||
count: { type: "Int!", defaultValue: 5 }
|
||||
cursor: { type: "Cursor" }
|
||||
) {
|
||||
username
|
||||
rejectedComments(first: $count, after: $cursor)
|
||||
@connection(key: "UserHistoryRejected_rejectedComments") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...ModerateCardContainer_comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
direction: "forward",
|
||||
getConnectionFromProps(props) {
|
||||
return props.user && props.user.rejectedComments;
|
||||
},
|
||||
getFragmentVariables(prevVars, totalCount) {
|
||||
return {
|
||||
...prevVars,
|
||||
count: totalCount,
|
||||
};
|
||||
},
|
||||
getVariables(props, { count, cursor }, fragmentVariables) {
|
||||
return {
|
||||
count,
|
||||
cursor,
|
||||
userID: fragmentVariables.userID,
|
||||
};
|
||||
},
|
||||
query: graphql`
|
||||
query UserHistoryRejectedCommentsPaginationQuery(
|
||||
$userID: ID!
|
||||
$count: Int!
|
||||
$cursor: Cursor
|
||||
) {
|
||||
user(id: $userID) {
|
||||
...UserHistoryRejectedComments_user
|
||||
@arguments(count: $count, cursor: $cursor)
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
)(UserHistoryRejectedComments);
|
||||
|
||||
export default enhanced;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
.root {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.callout {
|
||||
width: 100%;
|
||||
font-family: var(--font-family-sans-serif);
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { ReadyState } from "react-relay";
|
||||
|
||||
import { CallOut, Spinner } from "coral-ui/components";
|
||||
|
||||
import { UserHistoryRejectedCommentsContainerQuery as QueryTypes } from "coral-admin/__generated__/UserHistoryRejectedCommentsContainerQuery.graphql";
|
||||
import { graphql, QueryRenderer } from "coral-framework/lib/relay";
|
||||
|
||||
import UserHistoryRejectedComments from "./UserHistoryRejectedComments";
|
||||
|
||||
import styles from "./UserHistoryRejectedCommentsContainer.css";
|
||||
|
||||
interface Props {
|
||||
userID: string;
|
||||
}
|
||||
|
||||
const UserHistoryRejectedCommentsContainer: FunctionComponent<Props> = ({
|
||||
userID,
|
||||
}) => {
|
||||
return (
|
||||
<QueryRenderer<any>
|
||||
query={graphql`
|
||||
query UserHistoryRejectedCommentsContainerQuery($userID: ID!) {
|
||||
user(id: $userID) {
|
||||
...UserHistoryRejectedComments_user
|
||||
}
|
||||
viewer {
|
||||
...UserHistoryRejectedComments_viewer
|
||||
}
|
||||
settings {
|
||||
...UserHistoryRejectedComments_settings
|
||||
}
|
||||
}
|
||||
`}
|
||||
variables={{ userID }}
|
||||
cacheConfig={{ force: true }}
|
||||
render={({ error, props }: ReadyState<QueryTypes["response"]>) => {
|
||||
if (!props) {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!props.user) {
|
||||
return (
|
||||
<div className={styles.callout}>
|
||||
<CallOut>
|
||||
<Localized id="moderate-user-drawer-user-not-found ">
|
||||
User not found.
|
||||
</Localized>
|
||||
</CallOut>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<UserHistoryRejectedComments
|
||||
// We can never get to this part of the UI without being logged in.
|
||||
viewer={props.viewer!}
|
||||
settings={props.settings!}
|
||||
user={props.user}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserHistoryRejectedCommentsContainer;
|
||||
@@ -0,0 +1,59 @@
|
||||
.root {
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
min-height: 0;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tabBar {
|
||||
flex: none;
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
color: var(--palette-grey-dark);
|
||||
height: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 3px solid transparent;
|
||||
|
||||
font-size: calc(14rem / var(--rem-base));
|
||||
}
|
||||
|
||||
.activeTab {
|
||||
color: var(--palette-primary-main);
|
||||
}
|
||||
|
||||
.tabIcon {
|
||||
margin-right: var(--spacing-2);
|
||||
}
|
||||
|
||||
.tabContent {
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: calc(100% - 2 * var(--spacing-2));
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
min-height: 0;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
margin-bottom: var(--spacing-2);
|
||||
margin-top: var(--spacing-2);
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import cn from "classnames";
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
|
||||
import { Icon, Tab, TabBar, TabContent, TabPane } from "coral-ui/components";
|
||||
|
||||
import UserHistoryAllCommentsContainer from "./UserHistoryAllCommentsContainer";
|
||||
import UserHistoryRejectedCommentsContainer from "./UserHistoryRejectedCommentsContainer";
|
||||
|
||||
import styles from "./UserHistoryTabs.css";
|
||||
|
||||
type TabType = "ALL" | "REJECTED";
|
||||
|
||||
interface Props {
|
||||
userID: string;
|
||||
}
|
||||
|
||||
const UserHistoryTabs: FunctionComponent<Props> = ({ userID }) => {
|
||||
const [currentTab, setCurrentTab] = useState<TabType>("ALL");
|
||||
|
||||
const onTabChanged = useCallback(
|
||||
(tab: TabType) => {
|
||||
setCurrentTab(tab);
|
||||
},
|
||||
[setCurrentTab]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<TabBar
|
||||
activeTab={currentTab}
|
||||
variant="secondary"
|
||||
onTabClick={onTabChanged}
|
||||
className={styles.tabBar}
|
||||
>
|
||||
<Tab tabID="ALL" onTabClick={onTabChanged}>
|
||||
<div
|
||||
className={cn(styles.tab, {
|
||||
[styles.activeTab]: currentTab === "ALL",
|
||||
})}
|
||||
>
|
||||
<Icon size="sm" className={styles.tabIcon}>
|
||||
forum
|
||||
</Icon>
|
||||
<Localized id="moderate-user-drawer-tab-all-comments">
|
||||
<span>All Comments</span>
|
||||
</Localized>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab tabID="REJECTED" onTabClick={onTabChanged}>
|
||||
<div
|
||||
className={cn(styles.tab, {
|
||||
[styles.activeTab]: currentTab === "REJECTED",
|
||||
})}
|
||||
>
|
||||
<Icon size="sm" className={styles.tabIcon}>
|
||||
cancel
|
||||
</Icon>
|
||||
<Localized id="moderate-user-drawer-tab-rejected-comments">
|
||||
<span>Rejected</span>
|
||||
</Localized>
|
||||
</div>
|
||||
</Tab>
|
||||
</TabBar>
|
||||
<TabContent activeTab={currentTab} className={styles.tabContent}>
|
||||
<TabPane tabID="ALL">
|
||||
<div className={styles.container}>
|
||||
<div className={styles.scrollable}>
|
||||
<UserHistoryAllCommentsContainer userID={userID} />
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tabID="REJECTED">
|
||||
<div className={styles.container}>
|
||||
<div className={styles.scrollable}>
|
||||
<UserHistoryRejectedCommentsContainer userID={userID} />
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
</TabContent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserHistoryTabs;
|
||||
@@ -27,12 +27,25 @@ exports[`approves comment in reported queue: dangling 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -159,7 +172,7 @@ exports[`approves comment in reported queue: dangling 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -243,12 +256,25 @@ exports[`rejects comment in reported queue: dangling 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -375,7 +401,7 @@ exports[`rejects comment in reported queue: dangling 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -456,12 +482,25 @@ exports[`renders reported queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -588,7 +627,7 @@ exports[`renders reported queue with comments 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -656,12 +695,25 @@ exports[`renders reported queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Ngoc
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Ngoc
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -788,7 +840,7 @@ exports[`renders reported queue with comments 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -872,12 +924,25 @@ exports[`renders reported queue with comments 2`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -1004,7 +1069,7 @@ exports[`renders reported queue with comments 2`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -1072,12 +1137,25 @@ exports[`renders reported queue with comments 2`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Ngoc
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Ngoc
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -1204,7 +1282,7 @@ exports[`renders reported queue with comments 2`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -1278,12 +1356,25 @@ exports[`renders reported queue with comments and load more 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Max
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Max
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -1428,7 +1519,7 @@ exports[`renders reported queue with comments and load more 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
|
||||
@@ -27,12 +27,25 @@ exports[`approves comment in rejected queue: dangling 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -159,7 +172,7 @@ exports[`approves comment in rejected queue: dangling 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -240,12 +253,25 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -372,7 +398,7 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -440,12 +466,25 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Ngoc
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Ngoc
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -572,7 +611,7 @@ exports[`renders rejected queue with comments 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -646,12 +685,25 @@ exports[`renders rejected queue with comments and load more 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Max
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Max
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -791,7 +843,7 @@ exports[`renders rejected queue with comments and load more 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
|
||||
@@ -14,12 +14,25 @@ exports[`approves single comment 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -125,7 +138,7 @@ exports[`approves single comment 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -196,12 +209,25 @@ exports[`rejects single comment 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -307,7 +333,7 @@ exports[`rejects single comment 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
@@ -413,12 +439,25 @@ exports[`renders single comment view 1`] = `
|
||||
<div
|
||||
className="ModerateCard-topBar"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary ModerateCard-username Username-root"
|
||||
<div
|
||||
className="Box-root Flex-root Flex-flex Flex-alignCenter"
|
||||
>
|
||||
<button
|
||||
className="BaseButton-root ModerateCard-username"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
<span
|
||||
className="Box-root Typography-root Typography-heading4 Typography-colorTextPrimary Username-root"
|
||||
>
|
||||
Isabelle
|
||||
</span>
|
||||
</button>
|
||||
<time
|
||||
className="Timestamp-root RelativeTime-root"
|
||||
dateTime="2018-07-06T18:24:00.000Z"
|
||||
@@ -524,7 +563,7 @@ exports[`renders single comment view 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ModerateCard-separator"
|
||||
className="ModerateCard-separator ModerateCard-ruledSeparator"
|
||||
/>
|
||||
<div
|
||||
className="Box-root Flex-root ModerateCard-aside ModerateCard-asideWithoutReplyTo Flex-flex Flex-itemGutter Flex-alignCenter Flex-directionColumn gutter"
|
||||
|
||||
@@ -11,17 +11,22 @@ import {
|
||||
GQLUSER_ROLE,
|
||||
QueryToCommentsArgs,
|
||||
StoryToCommentsArgs,
|
||||
UserToAllCommentsArgs,
|
||||
UserToCommentsArgs,
|
||||
UserToRejectedCommentsArgs,
|
||||
} from "coral-server/graph/tenant/schema/__generated__/types";
|
||||
import { retrieveManyUserActionPresence } from "coral-server/models/action/comment";
|
||||
import {
|
||||
Comment,
|
||||
CommentConnectionInput,
|
||||
retrieveAllCommentsUserConnection,
|
||||
retrieveCommentConnection,
|
||||
retrieveCommentParentsConnection,
|
||||
retrieveCommentRepliesConnection,
|
||||
retrieveCommentStoryConnection,
|
||||
retrieveCommentUserConnection,
|
||||
retrieveManyComments,
|
||||
retrieveRejectedCommentUserConnection,
|
||||
retrieveStoryCommentTagCounts,
|
||||
} from "coral-server/models/comment";
|
||||
import { hasVisibleStatus } from "coral-server/models/comment/helpers";
|
||||
@@ -153,13 +158,33 @@ export default (ctx: Context) => ({
|
||||
first = 10,
|
||||
orderBy = GQLCOMMENT_SORT.CREATED_AT_DESC,
|
||||
after,
|
||||
}: StoryToCommentsArgs
|
||||
}: UserToCommentsArgs
|
||||
) =>
|
||||
retrieveCommentUserConnection(ctx.mongo, ctx.tenant.id, userID, {
|
||||
first,
|
||||
orderBy,
|
||||
after,
|
||||
}).then(primeCommentsFromConnection(ctx)),
|
||||
forUserAll: (
|
||||
userID: string,
|
||||
// Apply the graph schema defaults at the loader.
|
||||
{ first = 10, after }: UserToAllCommentsArgs
|
||||
) =>
|
||||
retrieveAllCommentsUserConnection(ctx.mongo, ctx.tenant.id, userID, {
|
||||
first,
|
||||
orderBy: GQLCOMMENT_SORT.CREATED_AT_DESC,
|
||||
after,
|
||||
}).then(primeCommentsFromConnection(ctx)),
|
||||
forUserRejected: (
|
||||
userID: string,
|
||||
// Apply the graph schema defaults at the loader.
|
||||
{ first = 10, after }: UserToRejectedCommentsArgs
|
||||
) =>
|
||||
retrieveRejectedCommentUserConnection(ctx.mongo, ctx.tenant.id, userID, {
|
||||
first,
|
||||
orderBy: GQLCOMMENT_SORT.CREATED_AT_DESC,
|
||||
after,
|
||||
}).then(primeCommentsFromConnection(ctx)),
|
||||
taggedForStory: (
|
||||
storyID: string,
|
||||
tag: GQLTAG,
|
||||
|
||||
@@ -34,6 +34,10 @@ const maybeLoadOnlyIgnoredUserID = (
|
||||
|
||||
export const User: GQLUserTypeResolver<user.User> = {
|
||||
comments: ({ id }, input, ctx) => ctx.loaders.Comments.forUser(id, input),
|
||||
allComments: ({ id }, input, ctx) =>
|
||||
ctx.loaders.Comments.forUserAll(id, input),
|
||||
rejectedComments: ({ id }, input, ctx) =>
|
||||
ctx.loaders.Comments.forUserRejected(id, input),
|
||||
commentModerationActionHistory: ({ id }, input, ctx) =>
|
||||
ctx.loaders.CommentModerationActions.forModerator(input, id),
|
||||
status: ({ id, status }): UserStatusInput => ({
|
||||
|
||||
@@ -1521,6 +1521,18 @@ type User {
|
||||
permit: [SUSPENDED, BANNED]
|
||||
)
|
||||
|
||||
"""
|
||||
allComments are comments regardless of visibility status.
|
||||
"""
|
||||
allComments(first: Int = 10, after: Cursor): CommentsConnection!
|
||||
@auth(roles: [ADMIN, MODERATOR])
|
||||
|
||||
"""
|
||||
rejectedComments are comments that have been rejected.
|
||||
"""
|
||||
rejectedComments(first: Int = 10, after: Cursor): CommentsConnection!
|
||||
@auth(roles: [ADMIN, MODERATOR])
|
||||
|
||||
"""
|
||||
commentModerationActionHistory returns a CommentModerationActionConnection
|
||||
that this User has created.
|
||||
|
||||
@@ -657,6 +657,57 @@ export const retrieveCommentUserConnection = (
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* retrieveAllCommentUserConnection returns a Connection<Comment> for a given User's
|
||||
* comments regardless of comment status.
|
||||
*
|
||||
* @param mongo database connection
|
||||
* @param tenantID the Tenant's ID
|
||||
* @param userID the User id for the comment to retrieve
|
||||
* @param input connection configuration
|
||||
*/
|
||||
export const retrieveAllCommentsUserConnection = (
|
||||
mongo: Db,
|
||||
tenantID: string,
|
||||
userID: string,
|
||||
input: CommentConnectionInput
|
||||
) =>
|
||||
retrieveCommentConnection(mongo, tenantID, {
|
||||
...input,
|
||||
filter: {
|
||||
...input.filter,
|
||||
authorID: userID,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* retrieveRejectedCommentUserConnection returns a Connection<Comment> for a given User's
|
||||
* rejected comments.
|
||||
*
|
||||
* @param mongo database connection
|
||||
* @param tenantID the Tenant's ID
|
||||
* @param userID the User id for the comment to retrieve
|
||||
* @param input connection configuration
|
||||
*/
|
||||
export const retrieveRejectedCommentUserConnection = (
|
||||
mongo: Db,
|
||||
tenantID: string,
|
||||
userID: string,
|
||||
input: CommentConnectionInput
|
||||
) =>
|
||||
retrieveStatusCommentConnection(
|
||||
mongo,
|
||||
tenantID,
|
||||
[GQLCOMMENT_STATUS.REJECTED],
|
||||
{
|
||||
...input,
|
||||
filter: {
|
||||
...input.filter,
|
||||
authorID: userID,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* retrieveVisibleCommentConnection will retrieve a connection that contains
|
||||
* comments that are visible.
|
||||
|
||||
@@ -388,6 +388,15 @@ moderate-searchBar-comboBoxTextField =
|
||||
moderate-searchBar-goTo = Go to
|
||||
moderate-searchBar-seeAllResults = See all results
|
||||
|
||||
### Moderate User History Drawer
|
||||
|
||||
moderate-user-drawer-tab-all-comments = All Comments
|
||||
moderate-user-drawer-tab-rejected-comments = Rejected
|
||||
moderate-user-drawer-load-more = Load More
|
||||
moderate-user-drawer-all-no-comments = {$username} has not submitted any comments.
|
||||
moderate-user-drawer-rejected-no-comments = {$username} does not have any rejected comments.
|
||||
moderate-user-drawer-user-not-found = User not found.
|
||||
|
||||
## Create Username
|
||||
|
||||
createUsername-createUsernameHeader = Create Username
|
||||
|
||||
Reference in New Issue
Block a user