Skip to content

Commit

Permalink
feat: post details page
Browse files Browse the repository at this point in the history
  • Loading branch information
warmachine028 committed Nov 9, 2024
1 parent e3a3031 commit b8fe3c7
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 12 deletions.
2 changes: 1 addition & 1 deletion client/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ model Post {
id String @id @default(cuid())
content String
authorId String
user User @relation(fields: [authorId], references: [id], onDelete: Cascade)
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
attachments Media[]
createdAt DateTime @default(now())
Expand Down
108 changes: 108 additions & 0 deletions client/src/app/(main)/posts/[postId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { validateRequest } from '@/auth'
import { prisma } from '@/lib'
import { getPostDataInclude, UserData } from '@/types'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
import { cache, Suspense } from 'react'
import { Avatar, FollowButton, Linkify, Menubar, Post } from '@/components'
import { UserTooltip } from '@/components/users'
import Link from 'next/link'
import { Loader2 } from 'lucide-react'

interface PageProps {
params: Promise<{ postId: string }>
}

const getPost = cache(async (postId: string, loggedInUserId: string) => {
const post = await prisma.post.findUnique({
where: {
id: postId
},
include: getPostDataInclude(loggedInUserId)
})

if (!post) {
notFound()
}
return post
})

export const generateMetadata = async ({ params }: PageProps): Promise<Metadata> => {
const { postId } = await params
const { user: currentUser } = await validateRequest()

if (!currentUser) {
return {}
}

const post = await getPost(postId, currentUser.id)

return {
title: `${post.author.userName}: ${post.content.slice(0, 50)}...`
}
}

const PostPage = async ({ params }: PageProps) => {
const { postId } = await params
const { user } = await validateRequest()

if (!user) {
return <p className="text-destructive">You&apos;re not authorized to view this page.</p>
}
const post = await getPost(postId, user.id)

return (
<main className="container mx-auto flex min-h-[calc(100vh-125px)] w-full grow gap-5 p-5">
<Menubar className="sticky top-[5.25rem] hidden h-fit flex-none space-y-3 bg-card px-3 py-5 shadow-sm sm:block lg:px-5 xl:w-80" />
<div className="w-full min-w-0 space-y-5">
<Post post={post} />
</div>
<div className="sticky top-[5.25rem] hidden h-fit w-80 flex-none lg:block">
<Suspense fallback={<Loader2 className="mx-auto animate-spin" />}>
<AuthorInfoSidebar user={post.author} />
</Suspense>
</div>
</main>
)
}

interface AuthorInfoSidebarProps {
user: UserData
}

const AuthorInfoSidebar = async ({ user }: AuthorInfoSidebarProps) => {
const { user: currentUser } = await validateRequest()
// await new Promise((resolve) => setTimeout(resolve, 5000))
if (!currentUser) {
return null
}

return (
<div className="space-y-5 rounded-xl bg-card p-5 shadow-sm">
<div className="text-xl font-bold">About the author</div>
<UserTooltip user={user}>
<Link href={`/users/${user.userName}`} className="flex items-center gap-3">
<Avatar url={user.avatarUrl} className="flex-none" />
<div className="">
<p className="line-clamp-1 break-all font-semibold hover:underline">{user.displayName}</p>
<p className="line-clamp-1 break-all text-muted-foreground">@{user.userName}</p>
</div>
</Link>
</UserTooltip>
<Linkify>
<div className="line-clamp-6 whitespace-pre-line break-words text-muted-foreground">{user.bio}</div>
</Linkify>
{user.id !== currentUser.id && (
<FollowButton
userId={user.id}
initialState={{
followers: user._count.followers,
isFollowedByUser: user.followers.some(({ followerId }) => followerId === currentUser.id)
}}
/>
)}
</div>
)
}

export default PostPage
1 change: 0 additions & 1 deletion client/src/app/(main)/users/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ const Profile = async ({ params }: PageProps) => {
if (!currentUser) {
return (
<p className="text-destructive">
{/* */}
You&apos;re not authorized to view this page.
</p>
)
Expand Down
19 changes: 10 additions & 9 deletions client/src/components/posts/Post.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use client'

import Link from 'next/link'
import { PostData } from '@/types'
import { cn, formatRelativeDate } from '@/lib/utils'
Expand All @@ -14,29 +15,29 @@ interface PostProps {
}

const Post = ({ post }: PostProps) => {
const { user } = post
const { author } = post
const { user: currentUser } = useSession()
return (
<article className="group/post space-y-3 rounded-md bg-card p-5 shadow-sm">
<div className="flex justify-between gap-3">
<div className="flex flex-wrap gap-3">
<UserTooltip user={user}>
<Link href={`/users/${user.userName}`}>
<Avatar url={user.avatarUrl} />
<UserTooltip user={author}>
<Link href={`/users/${author.userName}`}>
<Avatar url={author.avatarUrl} />
</Link>
</UserTooltip>
<div>
<UserTooltip user={user}>
<Link href={`users/${user.userName}`} className="block font-medium hover:underline">
{user.displayName}
<UserTooltip user={author}>
<Link href={`/users/${author.userName}`} className="block font-medium hover:underline">
{author.displayName}
</Link>
</UserTooltip>
<Link href={`post/${post.id}`} className="block text-sm text-muted-foreground hover:underline">
<Link suppressHydrationWarning href={`/posts/${post.id}`} className="block text-sm text-muted-foreground hover:underline">
{formatRelativeDate(post.createdAt)}
</Link>
</div>
</div>
{user.id === currentUser.id && (
{author.id === currentUser.id && (
<PostMoreButton
post={post}
className={'opacity-0 transition-opacity group-hover/post:opacity-100'}
Expand Down
1 change: 1 addition & 0 deletions client/src/components/posts/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const useDeletePostMutation = () => {
}
})
toast({
variant: 'info',
description: 'Post deleted Successfully'
})
if (pathName === `/posts/${deletedPost.id}`) {
Expand Down
2 changes: 1 addition & 1 deletion client/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface FollowerInfo {
}
export const getPostDataInclude = (userId: string) => {
return {
user: {
author: {
select: getUserDataSelect(userId)
},
attachments: true
Expand Down

0 comments on commit b8fe3c7

Please sign in to comment.