diff --git a/api/resolvers/item.js b/api/resolvers/item.js
index 65b4fcff..005abedb 100644
--- a/api/resolvers/item.js
+++ b/api/resolvers/item.js
@@ -159,11 +159,35 @@ export default {
}
if (!parentId) {
- throw new UserInputError('comment must have parent', { argumentName: 'text' })
+ throw new UserInputError('comment must have parent', { argumentName: 'parentId' })
}
return await createItem(parent, { text, parentId }, { me, models })
},
+ updateComment: async (parent, { id, text }, { me, models }) => {
+ if (!text) {
+ throw new UserInputError('comment must have text', { argumentName: 'text' })
+ }
+
+ if (!id) {
+ throw new UserInputError('comment must have id', { argumentName: 'id' })
+ }
+
+ // update iff this comment belongs to me
+ const comment = await models.item.findUnique({ where: { id: Number(id) } })
+ if (Number(comment.userId) !== Number(me.id)) {
+ throw new AuthenticationError('comment must belong to you')
+ }
+
+ if (Date.now() > new Date(comment.createdAt).getTime() + 10 * 60000) {
+ throw new UserInputError('comment can no longer be editted')
+ }
+
+ return await models.item.update({
+ where: { id: Number(id) },
+ data: { text }
+ })
+ },
vote: async (parent, { id, sats = 1 }, { me, models }) => {
// need to make sure we are logged in
if (!me) {
diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js
index c5d924cf..08980f17 100644
--- a/api/typeDefs/item.js
+++ b/api/typeDefs/item.js
@@ -13,6 +13,7 @@ export default gql`
createLink(title: String!, url: String): Item!
createDiscussion(title: String!, text: String): Item!
createComment(text: String!, parentId: ID!): Item!
+ updateComment(id: ID!, text: String!): Item!
vote(id: ID!, sats: Int): Int!
}
diff --git a/components/comment-edit.js b/components/comment-edit.js
new file mode 100644
index 00000000..3bdc2b52
--- /dev/null
+++ b/components/comment-edit.js
@@ -0,0 +1,79 @@
+import { Form, MarkdownInput, SubmitButton } from '../components/form'
+import * as Yup from 'yup'
+import { gql, useMutation } from '@apollo/client'
+import styles from './reply.module.css'
+import TextareaAutosize from 'react-textarea-autosize'
+import Countdown from 'react-countdown'
+
+export const CommentSchema = Yup.object({
+ text: Yup.string().required('required').trim()
+})
+
+export default function CommentEdit ({ comment, editThreshold, onSuccess, onCancel }) {
+ const [updateComment] = useMutation(
+ gql`
+ mutation updateComment($id: ID! $text: String!) {
+ updateComment(id: $id, text: $text) {
+ text
+ }
+ }`, {
+ update (cache, { data: { updateComment } }) {
+ cache.modify({
+ id: `Item:${comment.id}`,
+ fields: {
+ text () {
+ return updateComment.text
+ }
+ }
+ })
+ }
+ }
+ )
+
+ return (
+
+ )
+}
diff --git a/components/comment.js b/components/comment.js
index 56b2fd91..21f1069d 100644
--- a/components/comment.js
+++ b/components/comment.js
@@ -9,6 +9,9 @@ import UpVote from './upvote'
import Eye from '../svgs/eye-fill.svg'
import EyeClose from '../svgs/eye-close-line.svg'
import { useRouter } from 'next/router'
+import { useMe } from './me'
+import CommentEdit from './comment-edit'
+import Countdown from 'react-countdown'
function Parent ({ item }) {
const ParentFrag = () => (
@@ -37,9 +40,15 @@ function Parent ({ item }) {
export default function Comment ({ item, children, replyOpen, includeParent, cacheId, noComments, noReply, clickToContext }) {
const [reply, setReply] = useState(replyOpen)
+ const [edit, setEdit] = useState()
const [collapse, setCollapse] = useState(false)
const ref = useRef(null)
const router = useRouter()
+ const me = useMe()
+ const mine = me.id === item.user.id
+ const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000
+ const [canEdit, setCanEdit] =
+ useState(mine && (Date.now() < editThreshold))
useEffect(() => {
if (Number(router.query.commentId) === Number(item.id)) {
@@ -81,23 +90,63 @@ export default function Comment ({ item, children, replyOpen, includeParent, cac
: setCollapse(true)} />)}
-
- {item.text}
-
+ {edit
+ ? (
+
+ {
+ setEdit(!edit)
+ setCanEdit(mine && (Date.now() < editThreshold))
+ }}
+ onCancel={() => {
+ setEdit(!edit)
+ setCanEdit(mine && (Date.now() < editThreshold))
+ }}
+ editThreshold={editThreshold}
+ />
+
+ )
+ : (
+
+ {item.text}
+
+ )}
- {!noReply &&
-
setReply(!reply)}
- >
- {reply ? 'cancel' : 'reply'}
-
}
+ {!noReply && !edit && (
+
+
setReply(!reply)}
+ >
+ {reply ? 'cancel' : 'reply'}
+
+ {canEdit && !reply && !edit &&
+ <>
+
\
+
setEdit(!edit)}
+ >
+ edit
+ {props.formatted.minutes}:{props.formatted.seconds}}
+ onComplete={() => {
+ setCanEdit(false)
+ }}
+ />
+
+ >}
+
+ )}
+
setReply(replyOpen || false)} cacheId={cacheId}
+ onSuccess={() => setReply(replyOpen || false)}
/>
{children}
diff --git a/components/comment.module.css b/components/comment.module.css
index aa9ed4f1..e04fd681 100644
--- a/components/comment.module.css
+++ b/components/comment.module.css
@@ -45,7 +45,8 @@
}
.children {
- margin-top: .25rem;
+ margin-top: 0;
+ padding-top: .25rem;
}
.comments {
diff --git a/components/form.js b/components/form.js
index 40210798..7879d125 100644
--- a/components/form.js
+++ b/components/form.js
@@ -89,8 +89,8 @@ export function MarkdownInput ({ label, groupClassName, ...props }) {
{...props}
/>
-
-
+
diff --git a/components/item.module.css b/components/item.module.css
index b3283cff..112b857f 100644
--- a/components/item.module.css
+++ b/components/item.module.css
@@ -53,7 +53,6 @@
.skeleton .other {
height: 17px;
align-items: center;
- display: flex;
}
.skeleton .title {
diff --git a/components/reply.js b/components/reply.js
index 93886d82..8ba87042 100644
--- a/components/reply.js
+++ b/components/reply.js
@@ -47,7 +47,7 @@ export default function Reply ({ parentId, onSuccess, autoFocus }) {
)
return (
-