mirror of
https://github.com/wassname/talk.git
synced 2026-07-19 11:28:50 +08:00
fix: translations, typings, cleanup and fix view conversation
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import * as React from "react";
|
||||
import { StatelessComponent } from "react";
|
||||
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
TabPane,
|
||||
} from "talk-ui/components";
|
||||
|
||||
import { PropTypesOf } from "talk-ui/types";
|
||||
import CommentsPaneContainer from "../tabs/comments/containers/CommentsPaneContainer";
|
||||
import ProfileQuery from "../tabs/profile/queries/ProfileQuery";
|
||||
import * as styles from "./App.css";
|
||||
@@ -20,12 +22,24 @@ export interface AppProps {
|
||||
onTabClick: (tab: TabValue) => void;
|
||||
}
|
||||
|
||||
const CommentsTab: StatelessComponent<PropTypesOf<typeof Tab>> = props => (
|
||||
<Localized id="general-app-commentsTab">
|
||||
<Tab {...props}>Comments</Tab>
|
||||
</Localized>
|
||||
);
|
||||
|
||||
const MyProfileTab: StatelessComponent<PropTypesOf<typeof Tab>> = props => (
|
||||
<Localized id="general-app-myProfileTab">
|
||||
<Tab {...props}>My Profile</Tab>
|
||||
</Localized>
|
||||
);
|
||||
|
||||
const App: StatelessComponent<AppProps> = props => {
|
||||
return (
|
||||
<HorizontalGutter className={styles.root}>
|
||||
<TabBar activeTab={props.activeTab} onTabClick={props.onTabClick}>
|
||||
<Tab tabId="COMMENTS">Comments</Tab>
|
||||
<Tab tabId="PROFILE">My Profile</Tab>
|
||||
<CommentsTab tabId="COMMENTS" />
|
||||
<MyProfileTab tabId="PROFILE" />
|
||||
</TabBar>
|
||||
<TabContent activeTab={props.activeTab} className={styles.tabContent}>
|
||||
<TabPane tabId="COMMENTS">
|
||||
|
||||
@@ -5,17 +5,18 @@ import { HorizontalGutter, Typography } from "talk-ui/components";
|
||||
import HistoryComment from "./HistoryComment";
|
||||
|
||||
interface Comment {
|
||||
readonly id: string;
|
||||
readonly body: string | null;
|
||||
readonly createdAt: any;
|
||||
readonly replyCount: number | null;
|
||||
readonly asset: {
|
||||
readonly title: string | null;
|
||||
id: string;
|
||||
body: string | null;
|
||||
createdAt: any;
|
||||
replyCount: number | null;
|
||||
asset: {
|
||||
title: string | null;
|
||||
};
|
||||
conversationURL: string;
|
||||
onGotoConversation: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
interface CommentsHistoryProps {
|
||||
onGoToConversation: (id: string) => void;
|
||||
comments: Comment[];
|
||||
}
|
||||
|
||||
@@ -26,11 +27,7 @@ const CommentsHistory: StatelessComponent<CommentsHistoryProps> = props => {
|
||||
<Typography variant="heading3">Comment History</Typography>
|
||||
</Localized>
|
||||
{props.comments.map(comment => (
|
||||
<HistoryComment
|
||||
key={comment.id}
|
||||
comment={comment}
|
||||
onGoToConversation={() => props.onGoToConversation(comment.id)}
|
||||
/>
|
||||
<HistoryComment key={comment.id} {...comment} />
|
||||
))}
|
||||
</HorizontalGutter>
|
||||
);
|
||||
|
||||
@@ -3,25 +3,24 @@ import * as React from "react";
|
||||
import { StatelessComponent } from "react";
|
||||
import Timestamp from "talk-stream/components/Timestamp";
|
||||
import {
|
||||
BaseButton,
|
||||
ButtonIcon,
|
||||
Button,
|
||||
Flex,
|
||||
HorizontalGutter,
|
||||
Icon,
|
||||
Typography,
|
||||
} from "talk-ui/components";
|
||||
import HTMLContent from "../../../components/HTMLContent";
|
||||
import * as styles from "./HistoryComment.css";
|
||||
|
||||
export interface HistoryCommentProps {
|
||||
comment: {
|
||||
body: string | null;
|
||||
createdAt: string;
|
||||
replyCount: number | null;
|
||||
asset: {
|
||||
title: string | null;
|
||||
};
|
||||
body: string | null;
|
||||
createdAt: string;
|
||||
replyCount: number | null;
|
||||
asset: {
|
||||
title: string | null;
|
||||
};
|
||||
onGoToConversation: () => void;
|
||||
conversationURL: string;
|
||||
onGotoConversation: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
const HistoryComment: StatelessComponent<HistoryCommentProps> = props => {
|
||||
@@ -29,60 +28,50 @@ const HistoryComment: StatelessComponent<HistoryCommentProps> = props => {
|
||||
<HorizontalGutter>
|
||||
<Flex direction="row" justifyContent="space-between">
|
||||
<Typography variant="bodyCopy" container="div">
|
||||
{props.comment.body && (
|
||||
<HTMLContent className={styles.body}>
|
||||
{props.comment.body}
|
||||
</HTMLContent>
|
||||
{props.body && (
|
||||
<HTMLContent className={styles.body}>{props.body}</HTMLContent>
|
||||
)}
|
||||
</Typography>
|
||||
<Flex className={styles.sideBar} direction="column">
|
||||
<Flex direction="row" alignItems="center" itemGutter="half">
|
||||
<ButtonIcon className={styles.icon}>launch</ButtonIcon>
|
||||
<Localized id="profile-historyComment-viewConversation">
|
||||
<BaseButton
|
||||
className={styles.button}
|
||||
onClick={props.onGoToConversation}
|
||||
anchor
|
||||
>
|
||||
View Conversation
|
||||
</BaseButton>
|
||||
</Localized>
|
||||
<Button
|
||||
variant="underlined"
|
||||
target="_parent"
|
||||
href={props.conversationURL}
|
||||
onClick={props.onGotoConversation}
|
||||
anchor
|
||||
>
|
||||
<Icon>launch</Icon>
|
||||
<Localized id="profile-historyComment-viewConversation">
|
||||
<span>View Conversation</span>
|
||||
</Localized>
|
||||
</Button>
|
||||
</Flex>
|
||||
<Flex direction="row" alignItems="center" itemGutter="half">
|
||||
<ButtonIcon className={styles.icon}>schedule</ButtonIcon>
|
||||
<Timestamp>{props.comment.createdAt}</Timestamp>
|
||||
<Icon className={styles.icon}>schedule</Icon>
|
||||
<Timestamp>{props.createdAt}</Timestamp>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{!!props.comment.replyCount && (
|
||||
{!!props.replyCount && (
|
||||
<Flex
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
itemGutter="half"
|
||||
className={styles.replies}
|
||||
>
|
||||
<ButtonIcon className={styles.icon}>reply</ButtonIcon>
|
||||
<Icon className={styles.icon}>reply</Icon>
|
||||
<Localized
|
||||
id="profile-historyComment-replies"
|
||||
$replyCount={props.comment.replyCount}
|
||||
$replyCount={props.replyCount}
|
||||
>
|
||||
<span>{"Replies {$replyCount}"}</span>
|
||||
</Localized>
|
||||
</Flex>
|
||||
)}
|
||||
<Flex
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
itemGutter="half"
|
||||
className={styles.story}
|
||||
>
|
||||
<Localized
|
||||
id="profile-historyComment-story"
|
||||
$title={props.comment.asset.title}
|
||||
>
|
||||
<span>{"Story: {$title}"}</span>
|
||||
</Localized>
|
||||
</Flex>
|
||||
<Localized id="profile-historyComment-story" $title={props.asset.title}>
|
||||
<span className={styles.story}>{"Story: {$title}"}</span>
|
||||
</Localized>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,17 +6,18 @@ import { HorizontalGutter } from "talk-ui/components";
|
||||
import CommentsHistoryContainer from "../containers/CommentsHistoryContainer";
|
||||
|
||||
export interface ProfileProps {
|
||||
me:
|
||||
| PropTypesOf<typeof UserBoxContainer>["me"] &
|
||||
PropTypesOf<typeof CommentsHistoryContainer>["me"]
|
||||
| null;
|
||||
asset: PropTypesOf<typeof CommentsHistoryContainer>["asset"];
|
||||
me: PropTypesOf<typeof UserBoxContainer>["me"] &
|
||||
PropTypesOf<typeof CommentsHistoryContainer>["me"];
|
||||
}
|
||||
|
||||
const Profile: StatelessComponent<ProfileProps> = props => {
|
||||
return (
|
||||
<HorizontalGutter size="double">
|
||||
<UserBoxContainer me={props.me} />
|
||||
{props.me && <CommentsHistoryContainer me={props.me} />}
|
||||
{props.me && (
|
||||
<CommentsHistoryContainer me={props.me} asset={props.asset} />
|
||||
)}
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { getURLWithCommentID } from "talk-framework/helpers";
|
||||
import { withFragmentContainer } from "talk-framework/lib/relay";
|
||||
import { CommentsHistoryContainer_asset as AssetData } from "talk-stream/__generated__/CommentsHistoryContainer_asset.graphql";
|
||||
import { CommentsHistoryContainer_me as CommentsData } from "talk-stream/__generated__/CommentsHistoryContainer_me.graphql";
|
||||
import {
|
||||
SetCommentIDMutation,
|
||||
@@ -11,6 +14,7 @@ import CommentsHistory from "../components/CommentsHistory";
|
||||
interface CommentsHistoryContainerProps {
|
||||
setCommentID: SetCommentIDMutation;
|
||||
me: CommentsData;
|
||||
asset: AssetData;
|
||||
}
|
||||
|
||||
export class CommentsHistoryContainer extends React.Component<
|
||||
@@ -20,18 +24,27 @@ export class CommentsHistoryContainer extends React.Component<
|
||||
this.props.setCommentID({ id });
|
||||
};
|
||||
public render() {
|
||||
const comments = this.props.me.comments.edges.map(edge => edge.node);
|
||||
return (
|
||||
<CommentsHistory
|
||||
comments={comments}
|
||||
onGoToConversation={this.onGoToConversation}
|
||||
/>
|
||||
);
|
||||
const comments = this.props.me.comments.edges.map(edge => ({
|
||||
...edge.node,
|
||||
conversationURL: getURLWithCommentID(edge.node.asset.url, edge.node.id),
|
||||
onGotoConversation: (e: React.MouseEvent) => {
|
||||
if (this.props.asset.id === edge.node.asset.id) {
|
||||
this.onGoToConversation(edge.node.id);
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
}));
|
||||
return <CommentsHistory comments={comments} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withSetCommentIDMutation(
|
||||
withFragmentContainer<CommentsHistoryContainerProps>({
|
||||
asset: graphql`
|
||||
fragment CommentsHistoryContainer_asset on Asset {
|
||||
id
|
||||
}
|
||||
`,
|
||||
me: graphql`
|
||||
fragment CommentsHistoryContainer_me on User {
|
||||
comments {
|
||||
@@ -42,7 +55,9 @@ const enhanced = withSetCommentIDMutation(
|
||||
createdAt
|
||||
replyCount
|
||||
asset {
|
||||
id
|
||||
title
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,27 @@ import React from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "talk-framework/lib/relay";
|
||||
import { ProfileContainer_asset as AssetData } from "talk-stream/__generated__/ProfileContainer_asset.graphql";
|
||||
import { ProfileContainer_me as MeData } from "talk-stream/__generated__/ProfileContainer_me.graphql";
|
||||
|
||||
import Profile from "../components/Profile";
|
||||
|
||||
interface ProfileContainerProps {
|
||||
me: MeData | null;
|
||||
me: MeData;
|
||||
asset: AssetData;
|
||||
}
|
||||
|
||||
export class StreamContainer extends React.Component<ProfileContainerProps> {
|
||||
public render() {
|
||||
if (this.props.me) {
|
||||
return <Profile me={this.props.me} />;
|
||||
}
|
||||
return null;
|
||||
return <Profile me={this.props.me} asset={this.props.asset} />;
|
||||
}
|
||||
}
|
||||
const enhanced = withFragmentContainer<ProfileContainerProps>({
|
||||
asset: graphql`
|
||||
fragment ProfileContainer_asset on Asset {
|
||||
...CommentsHistoryContainer_asset
|
||||
}
|
||||
`,
|
||||
me: graphql`
|
||||
fragment ProfileContainer_me on User {
|
||||
...UserBoxContainer_me
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
import { Localized } from "fluent-react/compat";
|
||||
import React, { StatelessComponent } from "react";
|
||||
import { ReadyState } from "react-relay";
|
||||
import { graphql, QueryRenderer } from "talk-framework/lib/relay";
|
||||
|
||||
import {
|
||||
graphql,
|
||||
QueryRenderer,
|
||||
withLocalStateContainer,
|
||||
} from "talk-framework/lib/relay";
|
||||
import { ProfileQuery as QueryTypes } from "talk-stream/__generated__/ProfileQuery.graphql";
|
||||
import { ProfileQueryLocal as Local } from "talk-stream/__generated__/ProfileQueryLocal.graphql";
|
||||
import { Spinner } from "talk-ui/components";
|
||||
|
||||
import ProfileContainer from "../containers/ProfileContainer";
|
||||
|
||||
interface InnerProps {
|
||||
local: Local;
|
||||
}
|
||||
|
||||
export const render = ({
|
||||
error,
|
||||
props,
|
||||
@@ -15,26 +27,54 @@ export const render = ({
|
||||
|
||||
if (props) {
|
||||
if (!props.me) {
|
||||
return <div>Error loading profile</div>;
|
||||
return (
|
||||
<Localized id="profile-profileQuery-errorLoadingProfile">
|
||||
<div>Error loading profile</div>
|
||||
</Localized>
|
||||
);
|
||||
}
|
||||
return <ProfileContainer me={props.me} />;
|
||||
if (!props.asset) {
|
||||
return (
|
||||
<Localized id="comments-profileQuery-assetNotFound">
|
||||
<div>Asset not found</div>
|
||||
</Localized>
|
||||
);
|
||||
}
|
||||
return <ProfileContainer me={props.me} asset={props.asset} />;
|
||||
}
|
||||
|
||||
return <Spinner />;
|
||||
};
|
||||
|
||||
const ProfileQuery: StatelessComponent = () => (
|
||||
const ProfileQuery: StatelessComponent<InnerProps> = ({
|
||||
local: { assetID, assetURL },
|
||||
}) => (
|
||||
<QueryRenderer<QueryTypes>
|
||||
query={graphql`
|
||||
query ProfileQuery {
|
||||
query ProfileQuery($assetID: ID, $assetURL: String) {
|
||||
asset(id: $assetID, url: $assetURL) {
|
||||
...ProfileContainer_asset
|
||||
}
|
||||
me {
|
||||
...ProfileContainer_me
|
||||
}
|
||||
}
|
||||
`}
|
||||
variables={{}}
|
||||
variables={{
|
||||
assetID,
|
||||
assetURL,
|
||||
}}
|
||||
render={render}
|
||||
/>
|
||||
);
|
||||
|
||||
export default ProfileQuery;
|
||||
const enhanced = withLocalStateContainer(
|
||||
graphql`
|
||||
fragment ProfileQueryLocal on Local {
|
||||
assetID
|
||||
assetURL
|
||||
}
|
||||
`
|
||||
)(ProfileQuery);
|
||||
|
||||
export default enhanced;
|
||||
|
||||
@@ -12,6 +12,9 @@ general-userBoxAuthenticated-signedInAs =
|
||||
general-userBoxAuthenticated-notYou =
|
||||
Not you? <button>Sign Out</button>
|
||||
|
||||
general-app-commentsTab = Comments
|
||||
general-app-myProfileTab = My Profile
|
||||
|
||||
## Comments Tab
|
||||
|
||||
comments-streamQuery-assetNotFound = Asset not found
|
||||
@@ -75,4 +78,5 @@ profile-historyComment-viewConversation = View Conversation
|
||||
profile-historyComment-replies = Replies {$replyCount}
|
||||
profile-historyComment-commentHistory = Comment History
|
||||
profile-historyComment-story = Story: {$title}
|
||||
|
||||
profile-historyComment-errorLoadingProfile = Error loading profile
|
||||
profile-historyComment-assetNotFound = Asset not found
|
||||
|
||||
Reference in New Issue
Block a user