Implement Show Conversation Link

This commit is contained in:
Chi Vinh Le
2018-09-25 21:42:15 +02:00
parent 122b9e9871
commit b6554b344c
13 changed files with 147 additions and 54 deletions
-1
View File
@@ -25,7 +25,6 @@ export default class PymControl {
title: config.title,
id: `${config.id}_iframe`,
name: `${config.id}_iframe`,
optionalparams: "",
});
this.cleanups = decorators
@@ -0,0 +1,8 @@
import { modifyQuery } from "talk-framework/utils";
export default function getURLWithCommentID(
assetURL: string,
commentID?: string
) {
return modifyQuery(assetURL, { commentID });
}
@@ -1 +1,2 @@
export { default as getMe } from "./getMe";
export { default as getURLWithCommentID } from "./getURLWithCommentID";
@@ -0,0 +1,13 @@
import React, { StatelessComponent } from "react";
import { Flex } from "talk-ui/components";
const ButtonsBar: StatelessComponent = props => {
return (
<Flex direction="row" itemGutter="half">
{props.children}
</Flex>
);
};
export default ButtonsBar;
@@ -2,7 +2,7 @@ import React, { StatelessComponent } from "react";
import HTMLContent from "talk-stream/components/HTMLContent";
import Timestamp from "talk-stream/components/Timestamp";
import { Flex } from "talk-ui/components";
import { Flex, HorizontalGutter } from "talk-ui/components";
import * as styles from "./Comment.css";
import EditedMarker from "./EditedMarker";
@@ -43,10 +43,10 @@ const Comment: StatelessComponent<CommentProps> = props => {
</TopBarLeft>
{props.topBarRight && <div>{props.topBarRight}</div>}
</Flex>
<HTMLContent>{props.body || ""}</HTMLContent>
<Flex className={styles.footer} direction="row" itemGutter="half">
<HorizontalGutter>
<HTMLContent>{props.body || ""}</HTMLContent>
{props.footer}
</Flex>
</HorizontalGutter>
</div>
);
};
@@ -0,0 +1,31 @@
import React, { EventHandler, MouseEvent } from "react";
import { StatelessComponent } from "react";
import { Localized } from "fluent-react/compat";
import { Button } from "talk-ui/components";
export interface ShowConversationLinkProps {
href?: string;
onClick?: EventHandler<MouseEvent>;
}
const ShowConversationLink: StatelessComponent<
ShowConversationLinkProps
> = props => {
return (
<Localized id="comments-showConversationLink-readMore">
<Button
variant="underlined"
color="primary"
href={props.href}
onClick={props.onClick}
target="_parent"
anchor
>
Read More of this Conversation >
</Button>
</Localized>
);
};
export default ShowConversationLink;
@@ -1,3 +1,5 @@
export { default, default as IndentedComment } from "./IndentedComment";
export { default as TopBarLeft } from "./TopBarLeft";
export { default as Username } from "./Username";
export { default as ButtonsBar } from "./ButtonsBar";
export { default as ShowConversationLink } from "./ShowConversationLink";
@@ -15,7 +15,9 @@ export interface ReplyListProps {
id: string;
};
comments: ReadonlyArray<
{ id: string } & PropTypesOf<typeof CommentContainer>["comment"]
{ id: string; showConversationLink?: boolean } & PropTypesOf<
typeof CommentContainer
>["comment"]
>;
onShowAll?: () => void;
hasMore?: boolean;
@@ -52,6 +54,7 @@ const ReplyList: StatelessComponent<ReplyListProps> = props => {
indentLevel={props.indentLevel}
localReply={props.localReply}
disableReplies={props.disableReplies}
showConversationLink={!!comment.showConversationLink}
/>
{getReplyListElement(props, comment)}
</HorizontalGutter>
@@ -1,20 +1,26 @@
import { Localized } from "fluent-react/compat";
import React, { Component } from "react";
import React, { Component, MouseEvent } from "react";
import { graphql } from "react-relay";
import { isBeforeDate } from "talk-common/utils";
import { getURLWithCommentID } from "talk-framework/helpers";
import withFragmentContainer from "talk-framework/lib/relay/withFragmentContainer";
import { PropTypesOf } from "talk-framework/types";
import { CommentContainer_asset as AssetData } from "talk-stream/__generated__/CommentContainer_asset.graphql";
import { CommentContainer_comment as CommentData } from "talk-stream/__generated__/CommentContainer_comment.graphql";
import { CommentContainer_me as MeData } from "talk-stream/__generated__/CommentContainer_me.graphql";
import {
SetCommentIDMutation,
ShowAuthPopupMutation,
withSetCommentIDMutation,
withShowAuthPopupMutation,
} from "talk-stream/mutations";
import { Button } from "talk-ui/components";
import Comment from "../components/Comment";
import Comment, {
ButtonsBar,
ShowConversationLink,
} from "../components/Comment";
import ReplyButton from "../components/Comment/ReplyButton";
import EditCommentFormContainer from "./EditCommentFormContainer";
import PermalinkButtonContainer from "./PermalinkButtonContainer";
@@ -26,6 +32,7 @@ interface InnerProps {
asset: AssetData;
indentLevel?: number;
showAuthPopup: ShowAuthPopupMutation;
setCommentID: SetCommentIDMutation;
/**
* localReply will integrate the mutation response into
* localReplies
@@ -33,6 +40,8 @@ interface InnerProps {
localReply?: boolean;
/** disableReplies will remove the ReplyButton */
disableReplies?: boolean;
/** showConversationLink will render a link to the conversation */
showConversationLink?: boolean;
}
interface State {
@@ -113,6 +122,12 @@ export class CommentContainer extends Component<InnerProps, State> {
return;
}
private handleShowConversation = (e: MouseEvent) => {
e.preventDefault();
this.props.setCommentID({ id: this.props.comment.id });
return false;
};
public render() {
const {
comment,
@@ -120,6 +135,7 @@ export class CommentContainer extends Component<InnerProps, State> {
indentLevel,
localReply,
disableReplies,
showConversationLink,
} = this.props;
const { showReplyDialog, showEditDialog, editable } = this.state;
if (showEditDialog) {
@@ -157,14 +173,25 @@ export class CommentContainer extends Component<InnerProps, State> {
}
footer={
<>
{!disableReplies && (
<ReplyButton
id={`comments-commentContainer-replyButton-${comment.id}`}
onClick={this.openReplyDialog}
active={showReplyDialog}
<ButtonsBar>
{!disableReplies && (
<ReplyButton
id={`comments-commentContainer-replyButton-${comment.id}`}
onClick={this.openReplyDialog}
active={showReplyDialog}
/>
)}
<PermalinkButtonContainer commentID={comment.id} />
</ButtonsBar>
{showConversationLink && (
<ShowConversationLink
onClick={this.handleShowConversation}
href={getURLWithCommentID(
this.props.asset.url,
this.props.comment.id
)}
/>
)}
<PermalinkButtonContainer commentID={comment.id} />
</>
}
/>
@@ -181,37 +208,40 @@ export class CommentContainer extends Component<InnerProps, State> {
}
}
const enhanced = withShowAuthPopupMutation(
withFragmentContainer<InnerProps>({
me: graphql`
fragment CommentContainer_me on User {
id
}
`,
asset: graphql`
fragment CommentContainer_asset on Asset {
...ReplyCommentFormContainer_asset
}
`,
comment: graphql`
fragment CommentContainer_comment on Comment {
id
author {
const enhanced = withSetCommentIDMutation(
withShowAuthPopupMutation(
withFragmentContainer<InnerProps>({
me: graphql`
fragment CommentContainer_me on User {
id
username
}
body
createdAt
editing {
edited
editableUntil
`,
asset: graphql`
fragment CommentContainer_asset on Asset {
url
...ReplyCommentFormContainer_asset
}
pending
...ReplyCommentFormContainer_comment
...EditCommentFormContainer_comment
}
`,
})(CommentContainer)
`,
comment: graphql`
fragment CommentContainer_comment on Comment {
id
author {
id
username
}
body
createdAt
editing {
edited
editableUntil
}
pending
...ReplyCommentFormContainer_comment
...EditCommentFormContainer_comment
}
`,
})(CommentContainer)
)
);
export type CommentContainerProps = PropTypesOf<typeof enhanced>;
@@ -1,7 +1,7 @@
import React, { StatelessComponent } from "react";
import { graphql } from "react-relay";
import { getURLWithCommentID } from "talk-framework/helpers";
import { withLocalStateContainer } from "talk-framework/lib/relay";
import { modifyQuery } from "talk-framework/utils";
import { PermalinkButtonContainerLocal as Local } from "talk-stream/__generated__/PermalinkButtonContainerLocal.graphql";
import PermalinkButton from "../components/PermalinkButton";
@@ -18,7 +18,7 @@ export const PermalinkContainer: StatelessComponent<InnerProps> = ({
return local.assetURL ? (
<PermalinkButton
commentID={commentID}
url={modifyQuery(local.assetURL, { commentID })}
url={getURLWithCommentID(local.assetURL, commentID)}
/>
) : null;
};
@@ -1,10 +1,10 @@
import { Child as PymChild } from "pym.js";
import qs from "query-string";
import React, { MouseEvent } from "react";
import { graphql } from "react-relay";
import { getURLWithCommentID } from "talk-framework/helpers";
import { withContext } from "talk-framework/lib/bootstrap";
import { withFragmentContainer } from "talk-framework/lib/relay";
import { buildURL, parseURL } from "talk-framework/utils";
import { PermalinkViewContainer_asset as AssetData } from "talk-stream/__generated__/PermalinkViewContainer_asset.graphql";
import { PermalinkViewContainer_comment as CommentData } from "talk-stream/__generated__/PermalinkViewContainer_comment.graphql";
import { PermalinkViewContainer_me as MeData } from "talk-stream/__generated__/PermalinkViewContainer_me.graphql";
@@ -32,13 +32,8 @@ class PermalinkViewContainer extends React.Component<
};
private getShowAllCommentsHref() {
const { pym } = this.props;
const urlParts = parseURL((pym && pym.parentUrl) || window.location.href);
const search = qs.stringify({
...qs.parse(urlParts.search),
commentID: undefined,
});
// Remove the commentId url param.
return buildURL({ ...urlParts, search });
const url = (pym && pym.parentUrl) || window.location.href;
return getURLWithCommentID(url, undefined);
}
public componentDidMount() {
@@ -11,11 +11,15 @@ import {
COMMENT_SORT,
ReplyListContainer1PaginationQueryVariables,
} from "talk-stream/__generated__/ReplyListContainer1PaginationQuery.graphql";
import { ReplyListContainer5_comment as Comment5Data } from "talk-stream/__generated__/ReplyListContainer5_comment.graphql";
import { StatelessComponent } from "enzyme";
import ReplyList from "../components/ReplyList";
import LocalReplyListContainer from "./LocalReplyListContainer";
type UnpackArray<T> = T extends ReadonlyArray<infer U> ? U : any;
type ReplyNode5 = UnpackArray<Comment5Data["replies"]["edges"]>["node"];
export interface InnerProps {
me: MeData | null;
asset: AssetData;
@@ -44,7 +48,11 @@ export class ReplyListContainer extends React.Component<InnerProps> {
) {
return null;
}
const comments = this.props.comment.replies.edges.map(edge => edge.node);
const comments = this.props.comment.replies.edges.map(edge => ({
...edge.node,
// ReplyListContainer5 contains replyCount.
showConversationLink: ((edge.node as any) as ReplyNode5).replyCount > 0,
}));
return (
<ReplyList
me={this.props.me}
@@ -156,6 +164,7 @@ const ReplyListContainer5 = createReplyListContainer(
edges {
node {
id
replyCount
...CommentContainer_comment
...LocalReplyListContainer_comment
}
+2
View File
@@ -68,3 +68,5 @@ comments-editCommentForm-rte =
comments-editCommentForm-editRemainingTime = Edit: <time></time> remaining
comments-editCommentForm-editTimeExpired = Edit time has expired. You can no longer edit this comment. Why not post another one?
comments-editedMarker-edited = Edited
comments-showConversationLink-readMore = Read More of this Conversation >