Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial data #106

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
"@trpc/server": "^10.43.6",
"@tryfabric/mack": "^1.2.1",
"class-variance-authority": "^0.7.0",
"client-only": "^0.0.1",
"cloudinary": "^1.41.0",
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"date-fns": "^2.30.0",
"gemoji": "^8.1.0",
"isomorphic-dompurify": "^1.10.0",
"jsdom": "^23.0.1",
"marked": "^11.0.0",
"match-sorter": "^6.3.1",
"next": "^14.0.3",
Expand Down
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 6 additions & 12 deletions src/app/(default)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import { type ReactNode } from 'react'
import { Header } from '../_components/header'
import { Footer } from '../_components/footer'
import { AuthProvider } from '../_providers/auth'
import { getServerAuthSession } from '~/server/auth'

export default async function DefaultLayout({
children,
}: {
children: ReactNode
}) {
const session = await getServerAuthSession()

return (
<AuthProvider session={session}>
<div className="max-w-3xl px-6 mx-auto">
<Header />
{children}
<div className="py-20">
<Footer />
</div>
<div className="max-w-3xl px-6 mx-auto">
<Header />
{children}
<div className="py-20">
<Footer />
</div>
</AuthProvider>
</div>
)
}
42 changes: 17 additions & 25 deletions src/app/(default)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { api } from '~/trpc/server'
import { PostSummary } from '../_components/post-summary'
import { getServerAuthSession } from '~/server/auth'
import { Pagination } from '../_components/pagination'
import { PostFeed } from '../_components/post-feed'
import { cache } from 'react'

const POSTS_PER_PAGE = 20

