pre recursive comments

This commit is contained in:
keyan 2021-04-15 14:41:02 -05:00
parent 28ed42fc29
commit 95d6d789fa
6 changed files with 103 additions and 35 deletions

View File

@ -31,24 +31,33 @@ export default {
Query: { Query: {
items: async (parent, args, { models }) => { items: async (parent, args, { models }) => {
return await models.$queryRaw(` return await models.$queryRaw(`
SELECT id, "created_at" as "createdAt", title, url, text, "userId", ltree2text("path") AS "path" SELECT id, "created_at" as "createdAt", title, url, text,
"userId", nlevel(path)-1 AS depth, ltree2text("path") AS "path"
FROM "Item" FROM "Item"
WHERE "parentId" IS NULL WHERE "parentId" IS NULL`)
ORDER BY "path"`)
}, },
item: async (parent, { id }, { models }) => { item: async (parent, { id }, { models }) => {
const res = await models.$queryRaw(` const res = await models.$queryRaw(`
SELECT id, "created_at" as "createdAt", title, url, text, "parentId", "userId", ltree2text("path") AS "path" SELECT id, "created_at" as "createdAt", title, url, text,
"parentId", "userId", nlevel(path)-1 AS depth, ltree2text("path") AS "path"
FROM "Item" FROM "Item"
WHERE id = ${id} WHERE id = ${id}`)
ORDER BY "path"`)
return res.length ? res[0] : null return res.length ? res[0] : null
}, },
ncomments: async (parent, { parentId }, { models }) => { comments: async (parent, { parentId }, { models }) => {
return await models.$queryRaw(` return await models.$queryRaw(`
SELECT id, "created_at" as "createdAt", title, url, text, "userId", ltree2text("path") AS "path" SELECT id, "created_at" as "createdAt", text, "parentId",
"userId", nlevel(path)-1 AS depth, ltree2text("path") AS "path"
FROM "Item" FROM "Item"
WHERE path <@ (SELECT path FROM "Item" where id = ${parentId}) AND id != ${parentId}
ORDER BY "path"`) ORDER BY "path"`)
},
root: async (parent, { id }, { models }) => {
const res = await models.$queryRaw(`
SELECT id, title
FROM "Item"
WHERE id = (SELECT ltree2text(subltree(path, 0, 1))::integer FROM "Item" WHERE id = ${id})`)
return res.length ? res[0] : null
} }
}, },
@ -87,24 +96,11 @@ export default {
Item: { Item: {
user: async (item, args, { models }) => user: async (item, args, { models }) =>
await models.user.findUnique({ where: { id: item.userId } }), await models.user.findUnique({ where: { id: item.userId } }),
depth: async (item, args, { models }) => {
if (item.path) {
return item.path.split('.').length - 1
}
// as the result of a mutation, path is not populated
const [{ path }] = await models.$queryRaw`
SELECT ltree2text("path") AS "path"
FROM "Item"
WHERE id = ${item.id}`
return path.split('.').length - 1
},
ncomments: async (item, args, { models }) => { ncomments: async (item, args, { models }) => {
const [{ count }] = await models.$queryRaw` const [{ count }] = await models.$queryRaw`
SELECT count(*) SELECT count(*)
FROM "Item" FROM "Item"
WHERE path <@ text2ltree(${item.id}) AND id != ${item.id}` WHERE path <@ text2ltree(${item.path}) AND id != ${item.id}`
return count return count
}, },
sats: () => 0 sats: () => 0

View File

@ -4,7 +4,8 @@ export default gql`
extend type Query { extend type Query {
items: [Item!]! items: [Item!]!
item(id: ID!): Item item(id: ID!): Item
ncomments(id: ID!): [Item!]! comments(parentId: ID!): [Item!]!
root(id: ID!): Item
} }
extend type Mutation { extend type Mutation {

View File

@ -5,6 +5,7 @@ import Text from './text'
import Link from 'next/link' import Link from 'next/link'
import Reply from './reply' import Reply from './reply'
import { useState } from 'react' import { useState } from 'react'
import { gql, useQuery } from '@apollo/client'
function timeSince (timeStamp) { function timeSince (timeStamp) {
const now = new Date() const now = new Date()
@ -26,8 +27,42 @@ function timeSince (timeStamp) {
} }
} }
export default function Comment ({ item, children }) { function Parent ({ item }) {
const [reply, setReply] = useState(false) const { data } = useQuery(
gql`{
root(id: ${item.id}) {
id
title
}
}`
)
const ParentFrag = () => (
<>
<span> \ </span>
<Link href={`/items/${item.parentId}`} passHref>
<a className='text-reset'>parent</a>
</Link>
</>
)
if (!data) {
return <ParentFrag />
}
return (
<>
{data.root.id !== item.parentId && <ParentFrag />}
<span> \ </span>
<Link href={`/items/${data.root.id}`} passHref>
<a className='text-reset'>{data.root.title}</a>
</Link>
</>
)
}
export default function Comment ({ item, children, replyOpen, includeParent }) {
const [reply, setReply] = useState(replyOpen)
return ( return (
<> <>
@ -43,7 +78,10 @@ export default function Comment ({ item, children }) {
<span> \ </span> <span> \ </span>
<span>{item.sats} sats</span> <span>{item.sats} sats</span>
<span> \ </span> <span> \ </span>
<span>{item.ncomments} replies</span> <Link href={`/items/${item.id}`} passHref>
<a className='text-reset'>{item.ncomments} replies</a>
</Link>
{includeParent && <Parent item={item} />}
</div> </div>
<div className={styles.text}> <div className={styles.text}>
<Text>{item.text}</Text> <Text>{item.text}</Text>

35
components/comments.js Normal file
View File

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

View File

@ -37,7 +37,9 @@ export default function Item ({ item, children }) {
<div className={styles.other}> <div className={styles.other}>
<span>{item.sats} sats</span> <span>{item.sats} sats</span>
<span> \ </span> <span> \ </span>
<span>{item.ncomments} comments</span> <Link href={`/items/${item.id}`} passHref>
<a className='text-reset'>{item.ncomments} comments</a>
</Link>
<span> \ </span> <span> \ </span>
<Link href={`/@${item.user.name}`} passHref> <Link href={`/@${item.user.name}`} passHref>
<a>@{item.user.name}</a> <a>@{item.user.name}</a>
@ -50,11 +52,6 @@ export default function Item ({ item, children }) {
{children && ( {children && (
<div className={styles.children}> <div className={styles.children}>
{children} {children}
{item.comments
? item.comments.map((item) => (
<Item key={item.id} item={item} />
))
: null}
</div> </div>
)} )}
</> </>

View File

@ -5,6 +5,7 @@ import ApolloClient from '../../api/client'
import Reply from '../../components/reply' import Reply from '../../components/reply'
import Comment from '../../components/comment' import Comment from '../../components/comment'
import Text from '../../components/text' import Text from '../../components/text'
import Comments from '../../components/comments'
export async function getServerSideProps ({ params }) { export async function getServerSideProps ({ params }) {
const { error, data: { item } } = await ApolloClient.query({ const { error, data: { item } } = await ApolloClient.query({
@ -20,6 +21,7 @@ export async function getServerSideProps ({ params }) {
user { user {
name name
} }
depth
sats sats
ncomments ncomments
} }
@ -43,9 +45,7 @@ export default function FullItem ({ item }) {
return ( return (
<Layout> <Layout>
{item.parentId {item.parentId
? ( ? <Comment item={item} replyOpen includeParent />
<Comment item={item} />
)
: ( : (
<> <>
<Item item={item}> <Item item={item}>
@ -54,6 +54,7 @@ export default function FullItem ({ item }) {
</Item> </Item>
</> </>
)} )}
<Comments parentId={item.id} baseDepth={item.depth} />
</Layout> </Layout>
) )
} }