Style improvements, re-enable themes, bump next

This commit is contained in:
Max Leiter 2022-12-25 20:00:26 -08:00
parent e41dc292b8
commit b848aa9e40
36 changed files with 385 additions and 425 deletions

View file

@ -55,7 +55,6 @@ const Auth = ({
message: res.error
})
} else {
console.log("res", res)
startTransition(() => {
router.push("/new")
router.refresh()

View file

@ -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 = {

View file

@ -311,7 +311,7 @@ const Post = ({
enableTabLoop={false}
minDate={new Date()}
/>
<ButtonDropdown iconHeight={40}>
<ButtonDropdown>
<Button
height={40}
width={251}

View file

@ -13,19 +13,22 @@ export const PostButtons = ({
files,
loading,
postId,
parentId
parentId,
}: {
title: string
files?: Pick<PostWithFiles, "files">["files"]
loading?: boolean
postId?: string
parentId?: string
visibility?: string
authorId?: string
}) => {
const router = useRouter()
const downloadClick = async () => {
if (!files?.length) return
const downloadZip = (await import("client-zip")).downloadZip
const blob = await downloadZip(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
files.map((file: any) => {
return {
name: file.title,
@ -52,11 +55,7 @@ export const PostButtons = ({
return (
<span className={styles.buttons}>
<ButtonGroup verticalIfMobile>
<Button
iconLeft={<Edit />}
onClick={editACopy}
style={{ textTransform: "none" }}
>
<Button iconLeft={<Edit />} onClick={editACopy}>
Edit a Copy
</Button>
{parentId && (
@ -64,11 +63,7 @@ export const PostButtons = ({
View Parent
</Button>
)}
<Button
onClick={downloadClick}
iconLeft={<Archive />}
style={{ textTransform: "none" }}
>
<Button onClick={downloadClick} iconLeft={<Archive />}>
Download as ZIP Archive
</Button>
<FileDropdown loading={loading} files={files || []} />

View file

@ -34,7 +34,9 @@ export const PostTitle = ({
{title}{" "}
<span style={{ color: "var(--gray)" }}>
by{" "}
<Link href={`/author/${authorId}`}>{displayName || "anonymous"}</Link>
<Link colored href={`/author/${authorId}`}>
{displayName || "anonymous"}
</Link>
</span>
</h1>
{!loading && (

View file

@ -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;

View file

@ -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<PostWithFilesAndAuthor>(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 <DocumentComponent skeleton={true} initialTab={"preview"} />
}
if (isProtected) {
return <PasswordModalPage setPost={setPost} postId={post.id} />
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 (
<PasswordModalPage
authorId={post.authorId}
setPost={setPost}
postId={post.id}
/>
)
}
return (

View file

@ -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 (
<PasswordModal
creating={false}

View file

@ -68,7 +68,7 @@ const Document = ({ skeleton, ...props }: Props) => {
<Skeleton width={"100%"} height={36} />
</div>
<div className={styles.documentContainer}>
<Skeleton width={145} height={36} borderRadius={"4px 4px 0 0"} />
<Skeleton width={175} height={36} borderRadius={"4px 4px 0 0"} />
<Skeleton
width={"100%"}
height={350}

View file

@ -11,6 +11,7 @@
}
.controls {
height: 40px;
display: flex;
justify-content: flex-end;
}

View file

@ -1,111 +1,10 @@
import { notFound, redirect } from "next/navigation"
import { getPostById, Post, PostWithFilesAndAuthor } from "@lib/server/prisma"
import { getCurrentUser } from "@lib/server/session"
import { PostWithFilesAndAuthor } from "@lib/server/prisma"
import ScrollToTop from "@components/scroll-to-top"
import { title } from "process"
import { PostButtons } from "./components/header/post-buttons"
import styles from "./layout.module.css"
import { PostTitle } from "./components/header/title"
import VisibilityControl from "@components/badges/visibility-control"
export type PostProps = {
post: Post
isProtected?: boolean
}
// export async function generateStaticParams() {
// const posts = await getAllPosts({
// where: {
// visibility: {
// equals: "public"
// }
// }
// })
// return posts.map((post) => ({
// 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 (
<div className={styles.root}>
<div className={styles.header}>
<PostButtons
parentId={post.parentId || undefined}
postId={post.id}
files={post.files}
title={title}
/>
<PostTitle
title={post.title}
createdAt={post.createdAt.toString()}
expiresAt={post.expiresAt?.toString()}
// displayName is an optional param
displayName={post.author?.displayName || undefined}
visibility={post.visibility}
authorId={post.authorId}
/>
{/* post.title is not set when the post is protected */}
{post.title && (
<PostButtons
parentId={post.parentId || undefined}
postId={post.id}
files={post.files}
title={title}
authorId={post.authorId}
visibility={post.visibility}
/>
)}
{post.title && (
<PostTitle
title={post.title}
createdAt={post.createdAt.toString()}
expiresAt={post.expiresAt?.toString()}
// displayName is an optional param
displayName={post.author?.displayName || undefined}
visibility={post.visibility}
authorId={post.authorId}
/>
)}
</div>
{post.description && (
<div>
<p>{post.description}</p>
</div>
)}
{isAuthor && (
<span className={styles.controls}>
<VisibilityControl postId={post.id} visibility={post.visibility} />
</span>
)}
<ScrollToTop />
{children}
</div>

View file

@ -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 (
<>
<div className={styles.header}>
<PostButtons loading title="" />
<PostTitle title="" loading />
<DocumentComponent skeleton initialTab="preview" />
</div>
</>

View file

@ -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 <PostFiles post={stringifiedPost} isAuthor={isAuthor} />
return (
<>
<PostFiles post={stringifiedPost} />
<VisibilityControl
authorId={post.authorId}
postId={post.id}
visibility={post.visibility}
/>
</>
)
}

View file

@ -43,10 +43,10 @@ export default async function UserPage({
return (
<Image
src={user.image}
alt="User avatar"
className="w-12 h-12 rounded-full"
alt=""
width={48}
height={48}
style={{ borderRadius: "50%", height: 32, width: 32 }}
/>
)
}

View file

@ -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 = ({
<Tooltip
content={`${expirationDate.toLocaleDateString()} ${expirationDate.toLocaleTimeString()}`}
>
<>{isExpired ? "Expired" : `Expires ${timeUntilString}`}</>
<span suppressHydrationWarning>
{isExpired ? "Expired" : `Expires ${timeUntilString}`}
</span>
</Tooltip>
</Badge>
)

View file

@ -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<string>(postVisibility)
const [isSubmitting, setSubmitting] = useState<string | null>()
@ -66,9 +70,16 @@ const VisibilityControl = ({ postId, visibility: postVisibility }: Props) => {
const submitPassword = (password: string) => onSubmit("protected", password)
if (!isAuthor) {
return null
}
return (
<>
<ButtonGroup verticalIfMobile>
<ButtonGroup style={{
maxWidth: 600,
margin: "var(--gap) auto",
}}>
<Button
disabled={visibility === "private"}
onClick={() => onSubmit("private")}

View file

@ -5,10 +5,7 @@ import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
import { ArrowDown } from "react-feather"
type Props = {
type?: "primary" | "secondary"
loading?: boolean
disabled?: boolean
className?: string
iconHeight?: number
height?: number | string
}
type Attrs = Omit<React.HTMLAttributes<any>, keyof Props>
@ -16,14 +13,14 @@ type ButtonDropdownProps = Props & Attrs
const ButtonDropdown: React.FC<
React.PropsWithChildren<ButtonDropdownProps>
> = ({ type, className, disabled, loading, iconHeight = 24, ...props }) => {
> = ({ type, ...props }) => {
if (!Array.isArray(props.children)) {
return null
}
return (
<DropdownMenu.Root>
<div className={styles.dropdown}>
<div className={styles.dropdown} style={{ height: props.height }}>
{props.children[0]}
<DropdownMenu.Trigger
style={{

View file

@ -9,7 +9,7 @@
.card .content {
padding: var(--gap-half);
padding: var(--gap);
width: 100%;
height: auto;
}

View file

@ -7,7 +7,7 @@ import { usePathname } from "next/navigation"
import { signOut, useSession } from "next-auth/react"
import Button from "@components/button"
import clsx from "clsx"
import { useTheme } from "@wits/next-themes"
import { useTheme } from "next-themes"
import {
GitHub,
Home,

View file

@ -18,8 +18,8 @@ const PasswordModal = ({
onSubmit: onSubmitAfterVerify,
creating
}: Props) => {
const [password, setPassword] = useState<string>()
const [confirmPassword, setConfirmPassword] = useState<string>()
const [password, setPassword] = useState<string>("")
const [confirmPassword, setConfirmPassword] = useState<string>("")
const [error, setError] = useState<string>()
const onSubmit = () => {
@ -60,6 +60,12 @@ const PasswordModal = ({
: "Enter the password to access the post"}
</Dialog.Description>
<fieldset className={styles.fieldset}>
{!error && creating && (
<Note type="warning">
This doesn&apos;t protect your post from the server
administrator.
</Note>
)}
{error && <Note type="error">{error}</Note>}
<Input
width={"100%"}
@ -79,15 +85,11 @@ const PasswordModal = ({
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
/>
)}
{!error && creating && (
<Note type="warning">
This doesn&apos;t protect your post from the server
administrator.
</Note>
)}
</fieldset>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onSubmit}>Submit</Button>
<footer className={styles.footer}>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onSubmit}>Submit</Button>
</footer>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>

View file

@ -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;

View file

@ -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}
/>
)
})}

View file

@ -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 {

View file

@ -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 = ({
<Card style={{ overflowY: "scroll" }}>
<>
<div className={styles.title}>
<h3 style={{ display: "inline-block", margin: 0 }}>
<Link
colored
style={{ marginRight: "var(--gap)" }}
href={`/post/${post.id}`}
>
{post.title}
</Link>
</h3>
{isOwner && (
<span className={styles.buttons}>
{post.parentId && (
<Tooltip content={"View parent"}>
<Button
iconRight={<ArrowUpCircle />}
onClick={viewParentClick}
height={38}
/>
</Tooltip>
)}
<Tooltip content={"Make a copy"}>
<span className={styles.titleText}>
<h3 style={{ display: "inline-block", margin: 0 }}>
<Link
colored
style={{ marginRight: "var(--gap)" }}
href={`/post/${post.id}`}
>
{post.title}
</Link>
</h3>
<div className={styles.badges}>
<VisibilityBadge visibility={post.visibility} />
<Badge type="secondary">
{post.files?.length === 1
? "1 file"
: `${post.files?.length || 0} files`}
</Badge>
<CreatedAgoBadge createdAt={post.createdAt} />
<ExpirationBadge postExpirationDate={post.expiresAt} />
</div>
</span>
{!hideActions ? <span className={styles.buttons}>
{post.parentId && (
<Tooltip content={"View parent"}>
<Button
iconRight={<Edit />}
onClick={editACopy}
iconRight={<ArrowUpCircle />}
onClick={viewParentClick}
height={38}
/>
</Tooltip>
)}
<Tooltip content={"Make a copy"}>
<Button
iconRight={<Edit />}
onClick={editACopy}
height={38}
/>
</Tooltip>
{isOwner && (
<Tooltip content={"Delete"}>
<Button
iconRight={<Trash />}
@ -103,35 +117,28 @@ const ListItem = ({
height={38}
/>
</Tooltip>
</span>
)}
)}
</span> : null}
</div>
{post.description && (
<p className={styles.oneline}>{post.description}</p>
)}
<div className={styles.badges}>
<VisibilityBadge visibility={post.visibility} />
<Badge type="secondary">
{post.files?.length === 1
? "1 file"
: `${post.files?.length || 0} files`}
</Badge>
<CreatedAgoBadge createdAt={post.createdAt} />
<ExpirationBadge postExpirationDate={post.expiresAt} />
</div>
</>
<ul className={styles.files}>
{post?.files?.map(
(file: Pick<PostWithFiles, "files">["files"][0]) => {
return (
<li key={file.id}>
<Link colored href={`/post/${post.id}#${file.title}`} style={{
display: "flex",
alignItems: "center"
}}>
`` {getIconFromFilename(file.title)}
<Link
colored
href={`/post/${post.id}#${file.title}`}
style={{
display: "flex",
alignItems: "center"
}}
>
{getIconFromFilename(file.title)}
{file.title || "Untitled file"}
</Link>
</li>

View file

@ -8,12 +8,6 @@
padding: 0.5rem 0;
}
.container ul li::before {
content: "";
padding: 0;
margin: 0;
}
.postHeader {
display: flex;
justify-content: space-between;

View file

@ -25,6 +25,7 @@ export default async function Mine() {
userId={userId}
morePosts={hasMore}
initialPosts={stringifiedPosts}
isOwner={true}
/>
)
}

View file

@ -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 (
<div
@ -79,9 +84,10 @@ async function PublicPostList({
userId={undefined}
morePosts={false}
initialPosts={JSON.stringify(posts)}
hideActions
hideSearch
/>
)
}
export const revalidate = 30
export const revalidate = 60

View file

@ -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 (
<SessionProvider>
<RadixTooltip.Provider delayDuration={200}>
{children}
<ThemeProvider enableSystem defaultTheme="dark">
{children}
</ThemeProvider>
</RadixTooltip.Provider>
</SessionProvider>
)

View file

@ -10,9 +10,8 @@ import styles from "./profile.module.css"
const Profile = () => {
const { data: session } = useSession()
const [name, setName] = useState<string>(session?.user.displayName || "")
const [name, setName] = useState<string>(session?.user.name || "")
const [submitting, setSubmitting] = useState<boolean>(false)
const { setToast } = useToasts()
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {

View file

@ -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;

View file

@ -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"]
}
}

View file

@ -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",

View file

@ -61,14 +61,14 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse<unknown>) {
...post
})
} else {
return {
return res.json({
isProtected: true,
post: {
id: post.id,
visibility: post.visibility,
title: post.title
authorId: post.authorId,
}
}
})
}
}

View file

@ -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" })

View file

@ -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': '*'

View file

@ -24,6 +24,5 @@ declare module "next-auth" {
email?: string | null
role?: string | null
id: UserId
displayName?: string | null
}
}