diff --git a/src/core/client/framework/lib/bootstrap/createContext.tsx b/src/core/client/framework/lib/bootstrap/createContext.tsx
index 839cee453..d11b3b83c 100644
--- a/src/core/client/framework/lib/bootstrap/createContext.tsx
+++ b/src/core/client/framework/lib/bootstrap/createContext.tsx
@@ -47,7 +47,16 @@ interface CreateContextArguments {
*/
export const timeagoFormatter: Formatter = (value, unit, suffix) => {
// We use 'in' instead of 'from now' for language consistency
- const ourSuffix = suffix === "from now" ? "in" : suffix;
+ const ourSuffix = suffix === "from now" ? "noSuffix" : suffix;
+
+ if (unit === "second" && suffix === "ago") {
+ return (
+
+ Just now
+
+ );
+ }
+
return (
| Array>;
footer?: ReactElement | Array>;
}
const Comment: StatelessComponent = props => {
return (
-
- {props.author &&
- props.author.username && {props.author.username}}
- {props.createdAt}
-
+
+
+ {props.author &&
+ props.author.username && (
+ {props.author.username}
+ )}
+ {props.createdAt}
+
+ {props.topBarRight}
+
{props.body || ""}
{props.footer}
diff --git a/src/core/client/stream/components/Comment/TopBar.spec.tsx b/src/core/client/stream/components/Comment/TopBarLeft.spec.tsx
similarity index 81%
rename from src/core/client/stream/components/Comment/TopBar.spec.tsx
rename to src/core/client/stream/components/Comment/TopBarLeft.spec.tsx
index 5ee691f70..8769bb3dd 100644
--- a/src/core/client/stream/components/Comment/TopBar.spec.tsx
+++ b/src/core/client/stream/components/Comment/TopBarLeft.spec.tsx
@@ -4,10 +4,10 @@ import TestRenderer from "react-test-renderer";
import { PropTypesOf } from "talk-framework/types";
import { UIContext, UIContextProps } from "talk-ui/components";
-import TopBar from "./TopBar";
+import TopBarLeft from "./TopBarLeft";
it("renders correctly on small screens", () => {
- const props: PropTypesOf = {
+ const props: PropTypesOf = {
children: Hello World
,
};
@@ -19,14 +19,14 @@ it("renders correctly on small screens", () => {
const testRenderer = TestRenderer.create(
-
+
);
expect(testRenderer.toJSON()).toMatchSnapshot();
});
it("renders correctly on big screens", () => {
- const props: PropTypesOf = {
+ const props: PropTypesOf = {
children: Hello World
,
};
@@ -38,7 +38,7 @@ it("renders correctly on big screens", () => {
const testRenderer = TestRenderer.create(
-
+
);
expect(testRenderer.toJSON()).toMatchSnapshot();
diff --git a/src/core/client/stream/components/Comment/TopBar.tsx b/src/core/client/stream/components/Comment/TopBarLeft.tsx
similarity index 81%
rename from src/core/client/stream/components/Comment/TopBar.tsx
rename to src/core/client/stream/components/Comment/TopBarLeft.tsx
index eed8aea32..5f3862020 100644
--- a/src/core/client/stream/components/Comment/TopBar.tsx
+++ b/src/core/client/stream/components/Comment/TopBarLeft.tsx
@@ -4,12 +4,12 @@ import { StatelessComponent } from "react";
import { Flex, MatchMedia } from "talk-ui/components";
-export interface TopBarProps {
+export interface TopBarLeftProps {
className?: string;
children: React.ReactNode;
}
-const TopBar: StatelessComponent = props => {
+const TopBarLeft: StatelessComponent = props => {
const rootClassName = cn(props.className);
return (
@@ -27,4 +27,4 @@ const TopBar: StatelessComponent = props => {
);
};
-export default TopBar;
+export default TopBarLeft;
diff --git a/src/core/client/stream/components/Comment/index.ts b/src/core/client/stream/components/Comment/index.ts
index abb1d79ae..56c2da651 100644
--- a/src/core/client/stream/components/Comment/index.ts
+++ b/src/core/client/stream/components/Comment/index.ts
@@ -1 +1,4 @@
export { default, default as IndentedComment } from "./IndentedComment";
+export { default as TopBarLeft } from "./TopBarLeft";
+export { default as Username } from "./Username";
+export { default as Timestamp } from "./Timestamp";
diff --git a/src/core/client/stream/components/EditCommentForm.tsx b/src/core/client/stream/components/EditCommentForm.tsx
new file mode 100644
index 000000000..84c753b1b
--- /dev/null
+++ b/src/core/client/stream/components/EditCommentForm.tsx
@@ -0,0 +1,156 @@
+import { CoralRTE } from "@coralproject/rte";
+import { Localized } from "fluent-react/compat";
+import React, {
+ EventHandler,
+ MouseEvent,
+ Ref,
+ StatelessComponent,
+} from "react";
+import { Field, Form } from "react-final-form";
+
+import { OnSubmit } from "talk-framework/lib/form";
+import { required } from "talk-framework/lib/validation";
+import {
+ AriaInfo,
+ Button,
+ Flex,
+ HorizontalGutter,
+ RelativeTime,
+ Typography,
+ ValidationMessage,
+} from "talk-ui/components";
+
+import { Timestamp, TopBarLeft, Username } from "./Comment";
+import RTE from "./RTE";
+
+interface FormProps {
+ body: string;
+}
+
+export interface EditCommentFormProps {
+ id: string;
+ className?: string;
+ author: {
+ username: string | null;
+ } | null;
+ createdAt: string;
+ editableUntil: string;
+ onSubmit: OnSubmit;
+ onCancel?: EventHandler>;
+ onClose?: EventHandler>;
+ initialValues?: FormProps;
+ rteRef?: Ref;
+ expired?: boolean;
+}
+
+const EditCommentForm: StatelessComponent = props => {
+ const inputID = `comments-editCommentForm-rte-${props.id}`;
+ return (
+
+ )}
+
+ );
+};
+
+export default EditCommentForm;
diff --git a/src/core/client/stream/containers/CommentContainer.tsx b/src/core/client/stream/containers/CommentContainer.tsx
index 21b4b767e..c3368b9d0 100644
--- a/src/core/client/stream/containers/CommentContainer.tsx
+++ b/src/core/client/stream/containers/CommentContainer.tsx
@@ -1,6 +1,8 @@
+import { Localized } from "fluent-react/compat";
import React, { Component } from "react";
import { graphql } from "react-relay";
+import { isBeforeDate } from "talk-common/utils";
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";
@@ -11,10 +13,12 @@ import {
withShowAuthPopupMutation,
} from "talk-stream/mutations";
+import { Button } from "talk-ui/components";
import Comment from "../components/Comment";
import ReplyButton from "../components/Comment/ReplyButton";
-import ReplyCommentFormContainer from ".//ReplyCommentFormContainer";
+import EditCommentFormContainer from "./EditCommentFormContainer";
import PermalinkButtonContainer from "./PermalinkButtonContainer";
+import ReplyCommentFormContainer from "./ReplyCommentFormContainer";
interface InnerProps {
me: MeData | null;
@@ -26,13 +30,28 @@ interface InnerProps {
interface State {
showReplyDialog: boolean;
+ showEditDialog: boolean;
+ editable: boolean;
}
export class CommentContainer extends Component {
+ private uneditableTimer: any;
+
public state = {
showReplyDialog: false,
+ showEditDialog: false,
+ editable: isBeforeDate(this.props.comment.editing.editableUntil),
};
+ constructor(props: InnerProps) {
+ super(props);
+ this.uneditableTimer = this.updateWhenNotEditable();
+ }
+
+ public componentWillUnmount() {
+ clearTimeout(this.uneditableTimer);
+ }
+
private openReplyDialog = () => {
if (this.props.me) {
this.setState(state => ({
@@ -43,20 +62,68 @@ export class CommentContainer extends Component {
}
};
+ private openEditDialog = () => {
+ if (this.props.me) {
+ this.setState(state => ({
+ showEditDialog: true,
+ }));
+ } else {
+ this.props.showAuthPopup({ view: "SIGN_IN" });
+ }
+ };
+
+ private closeEditDialog = () => {
+ this.setState(state => ({
+ showEditDialog: false,
+ }));
+ };
+
private closeReplyDialog = () => {
this.setState(state => ({
showReplyDialog: false,
}));
};
+ private updateWhenNotEditable() {
+ const ms =
+ new Date(this.props.comment.editing.editableUntil).getTime() - Date.now();
+ if (ms > 0) {
+ return setTimeout(() => this.setState({ editable: false }), ms);
+ }
+ return;
+ }
+
public render() {
const { comment, asset, ...rest } = this.props;
- const { showReplyDialog } = this.state;
+ const { showReplyDialog, showEditDialog, editable } = this.state;
+ if (showEditDialog) {
+ return (
+
+ );
+ }
return (
<>