From b848aa9e4002e9ab5e23350cdc8a55e24510d568 Mon Sep 17 00:00:00 2001 From: Max Leiter Date: Sun, 25 Dec 2022 20:00:26 -0800 Subject: [PATCH] Style improvements, re-enable themes, bump next --- src/app/(auth)/components/index.tsx | 1 - src/app/(posts)/components/preview/index.tsx | 1 - src/app/(posts)/new/components/new.tsx | 2 +- .../components/header/post-buttons/index.tsx | 17 +- .../[id]/components/header/title/index.tsx | 4 +- .../components/header/title/title.module.css | 7 + .../post/[id]/components/post-files/index.tsx | 83 ++++------ .../post-files/password-modal-wrapper.tsx | 26 ++- .../post-files/view-document/index.tsx | 2 +- src/app/(posts)/post/[id]/layout.module.css | 1 + src/app/(posts)/post/[id]/layout.tsx | 152 ++++-------------- src/app/(posts)/post/[id]/loading.tsx | 4 - src/app/(posts)/post/[id]/page.tsx | 122 +++++++------- src/app/author/[username]/page.tsx | 4 +- .../badges/expiration-badge/index.tsx | 11 +- .../badges/visibility-control/index.tsx | 15 +- src/app/components/button-dropdown/index.tsx | 9 +- src/app/components/card/card.module.css | 2 +- src/app/components/header/index.tsx | 2 +- src/app/components/password-modal/index.tsx | 22 +-- .../password-modal/modal.module.css | 8 +- src/app/components/post-list/index.tsx | 8 +- .../components/post-list/list-item.module.css | 11 ++ src/app/components/post-list/list-item.tsx | 93 ++++++----- .../components/post-list/post-list.module.css | 6 - src/app/mine/page.tsx | 1 + src/app/page.tsx | 20 ++- src/app/providers.tsx | 5 +- .../settings/components/sections/profile.tsx | 3 +- src/app/styles/globals.css | 2 +- src/next.config.mjs | 5 +- src/package.json | 7 +- src/pages/api/post/[id].ts | 6 +- src/pages/api/user/[id].ts | 5 +- src/pnpm-lock.yaml | 142 ++++++++-------- src/types/next-auth.d.ts | 1 - 36 files changed, 385 insertions(+), 425 deletions(-) diff --git a/src/app/(auth)/components/index.tsx b/src/app/(auth)/components/index.tsx index f364fefc..f8a44bea 100644 --- a/src/app/(auth)/components/index.tsx +++ b/src/app/(auth)/components/index.tsx @@ -55,7 +55,6 @@ const Auth = ({ message: res.error }) } else { - console.log("res", res) startTransition(() => { router.push("/new") router.refresh() diff --git a/src/app/(posts)/components/preview/index.tsx b/src/app/(posts)/components/preview/index.tsx index 4b27b6c4..cdda5bd4 100644 --- a/src/app/(posts)/components/preview/index.tsx +++ b/src/app/(posts)/components/preview/index.tsx @@ -2,7 +2,6 @@ import { memo, useEffect, useState } from "react" import styles from "./preview.module.css" import "@styles/markdown.css" import "@styles/syntax.css" -import Skeleton from "@components/skeleton" import { Spinner } from "@components/spinner" type Props = { diff --git a/src/app/(posts)/new/components/new.tsx b/src/app/(posts)/new/components/new.tsx index cef7c11d..7b26c2dc 100644 --- a/src/app/(posts)/new/components/new.tsx +++ b/src/app/(posts)/new/components/new.tsx @@ -311,7 +311,7 @@ const Post = ({ enableTabLoop={false} minDate={new Date()} /> - + {parentId && ( @@ -64,11 +63,7 @@ export const PostButtons = ({ View Parent )} - diff --git a/src/app/(posts)/post/[id]/components/header/title/index.tsx b/src/app/(posts)/post/[id]/components/header/title/index.tsx index 10b488fc..fa3be480 100644 --- a/src/app/(posts)/post/[id]/components/header/title/index.tsx +++ b/src/app/(posts)/post/[id]/components/header/title/index.tsx @@ -34,7 +34,9 @@ export const PostTitle = ({ {title}{" "} by{" "} - {displayName || "anonymous"} + + {displayName || "anonymous"} + {!loading && ( diff --git a/src/app/(posts)/post/[id]/components/header/title/title.module.css b/src/app/(posts)/post/[id]/components/header/title/title.module.css index 613afacb..e8a1c24f 100644 --- a/src/app/(posts)/post/[id]/components/header/title/title.module.css +++ b/src/app/(posts)/post/[id]/components/header/title/title.module.css @@ -17,6 +17,13 @@ display: inline-block; } +.titleWithDropdown { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + @media screen and (max-width: 768px) { .title { flex-direction: column; diff --git a/src/app/(posts)/post/[id]/components/post-files/index.tsx b/src/app/(posts)/post/[id]/components/post-files/index.tsx index d343c586..4354a67d 100644 --- a/src/app/(posts)/post/[id]/components/post-files/index.tsx +++ b/src/app/(posts)/post/[id]/components/post-files/index.tsx @@ -5,8 +5,7 @@ import DocumentComponent from "./view-document" import { useEffect, useState } from "react" import { useRouter } from "next/navigation" import PasswordModalPage from "./password-modal-wrapper" -import { File, PostWithFilesAndAuthor } from "@lib/server/prisma" -import { useSession } from "next-auth/react" +import { PostWithFilesAndAuthor } from "@lib/server/prisma" type Props = { post: string | PostWithFilesAndAuthor @@ -14,66 +13,46 @@ type Props = { isAuthor?: boolean } -const PostFiles = ({ - post: _initialPost, - isAuthor: isAuthorFromServer -}: Props) => { - const { data: session, status } = useSession() - const isLoading = status === "loading" +const PostFiles = ({ post: _initialPost }: Props) => { const initialPost = typeof _initialPost === "string" ? (JSON.parse(_initialPost) as PostWithFilesAndAuthor) : _initialPost const [post, setPost] = useState(initialPost) - const isProtected = initialPost.visibility === "protected" - - // We generate public and unlisted posts at build time, so we can't use - // the session to determine if the user is the author on the server. We need to check - // the post's authorId against the session's user id. - const isAuthor = isAuthorFromServer - ? true - : session?.user?.id === post?.authorId const router = useRouter() - useEffect(() => { - if (post.expiresAt) { - if (new Date(post.expiresAt) < new Date()) { - if (!isAuthor) { - router.push("/expired") - } - - const expirationDate = new Date(post.expiresAt ? post.expiresAt : "") - if (!isAuthor && expirationDate < new Date()) { - router.push("/expired") - } - - let interval: NodeJS.Timer | null = null - if (post.expiresAt) { - interval = setInterval(() => { - const expirationDate = new Date( - post.expiresAt ? post.expiresAt : "" - ) - if (expirationDate < new Date()) { - if (!isAuthor) { - router.push("/expired") - } - clearInterval(interval!) - } - }, 4000) - } - return () => { - if (interval) clearInterval(interval) - } - } + if (post.expiresAt) { + if (new Date(post.expiresAt) < new Date()) { + router.push("/expired") } - }, [isAuthor, post.expiresAt, router]) - - if (isLoading) { - return } - if (isProtected) { - return + useEffect(() => { + let interval: NodeJS.Timer | null = null + if (post.expiresAt) { + interval = setInterval(() => { + const expirationDate = new Date(post.expiresAt ? post.expiresAt : "") + if (expirationDate < new Date()) { + router.push("/expired") + if (interval) clearInterval(interval) + } + }, 4000) + } + return () => { + if (interval) clearInterval(interval) + } + }, [post.expiresAt, router]) + + const isProtected = post.visibility === "protected" + const hasFetched = post.files !== undefined + if (isProtected && !hasFetched) { + return ( + + ) } return ( diff --git a/src/app/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx b/src/app/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx index 194b618f..1a227118 100644 --- a/src/app/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx +++ b/src/app/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx @@ -3,20 +3,27 @@ import { Post, PostWithFilesAndAuthor } from "@lib/server/prisma" import PasswordModal from "@components/password-modal" import { useRouter } from "next/navigation" -import { useState } from "react" +import { useCallback, useEffect, useState } from "react" import { useToasts } from "@components/toasts" +import { useSession } from "next-auth/react" type Props = { setPost: (post: PostWithFilesAndAuthor) => void postId: Post["id"] + authorId: Post["authorId"] } -const PasswordModalPage = ({ setPost, postId }: Props) => { +const PasswordModalPage = ({ setPost, postId, authorId }: Props) => { const router = useRouter() const { setToast } = useToasts() + const { data: session, status } = useSession() + const isAuthor = + status === "loading" + ? undefined + : session?.user && session?.user?.id === authorId const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(true) - const onSubmit = async (password: string) => { + const onSubmit = useCallback(async (password: string) => { const res = await fetch(`/api/post/${postId}?password=${password}`, { method: "GET", headers: { @@ -44,13 +51,24 @@ const PasswordModalPage = ({ setPost, postId }: Props) => { setPost(data) } } - } + }, [postId, setPost, setToast]) const onClose = () => { setIsPasswordModalOpen(false) router.push("/") } + useEffect(() => { + if (isAuthor) { + onSubmit("author") + setToast({ + message: + "You're the author of this post, so you automatically have access to it.", + type: "default" + }) + } + }, [isAuthor, onSubmit, setToast]) + return ( {
- + ({ -// id: post.id -// })) -// } - -// export const dynamic = 'error'; - -const getPost = async (id: string) => { - const [post, user] = await Promise.all([ - getPostById(id, { - select: { - visibility: true, - authorId: true, - title: true, - description: true, - id: true, - createdAt: true, - expiresAt: true, - parentId: true, - author: { - select: { - displayName: true, - image: true - } - }, - files: { - select: { - id: true, - content: true, - updatedAt: true, - title: true - } - } - } - }).then((post) => { - if (!post) { - return notFound() - } - return post as PostWithFilesAndAuthor - }), - getCurrentUser() - ]) - - const isAuthorOrAdmin = user?.id === post?.authorId || user?.role === "admin" - - if (post.visibility === "public" || post.visibility === "unlisted") { - return { post, isAuthorOrAdmin } - } - - if (post.visibility === "private" && !isAuthorOrAdmin) { - return redirect("/signin") - } - - if (post.visibility === "protected" && !isAuthorOrAdmin) { - return { - // TODO: remove this. It's temporary to appease typescript - post: { - visibility: "protected", - id: post.id, - files: [], - parentId: "", - title: "", - createdAt: "", - expiresAt: "", - author: { - displayName: "" - }, - description: "", - authorId: "" - }, - isAuthor: isAuthorOrAdmin - } - } - - // if expired - if (post.expiresAt && !isAuthorOrAdmin) { - const expirationDate = new Date(post.expiresAt) - if (expirationDate < new Date()) { - return redirect("/expired") - } - } - - return { post, isAuthor: isAuthorOrAdmin } -} +import { getPost } from "./page" export default async function PostLayout({ children, @@ -116,38 +15,41 @@ export default async function PostLayout({ } children: React.ReactNode }) { - const { post, isAuthor } = await getPost(params.id) + const { post } = (await getPost(params.id)) as { + post: PostWithFilesAndAuthor + } return (
- - + {/* post.title is not set when the post is protected */} + {post.title && ( + + )} + {post.title && ( + + )}
{post.description && (

{post.description}

)} - - {isAuthor && ( - - - - )} {children}
diff --git a/src/app/(posts)/post/[id]/loading.tsx b/src/app/(posts)/post/[id]/loading.tsx index 09eb8232..f6ac12d6 100644 --- a/src/app/(posts)/post/[id]/loading.tsx +++ b/src/app/(posts)/post/[id]/loading.tsx @@ -1,5 +1,3 @@ -import { PostButtons } from "./components/header/post-buttons" -import { PostTitle } from "./components/header/title" import DocumentComponent from "./components/post-files/view-document" import styles from "./layout.module.css" @@ -7,8 +5,6 @@ export default function PostLoading() { return ( <>
- -
diff --git a/src/app/(posts)/post/[id]/page.tsx b/src/app/(posts)/post/[id]/page.tsx index 1f25eced..0d31631e 100644 --- a/src/app/(posts)/post/[id]/page.tsx +++ b/src/app/(posts)/post/[id]/page.tsx @@ -1,86 +1,71 @@ +import VisibilityControl from "@components/badges/visibility-control" import { getPostById } from "@lib/server/prisma" import { getCurrentUser } from "@lib/server/session" import { notFound, redirect } from "next/navigation" +import { cache } from "react" import PostFiles from "./components/post-files" -const getPost = async (id: string) => { - const [post, user] = await Promise.all([ - getPostById(id, { - select: { - visibility: true, - authorId: true, - title: true, - description: true, - id: true, - createdAt: true, - expiresAt: true, - parentId: true, - author: { - select: { - displayName: true, - image: true - } - }, - files: { - select: { - id: true, - content: true, - updatedAt: true, - title: true, - html: true - } +export const getPost = cache(async (id: string) => { + const post = await getPostById(id, { + select: { + visibility: true, + authorId: true, + title: true, + description: true, + id: true, + createdAt: true, + expiresAt: true, + parentId: true, + author: { + select: { + displayName: true, + image: true + } + }, + files: { + select: { + id: true, + content: true, + updatedAt: true, + title: true, + html: true } } - }).then((post) => { - if (!post) { - return notFound() - } - return post - }), - getCurrentUser() - ]) - - const isAuthorOrAdmin = user?.id === post?.authorId || user?.role === "admin" - - // if expired - if (post.expiresAt && !isAuthorOrAdmin) { - const expirationDate = new Date(post.expiresAt) - if (expirationDate < new Date()) { - return redirect("/expired") } + }) + + if (!post) { + return notFound() + } + + if (post.expiresAt && new Date(post.expiresAt) < new Date()) { + return redirect("/expired") } if (post.visibility === "public" || post.visibility === "unlisted") { - return { post, isAuthorOrAdmin } + return { post } } - if (post.visibility === "private" && !isAuthorOrAdmin) { - return redirect("/signin") + if (post.visibility === "private") { + const user = await getCurrentUser() + if (user?.id === post.authorId || user.role === "admin") { + return { post } + } + return redirect("/new") } - if (post.visibility === "protected" && !isAuthorOrAdmin) { + if (post.visibility === "protected") { return { - // TODO: remove this. It's temporary to appease typescript post: { visibility: "protected", - id: post.id, - files: [], - parentId: "", - title: "", - createdAt: "", - expiresAt: "", - author: { - displayName: "" - }, - description: "", - authorId: "" - }, - isAuthor: isAuthorOrAdmin + authorId: post.authorId, + id: post.id + } } } - return { post, isAuthor: isAuthorOrAdmin } -} + return { post } +}) export default async function PostPage({ params @@ -89,7 +74,16 @@ export default async function PostPage({ id: string } }) { - const { post, isAuthor } = await getPost(params.id) + const { post } = await getPost(params.id) const stringifiedPost = JSON.stringify(post) - return + return ( + <> + + + + ) } diff --git a/src/app/author/[username]/page.tsx b/src/app/author/[username]/page.tsx index a437a0a1..e816028a 100644 --- a/src/app/author/[username]/page.tsx +++ b/src/app/author/[username]/page.tsx @@ -43,10 +43,10 @@ export default async function UserPage({ return ( User avatar ) } diff --git a/src/app/components/badges/expiration-badge/index.tsx b/src/app/components/badges/expiration-badge/index.tsx index 9b3309f8..caa7965e 100644 --- a/src/app/components/badges/expiration-badge/index.tsx +++ b/src/app/components/badges/expiration-badge/index.tsx @@ -37,8 +37,11 @@ const ExpirationBadge = ({ }, [expirationDate]) const isExpired = useMemo(() => { - return timeUntilString === "in 0 seconds" - }, [timeUntilString]) + if (!expirationDate) { + return false + } + return expirationDate < new Date() + }, [expirationDate]) if (!expirationDate) { return null @@ -49,7 +52,9 @@ const ExpirationBadge = ({ - <>{isExpired ? "Expired" : `Expires ${timeUntilString}`} + + {isExpired ? "Expired" : `Expires ${timeUntilString}`} + ) diff --git a/src/app/components/badges/visibility-control/index.tsx b/src/app/components/badges/visibility-control/index.tsx index 444a44aa..3d52da8a 100644 --- a/src/app/components/badges/visibility-control/index.tsx +++ b/src/app/components/badges/visibility-control/index.tsx @@ -6,13 +6,17 @@ import ButtonGroup from "@components/button-group" import Button from "@components/button" import { useToasts } from "@components/toasts" import { Spinner } from "@components/spinner" +import { useSession } from "next-auth/react" type Props = { + authorId: string postId: string visibility: string } -const VisibilityControl = ({ postId, visibility: postVisibility }: Props) => { +const VisibilityControl = ({ authorId, postId, visibility: postVisibility }: Props) => { + const { data: session } = useSession() + const isAuthor = session?.user && session?.user?.id === authorId const [visibility, setVisibility] = useState(postVisibility) const [isSubmitting, setSubmitting] = useState() @@ -66,9 +70,16 @@ const VisibilityControl = ({ postId, visibility: postVisibility }: Props) => { const submitPassword = (password: string) => onSubmit("protected", password) + if (!isAuthor) { + return null + } + return ( <> - + - +
+ + +
diff --git a/src/app/components/password-modal/modal.module.css b/src/app/components/password-modal/modal.module.css index ea258b67..fe527fab 100644 --- a/src/app/components/password-modal/modal.module.css +++ b/src/app/components/password-modal/modal.module.css @@ -34,7 +34,7 @@ display: flex; flex-direction: column; gap: var(--gap); - margin-bottom: var(--gap-half); + margin-bottom: var(--gap); } .content:focus { @@ -48,6 +48,12 @@ gap: var(--gap-half); } +.footer { + display: flex; + justify-content: flex-end; + gap: var(--gap-half); +} + @keyframes overlayShow { from { opacity: 0; diff --git a/src/app/components/post-list/index.tsx b/src/app/components/post-list/index.tsx index 5f89d8c4..cbb8103e 100644 --- a/src/app/components/post-list/index.tsx +++ b/src/app/components/post-list/index.tsx @@ -16,13 +16,17 @@ type Props = { morePosts?: boolean userId?: string hideSearch?: boolean + hideActions?: boolean + isOwner?: boolean } const PostList = ({ morePosts, initialPosts: initialPostsMaybeJSON, userId, - hideSearch + hideSearch, + hideActions, + isOwner }: Props) => { const initialPosts = typeof initialPostsMaybeJSON === "string" @@ -156,6 +160,8 @@ const PostList = ({ deletePost={deletePost(post.id)} post={post} key={post.id} + hideActions={hideActions} + isOwner={isOwner} /> ) })} diff --git a/src/app/components/post-list/list-item.module.css b/src/app/components/post-list/list-item.module.css index 2213f031..4b43e46d 100644 --- a/src/app/components/post-list/list-item.module.css +++ b/src/app/components/post-list/list-item.module.css @@ -3,6 +3,12 @@ justify-content: space-between; } +.titleText { + display: flex; + gap: var(--gap-half); + align-items: center; +} + .badges { display: flex; gap: var(--gap-half); @@ -33,6 +39,11 @@ .title h3 { margin: 0; } + + .titleText { + flex-direction: column; + align-items: flex-start; + } } .files { diff --git a/src/app/components/post-list/list-item.tsx b/src/app/components/post-list/list-item.tsx index 4127dafc..9e7353b7 100644 --- a/src/app/components/post-list/list-item.tsx +++ b/src/app/components/post-list/list-item.tsx @@ -24,12 +24,14 @@ import { codeFileExtensions } from "@lib/constants" // TODO: isOwner should default to false so this can be used generically const ListItem = ({ post, - isOwner = true, - deletePost + isOwner, + deletePost, + hideActions }: { post: PostWithFiles isOwner?: boolean deletePost: () => void + hideActions?: boolean }) => { const router = useRouter() @@ -69,33 +71,45 @@ const ListItem = ({ <>
-

- - {post.title} - -

- {isOwner && ( - - {post.parentId && ( - -
{post.description && (

{post.description}

)} - -
- - - {post.files?.length === 1 - ? "1 file" - : `${post.files?.length || 0} files`} - - - -
    {post?.files?.map( (file: Pick["files"][0]) => { return (
  • - -`` {getIconFromFilename(file.title)} + + {getIconFromFilename(file.title)} {file.title || "Untitled file"}
  • diff --git a/src/app/components/post-list/post-list.module.css b/src/app/components/post-list/post-list.module.css index 4e828992..39f82288 100644 --- a/src/app/components/post-list/post-list.module.css +++ b/src/app/components/post-list/post-list.module.css @@ -8,12 +8,6 @@ padding: 0.5rem 0; } -.container ul li::before { - content: ""; - padding: 0; - margin: 0; -} - .postHeader { display: flex; justify-content: space-between; diff --git a/src/app/mine/page.tsx b/src/app/mine/page.tsx index 1f0dcb53..5a2af622 100644 --- a/src/app/mine/page.tsx +++ b/src/app/mine/page.tsx @@ -25,6 +25,7 @@ export default async function Mine() { userId={userId} morePosts={hasMore} initialPosts={stringifiedPosts} + isOwner={true} /> ) } diff --git a/src/app/page.tsx b/src/app/page.tsx index 220cc7eb..1078959d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,7 +11,6 @@ const getWelcomeData = async () => { } export default async function Page() { - const { content, rendered, title } = await getWelcomeData() const getPostsPromise = getAllPosts({ select: { id: true, @@ -21,18 +20,24 @@ export default async function Page() { select: { name: true } - } + }, + visibility: true, + files: { + select: { + id: true, + title: true + } + }, + authorId: true }, where: { - deletedAt: null, - expiresAt: { - gt: new Date() - } + visibility: "public" }, orderBy: { createdAt: "desc" } }) + const { content, rendered, title } = await getWelcomeData() return (
    ) } -export const revalidate = 30 +export const revalidate = 60 diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 70c5fc44..9fb9d376 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -2,12 +2,15 @@ import * as RadixTooltip from "@radix-ui/react-tooltip" import { SessionProvider } from "next-auth/react" +import { ThemeProvider } from "next-themes" export function Providers({ children }: { children: React.ReactNode }) { return ( - {children} + + {children} + ) diff --git a/src/app/settings/components/sections/profile.tsx b/src/app/settings/components/sections/profile.tsx index a1f02158..283bdac0 100644 --- a/src/app/settings/components/sections/profile.tsx +++ b/src/app/settings/components/sections/profile.tsx @@ -10,9 +10,8 @@ import styles from "./profile.module.css" const Profile = () => { const { data: session } = useSession() - const [name, setName] = useState(session?.user.displayName || "") + const [name, setName] = useState(session?.user.name || "") const [submitting, setSubmitting] = useState(false) - const { setToast } = useToasts() const handleNameChange = (e: React.ChangeEvent) => { diff --git a/src/app/styles/globals.css b/src/app/styles/globals.css index b2f8292f..1d02e21e 100644 --- a/src/app/styles/globals.css +++ b/src/app/styles/globals.css @@ -42,7 +42,7 @@ --darkest-gray: #efefef; --article-color: #eaeaea; --header-bg: rgba(19, 20, 21, 0.45); - --gray-alpha: rgba(255, 255, 255, 0.5); + --gray-alpha: rgba(19, 20, 21, 0.6); --selection: rgba(255, 255, 255, 0.99); --border: var(--light-gray); --warning: #ff6700; diff --git a/src/next.config.mjs b/src/next.config.mjs index 8612c88f..f4afec3b 100644 --- a/src/next.config.mjs +++ b/src/next.config.mjs @@ -6,7 +6,7 @@ const nextConfig = { experimental: { // esmExternals: true, appDir: true, - serverComponentsExternalPackages: ["prisma", "@prisma/client"], + serverComponentsExternalPackages: ["prisma", "@prisma/client"] }, output: "standalone", rewrites() { @@ -20,6 +20,9 @@ const nextConfig = { destination: "/" } ] + }, + images: { + domains: ["avatars.githubusercontent.com"] } } diff --git a/src/package.json b/src/package.json index 0a6f3865..698d6d9f 100644 --- a/src/package.json +++ b/src/package.json @@ -14,8 +14,8 @@ }, "dependencies": { "@next-auth/prisma-adapter": "^1.0.5", - "@next/eslint-plugin-next": "13.0.7-canary.4", - "@next/font": "13.0.8-canary.0", + "@next/eslint-plugin-next": "13.1.1-canary.1", + "@next/font": "13.1.1-canary.1", "@prisma/client": "^4.7.1", "@radix-ui/react-dialog": "^1.0.2", "@radix-ui/react-dropdown-menu": "^2.0.1", @@ -28,8 +28,9 @@ "client-zip": "2.2.1", "jest": "^29.3.1", "lodash.debounce": "^4.0.8", - "next": "13.0.8-canary.0", + "next": "13.1.1-canary.1", "next-auth": "^4.18.6", + "next-themes": "^0.2.1", "prisma": "^4.7.1", "react": "18.2.0", "react-datepicker": "4.8.0", diff --git a/src/pages/api/post/[id].ts b/src/pages/api/post/[id].ts index 68e86b80..0fd60656 100644 --- a/src/pages/api/post/[id].ts +++ b/src/pages/api/post/[id].ts @@ -61,14 +61,14 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse) { ...post }) } else { - return { + return res.json({ isProtected: true, post: { id: post.id, visibility: post.visibility, - title: post.title + authorId: post.authorId, } - } + }) } } diff --git a/src/pages/api/user/[id].ts b/src/pages/api/user/[id].ts index 2efabde8..8ea31dfa 100644 --- a/src/pages/api/user/[id].ts +++ b/src/pages/api/user/[id].ts @@ -42,7 +42,10 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { }) } case "GET": - return res.json(currUser) + return res.json({ + ...currUser, + displayName: user.displayName + }) case "DELETE": if (currUser?.role !== "admin" && currUser?.id !== id) { return res.status(403).json({ message: "Unauthorized" }) diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml index 2c17a42d..d33668ba 100644 --- a/src/pnpm-lock.yaml +++ b/src/pnpm-lock.yaml @@ -3,8 +3,8 @@ lockfileVersion: 5.4 specifiers: '@next-auth/prisma-adapter': ^1.0.5 '@next/bundle-analyzer': 13.0.7-canary.4 - '@next/eslint-plugin-next': 13.0.7-canary.4 - '@next/font': 13.0.8-canary.0 + '@next/eslint-plugin-next': 13.1.1-canary.1 + '@next/font': 13.1.1-canary.1 '@prisma/client': ^4.7.1 '@radix-ui/react-dialog': ^1.0.2 '@radix-ui/react-dropdown-menu': ^2.0.1 @@ -31,8 +31,9 @@ specifiers: eslint-config-next: 13.0.3 jest: ^29.3.1 lodash.debounce: ^4.0.8 - next: 13.0.8-canary.0 + next: 13.1.1-canary.1 next-auth: ^4.18.6 + next-themes: ^0.2.1 next-unused: 0.0.6 prettier: 2.6.2 prisma: ^4.7.1 @@ -52,8 +53,8 @@ specifiers: dependencies: '@next-auth/prisma-adapter': 1.0.5_64qbzg5ec56bux2misz3l4u6g4 - '@next/eslint-plugin-next': 13.0.7-canary.4 - '@next/font': 13.0.8-canary.0 + '@next/eslint-plugin-next': 13.1.1-canary.1 + '@next/font': 13.1.1-canary.1 '@prisma/client': 4.7.1_prisma@4.7.1 '@radix-ui/react-dialog': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u '@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u @@ -61,13 +62,14 @@ dependencies: '@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y '@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u '@wcj/markdown-to-html': 2.1.2 - '@wits/next-themes': 0.2.14_rhfownvlqkszea7w3lnpwl7bzy + '@wits/next-themes': 0.2.14_jcrpix7mbfpfu5movksylxa5c4 client-only: 0.0.1 client-zip: 2.2.1 jest: 29.3.1_@types+node@17.0.23 lodash.debounce: 4.0.8 - next: 13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y - next-auth: 4.18.6_rhfownvlqkszea7w3lnpwl7bzy + next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y + next-auth: 4.18.6_jcrpix7mbfpfu5movksylxa5c4 + next-themes: 0.2.1_jcrpix7mbfpfu5movksylxa5c4 prisma: 4.7.1 react: 18.2.0 react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y @@ -790,7 +792,7 @@ packages: next-auth: ^4 dependencies: '@prisma/client': 4.7.1_prisma@4.7.1 - next-auth: 4.18.6_rhfownvlqkszea7w3lnpwl7bzy + next-auth: 4.18.6_jcrpix7mbfpfu5movksylxa5c4 dev: false /@next/bundle-analyzer/13.0.7-canary.4: @@ -802,8 +804,8 @@ packages: - utf-8-validate dev: true - /@next/env/13.0.8-canary.0: - resolution: {integrity: sha512-IiZM9mAUE9F3p9q/ydZBGlvmleOaMO6fBDBJzvQa4t3Ezg5e3NfGlTO01MTWvKPEKYPeAwFp+tcVh9ivA28+Dw==} + /@next/env/13.1.1-canary.1: + resolution: {integrity: sha512-h8DEj69dLJpFUuXOVPQeJ4/X1LbW5mtZSsaS5Xr/pt2VbrRN50eAV/6rMY+l6U6p/4AX1/F5aK4UBzLQJbwFzw==} dev: false /@next/eslint-plugin-next/13.0.3: @@ -812,18 +814,18 @@ packages: glob: 7.1.7 dev: true - /@next/eslint-plugin-next/13.0.7-canary.4: - resolution: {integrity: sha512-jNgarJTQSia+yTcQr6dF9wZhCfCFIXbc0WzSIAfyx4Z8FZjfmTmeVDGL1JHSYlcSmTnJ6o6Z36MsFh/mreuE4g==} + /@next/eslint-plugin-next/13.1.1-canary.1: + resolution: {integrity: sha512-gKabWQJ+Aps/u/lzVC3FoGwbG+r1t3cwoUXPbcpC3igrpBSbkhEoK9u3MYeMlkAUywJ7IvsENWVYqjzRuaso4Q==} dependencies: glob: 7.1.7 dev: false - /@next/font/13.0.8-canary.0: - resolution: {integrity: sha512-sdi4WoYMbJdiDev9UICg9dMhhA7I2xaJT07e8DZoSDWWTZaKk5zKNKuQFQoP47461R1WZAIQwIYTq+pVxQ87Gw==} + /@next/font/13.1.1-canary.1: + resolution: {integrity: sha512-cygeAS0h5OuWaorcQ6Ry8c/E0fwZEGQfZy7kUjXHVn6DP4sekB2RgR9aNWL3cUoJ35RwjwtsR7K6FufUyzfGag==} dev: false - /@next/swc-android-arm-eabi/13.0.8-canary.0: - resolution: {integrity: sha512-U6nayRvWuASLLBwqG4nN9540ako+JEBblN8479BpGvW1F2FyQPUx/zq+WO0b47KPyJI2XNPBIenHGvtSY7yN/Q==} + /@next/swc-android-arm-eabi/13.1.1-canary.1: + resolution: {integrity: sha512-0McGEjTnNXdBTlghWxkuM07qpKMr44afLeGFpS/zwIlDV7lNOXFzCpyHdJoJsFL4kBJgfbyCi8aamnhwqlwZxA==} engines: {node: '>= 10'} cpu: [arm] os: [android] @@ -831,8 +833,8 @@ packages: dev: false optional: true - /@next/swc-android-arm64/13.0.8-canary.0: - resolution: {integrity: sha512-GtUW5CCIfN1FUln+pRm0rAWe8k957rcKhYDPGBrfr+jaKvUgjI4NgMcXRJ0R83j+vcM4+DIhIkIO+OYQ1vU4RA==} + /@next/swc-android-arm64/13.1.1-canary.1: + resolution: {integrity: sha512-XCmPqmhtsc52lv0Qs/maThRrQpHMRK1AqFhgFXfFG9wclbFBtQIUapD/qD7nOlXbch+7RDONbABPf/8pE2T0cQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] @@ -840,8 +842,8 @@ packages: dev: false optional: true - /@next/swc-darwin-arm64/13.0.8-canary.0: - resolution: {integrity: sha512-dqUn4ERXHT+g/L+paIi+IhNP3P7HiF95ZBIjQvn++n0IhdT8rRfaQK3ubps/NopL14jHA33J7HnK73vgUBIvwg==} + /@next/swc-darwin-arm64/13.1.1-canary.1: + resolution: {integrity: sha512-qz+et20cTetOppH6stlDW171tTo1vG4eHGmXY1Zwa3D/sZPk5IRsqsmpdzxuBsVxdk5x7zaliYZowOlQM2awnw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -849,8 +851,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64/13.0.8-canary.0: - resolution: {integrity: sha512-jGaI2idOd2Z0Dvlnz0WYHC+hbqQPIhaso/upJQebknWeu1VsSrwH5oDbCgMBaXLkHO7rMOITWC5FjxdXjSGK6g==} + /@next/swc-darwin-x64/13.1.1-canary.1: + resolution: {integrity: sha512-rPGOUsxturFtSkqtbSis1kBuu0mNzCPibWEMihsM32EzdXeDXJMgl5EP3+RiwGfrawON5lcTEz0r52Zll+0kmw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -858,8 +860,8 @@ packages: dev: false optional: true - /@next/swc-freebsd-x64/13.0.8-canary.0: - resolution: {integrity: sha512-ieM8XwqX9m/frFGpSwrXubzZYPT+ZzOEJsDgCNo3CD0DGHu8hZz1XLRym0Nl2mZAnBlxgENN+RlGwutWKBQMHg==} + /@next/swc-freebsd-x64/13.1.1-canary.1: + resolution: {integrity: sha512-tEnpdXSEzltEEbeh32w4eQN1znR35xjX0pMC7leud8XhJvppWwdEqfdOp3OuviPmb8p6LzFqYyknNe710cFy+Q==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] @@ -867,8 +869,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm-gnueabihf/13.0.8-canary.0: - resolution: {integrity: sha512-/9CnPhcqu/kudpk07zCkApFRUwF0wbwdFm5CqtguZ6yubqhoAV1Wjmrs1gnt+MUBHsVnKRdoGkz6KupQEZqz7g==} + /@next/swc-linux-arm-gnueabihf/13.1.1-canary.1: + resolution: {integrity: sha512-EJdCFjRHVoyDC8Q0N8ULCJ7+5hl4NhaELlu+04cCcgQ3qFZkFZIfTLrXnCT1aa2Y8yyR5FvyBeHgvusL5abqpQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] @@ -876,8 +878,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu/13.0.8-canary.0: - resolution: {integrity: sha512-KUQs6KdX3lMxJu4ym/jNzotQvbkpXD/ne8KgjUuzTdgw3LYSfEMsTzORj71IR48H5yMDSLGPvCJA+B8FuVzS8Q==} + /@next/swc-linux-arm64-gnu/13.1.1-canary.1: + resolution: {integrity: sha512-BRN7Beg1OASa2F7FGYAdYL3O+bA2wFX6ow9QnHD312+JHCf/IKun3FSxSXBaSnc8ZJCnexmSWIz+hewKN1jGQQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -885,8 +887,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl/13.0.8-canary.0: - resolution: {integrity: sha512-bisV2RUrsQMJodK2xGszfqK9G/BuDlqVLeDZVrOENWaZnOVDtrP+WlqrN0vS1r8xn/OepJWKkMnibO4aLCruvw==} + /@next/swc-linux-arm64-musl/13.1.1-canary.1: + resolution: {integrity: sha512-WE1muJmocpSHUHBH02iMOy9RR4Hz7XFM6tjAevY40svXNmGNszhYzsm0MQ+/VnlqP9f9l1/dEiPN6tSbMAlH9A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -894,8 +896,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu/13.0.8-canary.0: - resolution: {integrity: sha512-X8pcTN7nPZ7gDXb04oGWOS/qPvPaPK5x753AmevQgVa7FwqXQ6IkJeD3sr8ergmu6Fcfr6c4LcnBEQzpIxOxYA==} + /@next/swc-linux-x64-gnu/13.1.1-canary.1: + resolution: {integrity: sha512-aeBiutM8gFndpUkDA6t8DKzD9TcYg48+b7QxuL2XyRJo+47muhNbXaB6y/MwarxwjnsAry0hMs/ycP3lOL7vnw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -903,8 +905,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl/13.0.8-canary.0: - resolution: {integrity: sha512-Kg+tsnDmQ21qYfLik3YH+ZOYMmoNyhYqLMZE6qSASA5uN448J1cJUHIdpJxUpidZHtWBV+kTVR2Hw7+We+BiKQ==} + /@next/swc-linux-x64-musl/13.1.1-canary.1: + resolution: {integrity: sha512-JyJzejDuu68bZj1jrdbgJEIyj0xQy8N0R363T6Rx5/F5Htk2vVzXaP+MkANcWuZjvmH/BHjQc515liiTwQ328Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -912,8 +914,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc/13.0.8-canary.0: - resolution: {integrity: sha512-tde5+ZQFT0+Pr/BKINQ32+8C/AEaZLzU69AvpD7dvbUEJ5fReIiSBPIL1ov3pZYR+EPwl7wFPoj7NLxTU1E8WA==} + /@next/swc-win32-arm64-msvc/13.1.1-canary.1: + resolution: {integrity: sha512-y/VxMhjXrTt4fGzrJwdfa6MM2ZauZ0dX20aRGDX/6VeaxO5toBsmXF7cwoDC97C65l93FY/X9vyc75WSLrXFrA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -921,8 +923,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc/13.0.8-canary.0: - resolution: {integrity: sha512-CKs0Os7cDKa9GZANf4HbOgkQodjQ2GtJZBBwdZ7OaFMWmWet/0JCkakaF/+EUl0vx0QP83qpIK8LHEkYXxJItg==} + /@next/swc-win32-ia32-msvc/13.1.1-canary.1: + resolution: {integrity: sha512-Nk1DdvC+Ocdqnj4Ra+qWJK/PQ68hrWmSg3FXL4I3pooX2IZcUSF8nPFNS0r8V47inTAXbwatcFEKSBRjFBS2ww==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -930,8 +932,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc/13.0.8-canary.0: - resolution: {integrity: sha512-DTICRWenuqExpO3WmFzkhvYwKgLuPweb3eWiYeybSwHB6ji/cC5ZQjh3AvGbff548Ye8Z1bf4SUAIjdcg0Y/fA==} + /@next/swc-win32-x64-msvc/13.1.1-canary.1: + resolution: {integrity: sha512-/7q6tjUebSaUYTGZRpp4qAmrcL6+tiKfHN5YgW6zpX5MWLEk1DkdnuBjO/jSvCJd0510byBkN6drlzmfTMjzzg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1802,14 +1804,14 @@ packages: - supports-color dev: false - /@wits/next-themes/0.2.14_rhfownvlqkszea7w3lnpwl7bzy: + /@wits/next-themes/0.2.14_jcrpix7mbfpfu5movksylxa5c4: resolution: {integrity: sha512-fHKb/tRcWbYNblGHZtfvAQztDhzUB9d7ZkYOny0BisSPh6EABcsqxKB48ABUQztcmKywlp2zEMkLcSRj/PQBSw==} peerDependencies: next: '*' react: '*' react-dom: '*' dependencies: - next: 13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y + next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 dev: false @@ -5215,7 +5217,7 @@ packages: dev: true optional: true - /next-auth/4.18.6_rhfownvlqkszea7w3lnpwl7bzy: + /next-auth/4.18.6_jcrpix7mbfpfu5movksylxa5c4: resolution: {integrity: sha512-0TQwbq5X9Jyd1wUVYUoyvHJh4JWXeW9UOcMEl245Er/Y5vsSbyGJHt8M7xjRMzk9mORVMYehoMdERgyiq/jCgA==} engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0} peerDependencies: @@ -5231,7 +5233,7 @@ packages: '@panva/hkdf': 1.0.2 cookie: 0.5.0 jose: 4.11.0 - next: 13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y + next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y oauth: 0.9.15 openid-client: 5.3.0 preact: 10.11.2 @@ -5241,6 +5243,18 @@ packages: uuid: 8.3.2 dev: false + /next-themes/0.2.1_jcrpix7mbfpfu5movksylxa5c4: + resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} + peerDependencies: + next: '*' + react: '*' + react-dom: '*' + dependencies: + next: 13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /next-unused/0.0.6: resolution: {integrity: sha512-dHFNNBanFq4wvYrULtsjfWyZ6BzOnr5VYI9EYMGAZYF2vkAhFpj2JOuT5Wu2o3LbFSG92PmAZnSUF/LstF82pA==} hasBin: true @@ -5252,8 +5266,8 @@ packages: - supports-color dev: true - /next/13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-+LP4KZGBp+97TRgYExChOvoONZY1qfJmtB6IjG2HXDshgYpQmsAPEMy9r0rWbvhOveChCJ6sv+yEFAOCNc4yKQ==} + /next/13.1.1-canary.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-20EeQyfGs9dGUAPrXAod5jay1plcM0itItL/7z9BMczYM55/it8TxS1OPTmseyM9Y8uuybTRoCHeKh6TCI09tg==} engines: {node: '>=14.6.0'} hasBin: true peerDependencies: @@ -5270,27 +5284,27 @@ packages: sass: optional: true dependencies: - '@next/env': 13.0.8-canary.0 + '@next/env': 13.1.1-canary.1 '@swc/helpers': 0.4.14 caniuse-lite: 1.0.30001431 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - styled-jsx: 5.1.0_react@18.2.0 + styled-jsx: 5.1.1_react@18.2.0 optionalDependencies: - '@next/swc-android-arm-eabi': 13.0.8-canary.0 - '@next/swc-android-arm64': 13.0.8-canary.0 - '@next/swc-darwin-arm64': 13.0.8-canary.0 - '@next/swc-darwin-x64': 13.0.8-canary.0 - '@next/swc-freebsd-x64': 13.0.8-canary.0 - '@next/swc-linux-arm-gnueabihf': 13.0.8-canary.0 - '@next/swc-linux-arm64-gnu': 13.0.8-canary.0 - '@next/swc-linux-arm64-musl': 13.0.8-canary.0 - '@next/swc-linux-x64-gnu': 13.0.8-canary.0 - '@next/swc-linux-x64-musl': 13.0.8-canary.0 - '@next/swc-win32-arm64-msvc': 13.0.8-canary.0 - '@next/swc-win32-ia32-msvc': 13.0.8-canary.0 - '@next/swc-win32-x64-msvc': 13.0.8-canary.0 + '@next/swc-android-arm-eabi': 13.1.1-canary.1 + '@next/swc-android-arm64': 13.1.1-canary.1 + '@next/swc-darwin-arm64': 13.1.1-canary.1 + '@next/swc-darwin-x64': 13.1.1-canary.1 + '@next/swc-freebsd-x64': 13.1.1-canary.1 + '@next/swc-linux-arm-gnueabihf': 13.1.1-canary.1 + '@next/swc-linux-arm64-gnu': 13.1.1-canary.1 + '@next/swc-linux-arm64-musl': 13.1.1-canary.1 + '@next/swc-linux-x64-gnu': 13.1.1-canary.1 + '@next/swc-linux-x64-musl': 13.1.1-canary.1 + '@next/swc-win32-arm64-msvc': 13.1.1-canary.1 + '@next/swc-win32-ia32-msvc': 13.1.1-canary.1 + '@next/swc-win32-x64-msvc': 13.1.1-canary.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -6589,8 +6603,8 @@ packages: inline-style-parser: 0.1.1 dev: false - /styled-jsx/5.1.0_react@18.2.0: - resolution: {integrity: sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==} + /styled-jsx/5.1.1_react@18.2.0: + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} peerDependencies: '@babel/core': '*' diff --git a/src/types/next-auth.d.ts b/src/types/next-auth.d.ts index f1fbc69f..11dc967f 100644 --- a/src/types/next-auth.d.ts +++ b/src/types/next-auth.d.ts @@ -24,6 +24,5 @@ declare module "next-auth" { email?: string | null role?: string | null id: UserId - displayName?: string | null } }