working comments with cache updates

This commit is contained in:
keyan 2021-04-17 13:15:18 -05:00
parent 95d6d789fa
commit 6792d1d5ff
10 changed files with 200 additions and 30 deletions

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "attach",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"port": 9229,
"outputCapture": "std"
}
]
}

View File

@ -24,7 +24,75 @@ const createItem = async (parent, { title, text, url, parentId }, { me, models }
} }
} }
return await models.item.create({ data }) const item = await models.item.create({ data })
item.comments = []
console.log(item)
return item
}
// function nestComments (flat, parentId) {
// const result = []
// for (let i = 0; i < flat.length; i++) {
// if (Number(flat[i].parentId) === Number(parentId)) {
// result.push(flat[i])
// } else if (result.length > 0) {
// // this comment is a child of the last one pushed
// const last = result[result.length - 1]
// last.comments = nestComments(flat.slice(i), last.id)
// // we consumed this many comments
// i += last.comments.length
// }
// }
// return result
// }
// function nestComments (flat, parentId) {
// const result = []
// let last
// for (let i = 0; i < flat.length; i++) {
// // initialize all comments
// if (!flat[i].comments) flat[i].comments = []
// if (Number(flat[i].parentId) === Number(parentId)) {
// result.push(flat[i])
// last = flat[i]
// } else if (last && Number(last.id) === flat[i].parentId) {
// const nested = nestComments(flat.slice(i), last.id)
// if (nested.length > 0) {
// last.comments.push(...nested)
// i += nested.length - 1
// }
// }
// }
// return result
// }
function nestComments (flat, parentId) {
const result = []
let added = 0
for (let i = 0; i < flat.length;) {
if (!flat[i].comments) flat[i].comments = []
if (Number(flat[i].parentId) === Number(parentId)) {
result.push(flat[i])
added++
i++
} else if (result.length > 0) {
const item = result[result.length - 1]
const [nested, newAdded] = nestComments(flat.slice(i), item.id)
if (newAdded === 0) {
break
}
item.comments.push(...nested)
i += newAdded
added += newAdded
} else {
break
}
}
return [result, added]
} }
export default { export default {
@ -44,7 +112,7 @@ export default {
WHERE id = ${id}`) WHERE id = ${id}`)
return res.length ? res[0] : null return res.length ? res[0] : null
}, },
comments: async (parent, { parentId }, { models }) => { flatcomments: async (parent, { parentId }, { models }) => {
return await models.$queryRaw(` return await models.$queryRaw(`
SELECT id, "created_at" as "createdAt", text, "parentId", SELECT id, "created_at" as "createdAt", text, "parentId",
"userId", nlevel(path)-1 AS depth, ltree2text("path") AS "path" "userId", nlevel(path)-1 AS depth, ltree2text("path") AS "path"
@ -52,6 +120,15 @@ export default {
WHERE path <@ (SELECT path FROM "Item" where id = ${parentId}) AND id != ${parentId} WHERE path <@ (SELECT path FROM "Item" where id = ${parentId}) AND id != ${parentId}
ORDER BY "path"`) ORDER BY "path"`)
}, },
comments: async (parent, { parentId }, { models }) => {
const flat = await models.$queryRaw(`
SELECT id, "created_at" as "createdAt", text, "parentId",
"userId", nlevel(path)-1 AS depth, ltree2text("path") AS "path"
FROM "Item"
WHERE path <@ (SELECT path FROM "Item" where id = ${parentId}) AND id != ${parentId}
ORDER BY "path"`)
return nestComments(flat, parentId)[0]
},
root: async (parent, { id }, { models }) => { root: async (parent, { id }, { models }) => {
const res = await models.$queryRaw(` const res = await models.$queryRaw(`
SELECT id, title SELECT id, title

View File

@ -5,6 +5,7 @@ export default gql`
items: [Item!]! items: [Item!]!
item(id: ID!): Item item(id: ID!): Item
comments(parentId: ID!): [Item!]! comments(parentId: ID!): [Item!]!
flatcomments(parentId: ID!): [Item!]!
root(id: ID!): Item root(id: ID!): Item
} }
@ -25,5 +26,6 @@ export default gql`
depth: Int! depth: Int!
sats: Int! sats: Int!
ncomments: Int! ncomments: Int!
comments: [Item!]!
} }
` `

View File

@ -95,13 +95,15 @@ export default function Comment ({ item, children, replyOpen, includeParent }) {
> >
{reply ? 'cancel' : 'reply'} {reply ? 'cancel' : 'reply'}
</div> </div>
{reply && <Reply parentId={item.id} />} {reply && <Reply item={item} />}
{children} {children}
{item.comments <div className={styles.comments}>
? item.comments.map((item) => ( {item.comments
<Comment key={item.id} item={item} /> ? item.comments.map((item) => (
)) <Comment key={item.id} item={item} />
: null} ))
: null}
</div>
</div> </div>
</> </>
) )

View File

@ -19,3 +19,8 @@
.children { .children {
margin-top: .4rem; margin-top: .4rem;
} }
.comments {
margin-left: 16px;
margin-top: .5rem;
}

View File

@ -1,19 +1,15 @@
import { useQuery, gql } from '@apollo/client' import { useQuery, gql } from '@apollo/client'
import Comment from './comment' import Comment from './comment'
import { COMMENTS } from '../fragments'
export default function Comments ({ parentId, baseDepth }) { export default function Comments ({ parentId }) {
const { data } = useQuery( const { data } = useQuery(
gql`{ gql`
${COMMENTS}
{
comments(parentId: ${parentId}) { comments(parentId: ${parentId}) {
id ...CommentsRecursive
createdAt
text
user {
name
}
depth
sats
ncomments
} }
}` }`
) )
@ -23,10 +19,7 @@ export default function Comments ({ parentId, baseDepth }) {
return ( return (
<div className='mt-5'> <div className='mt-5'>
{data.comments.map(item => ( {data.comments.map(item => (
<div <div key={item.id} className='mt-2'>
key={item.id} className='mt-2'
style={{ marginLeft: `${42 * (item.depth - baseDepth - 1)}px` }}
>
<Comment item={item} /> <Comment item={item} />
</div> </div>
))} ))}

View File

@ -2,19 +2,41 @@ import { Form, Input, SubmitButton } from '../components/form'
import * as Yup from 'yup' import * as Yup from 'yup'
import { gql, useMutation } from '@apollo/client' import { gql, useMutation } from '@apollo/client'
import styles from './reply.module.css' import styles from './reply.module.css'
import { COMMENTS } from '../fragments'
export const CommentSchema = Yup.object({ export const CommentSchema = Yup.object({
text: Yup.string().required('required').trim() text: Yup.string().required('required').trim()
}) })
export default function Reply ({ parentId }) { export default function Reply ({ item }) {
const parentId = item.id
const [createComment] = useMutation( const [createComment] = useMutation(
gql` gql`
${COMMENTS}
mutation createComment($text: String!, $parentId: ID!) { mutation createComment($text: String!, $parentId: ID!) {
createComment(text: $text, parentId: $parentId) { createComment(text: $text, parentId: $parentId) {
id ...CommentFields
comments {
...CommentsRecursive
}
} }
}` }`, {
update (cache, { data: { createComment } }) {
cache.modify({
id: `Item:${item.id}`,
fields: {
comments (existingCommentRefs = [], { readField }) {
const newCommentRef = cache.writeFragment({
data: createComment,
fragment: COMMENTS,
fragmentName: 'CommentsRecursive'
})
return [newCommentRef, ...existingCommentRefs]
}
}
})
}
}
) )
return ( return (

55
fragments/index.js Normal file
View File

@ -0,0 +1,55 @@
import { gql } from '@apollo/client'
export const COMMENT_FIELDS = gql`
fragment CommentFields on Item {
id
parentId
createdAt
text
user {
name
}
sats
ncomments
}
`
export const COMMENTS = gql`
${COMMENT_FIELDS}
fragment CommentsRecursive on Item {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
comments {
...CommentFields
}
}
}
}
}
}
}
}
}
}
}
}`

View File

@ -21,7 +21,6 @@ export async function getServerSideProps ({ params }) {
user { user {
name name
} }
depth
sats sats
ncomments ncomments
} }
@ -50,11 +49,11 @@ export default function FullItem ({ item }) {
<> <>
<Item item={item}> <Item item={item}>
{item.text && <Text>{item.text}</Text>} {item.text && <Text>{item.text}</Text>}
<Reply parentId={item.id} /> <Reply item={item} />
</Item> </Item>
</> </>
)} )}
<Comments parentId={item.id} baseDepth={item.depth} /> <Comments parentId={item.id} />
</Layout> </Layout>
) )
} }