mostly clientside render
This commit is contained in:
parent
c626998952
commit
c82c82bb7b
|
@ -28,14 +28,6 @@ export default {
|
||||||
WHERE "userId" = ${userId} AND "parentId" IS NULL
|
WHERE "userId" = ${userId} AND "parentId" IS NULL
|
||||||
ORDER BY created_at`)
|
ORDER BY created_at`)
|
||||||
},
|
},
|
||||||
comments: async (parent, { parentId }, { models }) => {
|
|
||||||
const flat = await models.$queryRaw(`
|
|
||||||
${SELECT}
|
|
||||||
FROM "Item"
|
|
||||||
WHERE path <@ (SELECT path FROM "Item" where id = ${parentId}) AND id != ${parentId}
|
|
||||||
ORDER BY "path"`)
|
|
||||||
return nestComments(flat, parentId)[0]
|
|
||||||
},
|
|
||||||
userComments: async (parent, { userId }, { models }) => {
|
userComments: async (parent, { userId }, { models }) => {
|
||||||
return await models.$queryRaw(`
|
return await models.$queryRaw(`
|
||||||
${SELECT}
|
${SELECT}
|
||||||
|
@ -119,6 +111,14 @@ export default {
|
||||||
WHERE path <@ text2ltree(${item.path}) AND id != ${item.id}`
|
WHERE path <@ text2ltree(${item.path}) AND id != ${item.id}`
|
||||||
return count
|
return count
|
||||||
},
|
},
|
||||||
|
comments: async (item, args, { models }) => {
|
||||||
|
const flat = await models.$queryRaw(`
|
||||||
|
${SELECT}
|
||||||
|
FROM "Item"
|
||||||
|
WHERE path <@ (SELECT path FROM "Item" where id = ${item.id}) AND id != ${item.id}
|
||||||
|
ORDER BY "path"`)
|
||||||
|
return nestComments(flat, item.id)[0]
|
||||||
|
},
|
||||||
sats: async (item, args, { models }) => {
|
sats: async (item, args, { models }) => {
|
||||||
const { sum: { sats } } = await models.vote.aggregate({
|
const { sum: { sats } } = await models.vote.aggregate({
|
||||||
sum: {
|
sum: {
|
||||||
|
@ -158,11 +158,6 @@ const createItem = async (parent, { title, text, url, parentId }, { me, models }
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
text,
|
text,
|
||||||
item: {
|
|
||||||
connect: {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: {
|
user: {
|
||||||
connect: {
|
connect: {
|
||||||
name: me.name
|
name: me.name
|
||||||
|
|
|
@ -6,7 +6,6 @@ export default gql`
|
||||||
recent: [Item!]!
|
recent: [Item!]!
|
||||||
item(id: ID!): Item
|
item(id: ID!): Item
|
||||||
userItems(userId: ID!): [Item!]
|
userItems(userId: ID!): [Item!]
|
||||||
comments(parentId: ID!): [Item!]!
|
|
||||||
userComments(userId: ID!): [Item!]
|
userComments(userId: ID!): [Item!]
|
||||||
root(id: ID!): Item
|
root(id: ID!): Item
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ function Parent ({ item }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Comment ({ item, children, replyOpen, includeParent, cacheId, noReply }) {
|
export default function Comment ({ item, children, replyOpen, includeParent, cacheId, noComments, noReply }) {
|
||||||
const [reply, setReply] = useState(replyOpen)
|
const [reply, setReply] = useState(replyOpen)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -82,7 +82,7 @@ export default function Comment ({ item, children, replyOpen, includeParent, cac
|
||||||
{reply && <Reply parentId={item.id} onSuccess={() => setReply(replyOpen || false)} cacheId={cacheId} />}
|
{reply && <Reply parentId={item.id} onSuccess={() => setReply(replyOpen || false)} cacheId={cacheId} />}
|
||||||
{children}
|
{children}
|
||||||
<div className={styles.comments}>
|
<div className={styles.comments}>
|
||||||
{item.comments
|
{item.comments && !noComments
|
||||||
? item.comments.map((item) => (
|
? item.comments.map((item) => (
|
||||||
<Comment key={item.id} item={item} />
|
<Comment key={item.id} item={item} />
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import Comment, { CommentSkeleton } from './comment'
|
import Comment, { CommentSkeleton } from './comment'
|
||||||
|
|
||||||
export default function Comments ({ query, ...props }) {
|
export default function Comments ({ comments, ...props }) {
|
||||||
const { loading, error, data } = useQuery(query)
|
return comments.map(item => (
|
||||||
|
<div key={item.id} className='mt-2'>
|
||||||
|
<Comment item={item} {...props} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if (error) return <div>Failed to load!</div>
|
export function CommentsSkeleton () {
|
||||||
if (loading) {
|
|
||||||
const comments = new Array(3).fill(null)
|
const comments = new Array(3).fill(null)
|
||||||
|
|
||||||
return comments.map((_, i) => (
|
return comments.map((_, i) => (
|
||||||
|
@ -15,9 +19,13 @@ export default function Comments ({ query, ...props }) {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.comments.map(item => (
|
export function CommentsQuery ({ query, ...props }) {
|
||||||
<div key={item.id} className='mt-2'>
|
const { loading, error, data } = useQuery(query)
|
||||||
<Comment item={item} {...props} />
|
|
||||||
</div>
|
if (error) return <div>Failed to load!</div>
|
||||||
))
|
if (loading) {
|
||||||
|
return <CommentsSkeleton />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Comments comments={data.comments} {...props} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default function Item ({ item, rank, children }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ItemSkeleton ({ rank }) {
|
export function ItemSkeleton ({ rank, children }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{rank &&
|
{rank &&
|
||||||
|
@ -66,6 +66,11 @@ export function ItemSkeleton ({ rank }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{children && (
|
||||||
|
<div className={styles.children}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const CommentSchema = Yup.object({
|
||||||
text: Yup.string().required('required').trim()
|
text: Yup.string().required('required').trim()
|
||||||
})
|
})
|
||||||
|
|
||||||
export default function Reply ({ parentId, onSuccess, cacheId }) {
|
export default function Reply ({ parentId, onSuccess }) {
|
||||||
const [createComment] = useMutation(
|
const [createComment] = useMutation(
|
||||||
gql`
|
gql`
|
||||||
${COMMENTS}
|
${COMMENTS}
|
||||||
|
@ -22,7 +22,7 @@ export default function Reply ({ parentId, onSuccess, cacheId }) {
|
||||||
}`, {
|
}`, {
|
||||||
update (cache, { data: { createComment } }) {
|
update (cache, { data: { createComment } }) {
|
||||||
cache.modify({
|
cache.modify({
|
||||||
id: cacheId || `Item:${parentId}`,
|
id: `Item:${parentId}`,
|
||||||
fields: {
|
fields: {
|
||||||
comments (existingCommentRefs = []) {
|
comments (existingCommentRefs = []) {
|
||||||
const newCommentRef = cache.writeFragment({
|
const newCommentRef = cache.writeFragment({
|
||||||
|
@ -67,3 +67,12 @@ export default function Reply ({ parentId, onSuccess, cacheId }) {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ReplySkeleton () {
|
||||||
|
return (
|
||||||
|
<div className={`${styles.reply} ${styles.skeleton}`}>
|
||||||
|
<div className={`${styles.input} clouds`} />
|
||||||
|
<div className={`${styles.button} clouds`} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -2,3 +2,19 @@
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton .input {
|
||||||
|
background-color: grey;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: .4rem;
|
||||||
|
height: 115px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton .button {
|
||||||
|
background-color: grey;
|
||||||
|
border-radius: .4rem;
|
||||||
|
margin-top: .25rem;
|
||||||
|
width: 71px;
|
||||||
|
height: 38px;
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
import Layout from '../../components/layout'
|
import Layout from '../../components/layout'
|
||||||
import Comments from '../../components/comments'
|
import { CommentsQuery } from '../../components/comments'
|
||||||
import { COMMENT_FIELDS } from '../../fragments/comments'
|
import { COMMENT_FIELDS } from '../../fragments/comments'
|
||||||
import { gql } from '@apollo/client'
|
import { gql } from '@apollo/client'
|
||||||
import ApolloClient from '../../api/client'
|
import ApolloClient from '../../api/client'
|
||||||
import UserHeader from '../../components/user-header'
|
import UserHeader from '../../components/user-header'
|
||||||
|
|
||||||
export async function getServerSideProps ({ params }) {
|
export async function getServerSideProps ({ req, params }) {
|
||||||
const { error, data: { user } } = await ApolloClient.query({
|
const { error, data: { user } } = await (await ApolloClient(req)).query({
|
||||||
query:
|
query:
|
||||||
gql`{
|
gql`{
|
||||||
user(name: "${params.username}") {
|
user(name: "${params.username}") {
|
||||||
|
@ -46,7 +46,7 @@ export default function User ({ user }) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<UserHeader user={user} />
|
<UserHeader user={user} />
|
||||||
<Comments query={query} includeParent noReply />
|
<CommentsQuery query={query} includeParent noReply />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,72 @@
|
||||||
import Item from '../../components/item'
|
import Item, { ItemSkeleton } from '../../components/item'
|
||||||
import Layout from '../../components/layout'
|
import Layout from '../../components/layout'
|
||||||
import ApolloClient from '../../api/client'
|
import Reply, { ReplySkeleton } 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'
|
import Comments, { CommentsSkeleton } from '../../components/comments'
|
||||||
import { COMMENTS } from '../../fragments/comments'
|
import { COMMENTS } from '../../fragments/comments'
|
||||||
import { ITEM_FIELDS } from '../../fragments/items'
|
import { ITEM_FIELDS } from '../../fragments/items'
|
||||||
import { gql } from '@apollo/client'
|
import { gql, useQuery } from '@apollo/client'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
export async function getServerSideProps ({ req, params }) {
|
|
||||||
const { error, data: { item } } = await (await ApolloClient(req)).query({
|
|
||||||
query:
|
|
||||||
gql`
|
|
||||||
${ITEM_FIELDS}
|
|
||||||
{
|
|
||||||
item(id: ${params.id}) {
|
|
||||||
...ItemFields
|
|
||||||
text
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!item || error) {
|
|
||||||
return {
|
|
||||||
notFound: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
item: item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function FullItem ({ item }) {
|
export default function FullItem ({ item }) {
|
||||||
const commentsQuery = gql`
|
const router = useRouter()
|
||||||
|
const { id } = router.query
|
||||||
|
|
||||||
|
const query = gql`
|
||||||
|
${ITEM_FIELDS}
|
||||||
${COMMENTS}
|
${COMMENTS}
|
||||||
{
|
{
|
||||||
comments(parentId: ${item.id}) {
|
item(id: ${id}) {
|
||||||
|
...ItemFields
|
||||||
|
text
|
||||||
|
comments {
|
||||||
...CommentsRecursive
|
...CommentsRecursive
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
<LoadItem query={query} />
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadItem ({ query }) {
|
||||||
|
const { loading, error, data } = useQuery(query)
|
||||||
|
if (error) return <div>Failed to load!</div>
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ItemSkeleton>
|
||||||
|
<ReplySkeleton />
|
||||||
|
</ItemSkeleton>
|
||||||
|
<div className='mt-5'>
|
||||||
|
<CommentsSkeleton />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { item } = data
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
{item.parentId
|
{item.parentId
|
||||||
? <Comment item={item} replyOpen includeParent cacheId='ROOT_QUERY' />
|
? <Comment item={item} replyOpen includeParent noComments />
|
||||||
: (
|
: (
|
||||||
<>
|
<>
|
||||||
<Item item={item}>
|
<Item item={item}>
|
||||||
{item.text && <Text>{item.text}</Text>}
|
{item.text && <Text>{item.text}</Text>}
|
||||||
<Reply parentId={item.id} cacheId='ROOT_QUERY' />
|
<Reply parentId={item.id} />
|
||||||
</Item>
|
</Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className='mt-5'>
|
<div className='mt-5'>
|
||||||
<Comments query={commentsQuery} />
|
<Comments comments={item.comments} />
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,12 +82,12 @@ $container-max-widths: (
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes flash {
|
@keyframes flash {
|
||||||
from { filter: brightness(1); background-position: 0 0;}
|
from { filter: brightness(1);}
|
||||||
2% { filter: brightness(2.3); }
|
2% { filter: brightness(2.3); }
|
||||||
4% { filter: brightness(1.4); }
|
4% { filter: brightness(1.4); }
|
||||||
8% { filter: brightness(3); }
|
8% { filter: brightness(3); }
|
||||||
16% { filter: brightness(1); }
|
16% { filter: brightness(1); }
|
||||||
to { filter: brightness(1); background-position: 250px 0;}
|
to { filter: brightness(1);}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clouds {
|
.clouds {
|
||||||
|
|
Loading…
Reference in New Issue