diff --git a/package-lock.json b/package-lock.json index 6b20227b4..3f6204f4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2162,9 +2162,9 @@ } }, "@types/recompose": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/@types/recompose/-/recompose-0.26.1.tgz", - "integrity": "sha512-S5fkitL277yWCEHDzgb/3aJ4RySeqSC7L3Xo+7AlDk6+DAPpQAfF0iwgfjAqbP49JAjVCY+asPQxFPiw1+4CYg==", + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/@types/recompose/-/recompose-0.26.4.tgz", + "integrity": "sha512-QOLPlsBxn/yOxSv4Au66kd8KvYZRCgZA3vV5pNZ6YTEY4GeDHNoYL+sCnbzGIcmWDYoN7PUNZSopaGhvHQperw==", "dev": true, "requires": { "@types/react": "*" diff --git a/package.json b/package.json index 5acf90d14..c7eea6abb 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "@types/react-relay": "^1.3.9", "@types/react-responsive": "^3.0.1", "@types/react-test-renderer": "^16.0.1", - "@types/recompose": "^0.26.1", + "@types/recompose": "^0.26.4", "@types/relay-runtime": "^1.3.6", "@types/sane": "^2.0.0", "@types/sinon": "^5.0.1", diff --git a/src/core/client/framework/lib/bootstrap/withContext.tsx b/src/core/client/framework/lib/bootstrap/withContext.tsx index 677da236a..89a0da1d8 100644 --- a/src/core/client/framework/lib/bootstrap/withContext.tsx +++ b/src/core/client/framework/lib/bootstrap/withContext.tsx @@ -1,12 +1,18 @@ -import * as React from "react"; -import { - hoistStatics, - InferableComponentEnhancer, - wrapDisplayName, -} from "recompose"; +import React from "react"; +import { hoistStatics, wrapDisplayName } from "recompose"; +import { Omit } from "talk-ui/types"; import { TalkContext, TalkContextConsumer } from "./TalkContext"; +// Injects props and removes them from the prop requirements. +// Will not pass through the injected props if they are passed in during +// render. Also adds new prop requirements from TNeedsProps. +type InferableComponentEnhancerWithProps = < + P extends TInjectedProps +>( + component: React.ComponentType

+) => React.ComponentType>; + /** * withContext is a HOC wrapper around `TalkContextConsumer`. * `propsCallback` must be provided which accepts the `TalkContext` @@ -14,7 +20,7 @@ import { TalkContext, TalkContextConsumer } from "./TalkContext"; */ function withContext( propsCallback: (context: TalkContext) => T -): InferableComponentEnhancer { +): InferableComponentEnhancerWithProps { return hoistStatics( (WrappedComponent: React.ComponentType) => { const Component: React.StatelessComponent = props => ( @@ -27,7 +33,7 @@ function withContext( Component.displayName = wrapDisplayName(WrappedComponent, "withContext"); return Component; } - ); + ) as any; } export default withContext; diff --git a/src/core/client/stream/components/Comment/Comment.css b/src/core/client/stream/components/Comment/Comment.css index 821be5613..b996f5d55 100644 --- a/src/core/client/stream/components/Comment/Comment.css +++ b/src/core/client/stream/components/Comment/Comment.css @@ -1,5 +1,11 @@ .root { } -.footer { +.topBar { + margin-bottom: calc(0.5 * var(--spacing-unit)); +} +.footer:not(:empty) { + margin-top: var(--spacing-unit); +} +.reply { margin-top: var(--spacing-unit); } diff --git a/src/core/client/stream/components/Comment/Comment.tsx b/src/core/client/stream/components/Comment/Comment.tsx index 4cee7fe37..bd4fc17f7 100644 --- a/src/core/client/stream/components/Comment/Comment.tsx +++ b/src/core/client/stream/components/Comment/Comment.tsx @@ -1,8 +1,8 @@ -import React from "react"; -import { StatelessComponent } from "react"; -import * as styles from "./Comment.css"; +import React, { ReactElement, StatelessComponent } from "react"; -import PermalinkButtonContainer from "../../containers/PermalinkButtonContainer"; +import { Flex } from "talk-ui/components"; + +import * as styles from "./Comment.css"; import HTMLContent from "./HTMLContent"; import Timestamp from "./Timestamp"; import TopBar from "./TopBar"; @@ -16,20 +16,21 @@ export interface CommentProps { } | null; body: string | null; createdAt: string; + footer?: ReactElement | Array>; } const Comment: StatelessComponent = props => { return (

- + {props.author && props.author.username && {props.author.username}} {props.createdAt} {props.body || ""} -
- -
+ + {props.footer} +
); }; diff --git a/src/core/client/stream/components/Comment/ReplyButton.tsx b/src/core/client/stream/components/Comment/ReplyButton.tsx new file mode 100644 index 000000000..43b1028ca --- /dev/null +++ b/src/core/client/stream/components/Comment/ReplyButton.tsx @@ -0,0 +1,27 @@ +import { Localized } from "fluent-react/compat"; +import React, { EventHandler, MouseEvent, StatelessComponent } from "react"; + +import { Button, ButtonIcon, MatchMedia } from "talk-ui/components"; + +interface Props { + onClick?: EventHandler>; + active?: boolean; +} + +const ReplyButton: StatelessComponent = props => ( + +); + +export default ReplyButton; diff --git a/src/core/client/stream/components/Comment/TopBar.css b/src/core/client/stream/components/Comment/TopBar.css deleted file mode 100644 index dff9c8a74..000000000 --- a/src/core/client/stream/components/Comment/TopBar.css +++ /dev/null @@ -1,3 +0,0 @@ -.root { - margin-bottom: calc(0.5 * var(--spacing-unit)); -} diff --git a/src/core/client/stream/components/Comment/TopBar.tsx b/src/core/client/stream/components/Comment/TopBar.tsx index a82dd0a28..eed8aea32 100644 --- a/src/core/client/stream/components/Comment/TopBar.tsx +++ b/src/core/client/stream/components/Comment/TopBar.tsx @@ -4,15 +4,13 @@ import { StatelessComponent } from "react"; import { Flex, MatchMedia } from "talk-ui/components"; -import * as styles from "./TopBar.css"; - export interface TopBarProps { className?: string; children: React.ReactNode; } const TopBar: StatelessComponent = props => { - const rootClassName = cn(styles.root, props.className); + const rootClassName = cn(props.className); return ( {matches => ( diff --git a/src/core/client/stream/components/PermalinkView.tsx b/src/core/client/stream/components/PermalinkView.tsx index 1747152d7..38d2d29a0 100644 --- a/src/core/client/stream/components/PermalinkView.tsx +++ b/src/core/client/stream/components/PermalinkView.tsx @@ -8,7 +8,8 @@ import CommentContainer from "../containers/CommentContainer"; import * as styles from "./PermalinkView.css"; export interface PermalinkViewProps { - comment: PropTypesOf["data"] | null; + asset: PropTypesOf["asset"]; + comment: PropTypesOf["comment"] | null; showAllCommentsHref: string | null; onShowAllComments: (e: MouseEvent) => void; } @@ -16,6 +17,7 @@ export interface PermalinkViewProps { const PermalinkView: StatelessComponent = ({ showAllCommentsHref, comment, + asset, onShowAllComments, }) => { return ( @@ -42,7 +44,7 @@ const PermalinkView: StatelessComponent = ({ Comment not found )} - {comment && } + {comment && } ); }; diff --git a/src/core/client/stream/components/PostCommentForm.css b/src/core/client/stream/components/PostCommentForm.css index 8c35347ff..59669f339 100644 --- a/src/core/client/stream/components/PostCommentForm.css +++ b/src/core/client/stream/components/PostCommentForm.css @@ -1,17 +1,6 @@ .root { } -.textarea { - composes: bodyCopy from "talk-ui/shared/typography.css"; - display: block; - height: 150px; - width: 100%; - box-sizing: border-box; - padding: var(--spacing-unit); - border-radius: var(--round-corners); - resize: vertical; -} - .poweredBy { margin-top: calc(-0.5 * var(--spacing-unit)); } diff --git a/src/core/client/stream/components/ReplyCommentForm.tsx b/src/core/client/stream/components/ReplyCommentForm.tsx new file mode 100644 index 000000000..608d9a74d --- /dev/null +++ b/src/core/client/stream/components/ReplyCommentForm.tsx @@ -0,0 +1,99 @@ +import { FormState } from "final-form"; +import { Localized } from "fluent-react/compat"; +import React, { EventHandler, MouseEvent, StatelessComponent } from "react"; +import { Field, Form, FormSpy } from "react-final-form"; + +import { OnSubmit } from "talk-framework/lib/form"; +import { required } from "talk-framework/lib/validation"; +import { + AriaInfo, + Button, + Flex, + HorizontalGutter, + Typography, +} from "talk-ui/components"; + +import RTE from "./RTE"; + +interface FormProps { + body: string; +} + +export interface ReplyCommentFormProps { + className?: string; + onSubmit: OnSubmit; + onCancel?: EventHandler>; + onChange?: (state: FormState) => void; + initialValues?: FormProps; +} + +const ReplyCommentForm: StatelessComponent = props => ( +
+ {({ handleSubmit, submitting }) => ( + + + + + {({ input, meta }) => ( +
+ + + Write a reply + + + + input.onChange(html)} + value={input.value} + placeholder="Write a reply" + /> + + {meta.touched && + (meta.error || meta.submitError) && ( + + {meta.error || meta.submitError} + + )} +
+ )} +
+ + + + + + + + +
+ + )} + +); + +export default ReplyCommentForm; diff --git a/src/core/client/stream/components/ReplyList.tsx b/src/core/client/stream/components/ReplyList.tsx index cd2be4660..9e6897514 100644 --- a/src/core/client/stream/components/ReplyList.tsx +++ b/src/core/client/stream/components/ReplyList.tsx @@ -9,9 +9,12 @@ import CommentContainer from "../containers/CommentContainer"; import Indent from "./Indent"; export interface ReplyListProps { - commentID: string; + asset: PropTypesOf["asset"]; + comment: { + id: string; + }; comments: ReadonlyArray< - { id: string } & PropTypesOf["data"] + { id: string } & PropTypesOf["comment"] >; onShowAll: () => void; hasMore: boolean; @@ -22,17 +25,21 @@ const ReplyList: StatelessComponent = props => { return ( {props.comments.map(comment => ( - + ))} {props.hasMore && (