mirror of
https://github.com/wassname/talk.git
synced 2026-08-10 12:41:02 +08:00
Implement Show Conversation Link
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 >
|
||||
|
||||
Reference in New Issue
Block a user