Expand All @@ -12,37 +11,30 @@ export default async function Index({
}) {
const currentPageNumber = searchParams.page ? Number(searchParams.page) : 1

const { posts, postCount } = await api.post.feed.query({
take: POSTS_PER_PAGE,
skip:
currentPageNumber === 1
? undefined
: POSTS_PER_PAGE * (currentPageNumber - 1),
const cachedData = cache(async () => {
return await api.post.feed.query({
take: POSTS_PER_PAGE,
skip:
currentPageNumber === 1
? undefined
: POSTS_PER_PAGE * (currentPageNumber - 1),
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How long does this query take? If it's slow, we should fix the query before caching it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's coming in about 490ms, so pretty long...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great info. I checked insights and this query looks like it's max taking 5-10ms. Which is slow for that query & data size, so can likely be optimized. But that's not the source of the 490ms.

Next places to look:

  • Where is the server vs db? Is it network latency?
  • How is the connection established? Is there anything in prisma in between?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I'll keep digging in, get it fixed up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beam's DB is located in us-east-1:

CleanShot 2024-01-17 at 12 54 07@2x

The Vercel functions are in iad1

CleanShot 2024-01-17 at 12 52 48@2x

Copy link
Contributor Author

@thejessewinton thejessewinton Jan 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ayrton @mscoutermarsh I've done a fair amount of work here to try and get this faster, and have also improved some of the UI using the initial data.

On the query latency, I'm not seeing anything happening on the Prisma end that would lead to higher latency like this; in some testing, caching the query calls seemed to have done quite a bit to improve load times across the board (176ms). Overall, the Prisma and data implementations are exactly the same as the old one, but in the 2.0 we're getting the data on the server rather than the client, and we're removing loading skeletons, which we had before. Since that would block the TTFB, I think it could be leading to us noticing a slower initial load. Any suggestions for getting this stronger are welcome, I'll try and get them implemented over the weekend.

})
const session = await getServerAuthSession()

const initialPostData = await cachedData()

return (
<>
{!postCount ? (
{!initialPostData.postCount ? (
<div className="text-center text-secondary border rounded py-20 px-10">
There are no published posts to show yet.
</div>
) : (
<div className="flow-root">
<ul className="-my-12 divide-y divide-primary">
{posts.map((post) => (
<li key={post.id} className="py-10">
<PostSummary session={session} post={post} />
</li>
))}
</ul>
</div>
<PostFeed
initialPosts={initialPostData}
postsPerPage={POSTS_PER_PAGE}
/>
)}
<Pagination
itemCount={postCount}
itemsPerPage={POSTS_PER_PAGE}
currentPageNumber={currentPageNumber}
/>
</>
)
}
21 changes: 15 additions & 6 deletions src/app/(default)/post/[id]/_components/edit-post-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { TextField } from '~/app/_components/text-field'
import { Button } from '~/app/_components/button'
import MarkdownIcon from '~/app/_svg/markdown-icon'
import { MarkdownEditor } from '~/app/_components/markdown-editor'
import { useRouter } from 'next/navigation'

import { api } from '~/trpc/react'
import { useRouter } from 'next/navigation'
import { type RouterOutputs } from '~/trpc/shared'

type FormData = {
title: string
Expand All @@ -17,25 +19,32 @@ type FormData = {

type PostFormProps = {
postId: number
defaultValues?: FormData
isSubmitting?: boolean
backTo: string
initialData: RouterOutputs['post']['detail']
}

export const EditPostForm = ({
postId,
defaultValues,
backTo,
initialData,
}: PostFormProps) => {
const { data } = api.post.detail.useQuery(
{
id: postId,
},
{ initialData },
)

const { control, register, handleSubmit } = useForm<FormData>({
defaultValues,
defaultValues: data,
})

// useLeaveConfirm({ formState })

const router = useRouter()

const editPostMutation = api.post.edit.useMutation({
onSuccess: () => {
onSuccess: async () => {
router.push(`/post/${postId}`)
},
})
Expand Down
26 changes: 14 additions & 12 deletions src/app/(default)/post/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getServerAuthSession } from '~/server/auth'
import { api } from '~/trpc/server'

import { EditPostForm } from '../_components/edit-post-form'
import { cache } from 'react'

type ProfilePageParams = {
params: {
Expand All @@ -10,10 +11,14 @@ type ProfilePageParams = {
}

export const generateMetadata = async ({ params }: ProfilePageParams) => {
const post = await api.post.detail.query({
id: Number(params.id),
const cachedPostData = cache(async () => {
return await api.post.detail.query({
id: Number(params.id),
})
})

const post = await cachedPostData()

if (!post) return

return {
Expand All @@ -22,10 +27,14 @@ export const generateMetadata = async ({ params }: ProfilePageParams) => {
}

export default async function EditPostPage({ params }: ProfilePageParams) {
const post = await api.post.detail.query({
id: Number(params.id),
const cachedPostData = cache(async () => {
return await api.post.detail.query({
id: Number(params.id),
})
})

const post = await cachedPostData()

const session = await getServerAuthSession()
const postBelongsToUser = post.author.id === session!.user.id

Expand All @@ -37,14 +46,7 @@ export default async function EditPostPage({ params }: ProfilePageParams) {
Edit &quot;{post.title}&quot;
</h1>
<div className="mt-6">
<EditPostForm
postId={post.id}
defaultValues={{
title: post.title,
content: post.content,
}}
backTo="/"
/>
<EditPostForm initialData={post} postId={post.id} backTo="/" />
</div>
</>
) : (
Expand Down
69 changes: 15 additions & 54 deletions src/app/(default)/post/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { AuthorWithDate } from '~/app/_components/author-with-date'
import { Avatar } from '~/app/_components/avatar'
import { Banner } from '~/app/_components/banner'
import { Button } from '~/app/_components/button'
import { Comment, AddCommentForm } from '~/app/_components/comment'

import { HtmlView } from '~/app/_components/html-view'
import { ReactionButton } from '~/app/_components/like-button'

import MessageIcon from '~/app/_svg/message-icon'
import { Comment, AddCommentForm } from '~/app/_components/comment'

import { getServerAuthSession } from '~/server/auth'
import { api } from '~/trpc/server'
import { PostAction } from './_components/post-action'
import { PostView } from '~/app/_components/post-view'
import { cache } from 'react'

type PostPageParams = {
params: {
Expand All @@ -20,10 +14,13 @@ type PostPageParams = {
}

export const generateMetadata = async ({ params }: PostPageParams) => {
const post = await api.post.detail.query({
id: Number(params.id),
const cachedPost = cache(async () => {
return await api.post.detail.query({
id: Number(params.id),
})
})

const post = await cachedPost()
if (!post) return

return {
Expand All @@ -32,55 +29,19 @@ export const generateMetadata = async ({ params }: PostPageParams) => {
}

export default async function PostPage({ params }: PostPageParams) {
const post = await api.post.detail.query({
id: Number(params.id),
const cachedPost = cache(async () => {
return await api.post.detail.query({
id: Number(params.id),
})
})

const post = await cachedPost()

const session = await getServerAuthSession()
const isUserAdmin = session!.user.role === 'ADMIN'
const postBelongsToUser = post.author.id === session!.user.id

return (
<article className="divide-y divide-primary">
<div className="pb-12">
{post.hidden && (
<Banner className="mb-6">
This post has been hidden and is only visible to administrators.
</Banner>
)}

<div className="flex items-center justify-between gap-4">
<h1 className="text-3xl font-semibold tracking-tighter md:text-4xl">
{post.title}
</h1>
{(postBelongsToUser || isUserAdmin) && (
<>
<PostAction
isHidden={post.hidden}
isUserAdmin={isUserAdmin}
postBelongsToUser={postBelongsToUser}
postId={post.id}
/>
</>
)}
</div>
<div className="mt-6">
<AuthorWithDate author={post.author} date={post.createdAt} />
</div>
<HtmlView html={post.contentHtml} className="mt-8" />
<div className="flex gap-4 mt-6">
<ReactionButton
likedBy={post.likedBy}
likeCount={post.likedBy.length}
isLikedByCurrentUser={post.isLikedByCurrentUser}
id={post.id}
/>
<Button href={`/post/${post.id}#comments`} variant="secondary">
<MessageIcon className="w-4 h-4 text-secondary" />
<span className="ml-1.5">{post.comments.length}</span>
</Button>
</div>
</div>
<PostView postId={params.id} initialPostData={post} />

<div id="comments" className="pt-12 space-y-12">
{post.comments.length > 0 && (
Expand Down
Loading
Loading