Page/layout optimizations, bump next, styling fixes
This commit is contained in:
parent
f034f29a1d
commit
ff310a67b9
28 changed files with 431 additions and 247 deletions
|
@ -17,6 +17,7 @@ import Button from "@components/button"
|
||||||
import Input from "@components/input"
|
import Input from "@components/input"
|
||||||
import ButtonDropdown from "@components/button-dropdown"
|
import ButtonDropdown from "@components/button-dropdown"
|
||||||
import { useToasts } from "@components/toasts"
|
import { useToasts } from "@components/toasts"
|
||||||
|
import { useSession } from "next-auth/react"
|
||||||
|
|
||||||
const emptyDoc = {
|
const emptyDoc = {
|
||||||
title: "",
|
title: "",
|
||||||
|
@ -37,6 +38,8 @@ const Post = ({
|
||||||
initialPost?: string
|
initialPost?: string
|
||||||
newPostParent?: string
|
newPostParent?: string
|
||||||
}) => {
|
}) => {
|
||||||
|
const session = useSession()
|
||||||
|
|
||||||
const parsedPost = JSON.parse(stringifiedInitialPost || "{}")
|
const parsedPost = JSON.parse(stringifiedInitialPost || "{}")
|
||||||
const initialPost = parsedPost?.id ? parsedPost : null
|
const initialPost = parsedPost?.id ? parsedPost : null
|
||||||
const { setToast } = useToasts()
|
const { setToast } = useToasts()
|
||||||
|
@ -140,6 +143,7 @@ const Post = ({
|
||||||
type: "error"
|
type: "error"
|
||||||
})
|
})
|
||||||
hasErrored = true
|
hasErrored = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +186,11 @@ const Post = ({
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (session.status === "unauthenticated") {
|
||||||
|
router.push("/login")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const updateDocTitle = (i: number) => (title: string) => {
|
const updateDocTitle = (i: number) => (title: string) => {
|
||||||
setDocs((docs) =>
|
setDocs((docs) =>
|
||||||
docs.map((doc, index) => (i === index ? { ...doc, title } : doc))
|
docs.map((doc, index) => (i === index ? { ...doc, title } : doc))
|
||||||
|
@ -265,7 +274,6 @@ const Post = ({
|
||||||
/>
|
/>
|
||||||
<div className={styles.buttons}>
|
<div className={styles.buttons}>
|
||||||
<Button
|
<Button
|
||||||
className={styles.button}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDocs([
|
setDocs([
|
||||||
...docs,
|
...docs,
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: var(--gap-double);
|
|
||||||
gap: var(--gap);
|
gap: var(--gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,16 @@ const NewFromExisting = async ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const post = await getPostById(id, {
|
const post = await getPostById(id, {
|
||||||
withFiles: true,
|
include: {
|
||||||
withAuthor: false
|
files: true,
|
||||||
|
author: false
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
authorId: true,
|
||||||
|
title: true,
|
||||||
|
description: true,
|
||||||
|
id: true,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const serialized = JSON.stringify(post)
|
const serialized = JSON.stringify(post)
|
||||||
|
|
|
@ -2,10 +2,5 @@ import { getCurrentUser } from "@lib/server/session"
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
|
|
||||||
export default function NewLayout({ children }: { children: React.ReactNode }) {
|
export default function NewLayout({ children }: { children: React.ReactNode }) {
|
||||||
const user = getCurrentUser()
|
|
||||||
if (!user) {
|
|
||||||
return redirect("/new")
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{children}</>
|
return <>{children}</>
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,3 +4,5 @@ import "./react-datepicker.css"
|
||||||
const New = () => <NewPost />
|
const New = () => <NewPost />
|
||||||
|
|
||||||
export default New
|
export default New
|
||||||
|
|
||||||
|
export const dynamic = 'force-static'
|
||||||
|
|
|
@ -14,16 +14,27 @@ type Props = {
|
||||||
isAuthor?: boolean
|
isAuthor?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const PostPage = ({ post: initialPost, isProtected, isAuthor: isAuthorFromServer }: Props) => {
|
const PostFiles = ({
|
||||||
const { data: session } = useSession()
|
post: _initialPost,
|
||||||
const [post, setPost] = useState<PostWithFilesAndAuthor>(
|
isAuthor: isAuthorFromServer
|
||||||
typeof initialPost === "string" ? JSON.parse(initialPost) : initialPost
|
}: Props) => {
|
||||||
)
|
console.log("PostFiles")
|
||||||
|
const { data: session, status } = useSession()
|
||||||
|
const isLoading = status === "loading"
|
||||||
|
const initialPost =
|
||||||
|
typeof _initialPost === "string"
|
||||||
|
? (JSON.parse(_initialPost) as PostWithFilesAndAuthor)
|
||||||
|
: _initialPost
|
||||||
|
const [post, setPost] = useState<PostWithFilesAndAuthor>(initialPost)
|
||||||
|
const isProtected = initialPost.visibility === "protected"
|
||||||
|
console.log(_initialPost, post)
|
||||||
|
|
||||||
// We generate public and unlisted posts at build time, so we can't use
|
// 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 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.
|
// the post's authorId against the session's user id.
|
||||||
const isAuthor = isAuthorFromServer ? true : session?.user?.id === post?.authorId;
|
const isAuthor = isAuthorFromServer
|
||||||
|
? true
|
||||||
|
: session?.user?.id === post?.authorId
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -58,6 +69,15 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor: isAuthorFromServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [isAuthor, post.expiresAt, router])
|
}, [isAuthor, post.expiresAt, router])
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<DocumentComponent
|
||||||
|
skeleton={true}
|
||||||
|
initialTab={"preview"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isProtected) {
|
if (isProtected) {
|
||||||
return <PasswordModalPage setPost={setPost} postId={post.id} />
|
return <PasswordModalPage setPost={setPost} postId={post.id} />
|
||||||
|
@ -67,6 +87,7 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor: isAuthorFromServer
|
||||||
<>
|
<>
|
||||||
{post.files?.map(({ id, content, title, html }: File) => (
|
{post.files?.map(({ id, content, title, html }: File) => (
|
||||||
<DocumentComponent
|
<DocumentComponent
|
||||||
|
skeleton={false}
|
||||||
key={id}
|
key={id}
|
||||||
title={title}
|
title={title}
|
||||||
initialTab={"preview"}
|
initialTab={"preview"}
|
||||||
|
@ -79,4 +100,4 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor: isAuthorFromServer
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PostPage
|
export default PostFiles
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
import { memo } from "react"
|
import { memo } from "react"
|
||||||
import styles from "./document.module.css"
|
import styles from "./document.module.css"
|
||||||
import Skeleton from "@components/skeleton"
|
import Skeleton from "@components/skeleton"
|
||||||
|
@ -10,16 +12,24 @@ import DocumentTabs from "app/(posts)/components/tabs"
|
||||||
import Input from "@components/input"
|
import Input from "@components/input"
|
||||||
import { Download, ExternalLink } from "react-feather"
|
import { Download, ExternalLink } from "react-feather"
|
||||||
|
|
||||||
// import Link from "next/link"
|
type SharedProps = {
|
||||||
type Props = {
|
title?: string
|
||||||
title: string
|
initialTab: "edit" | "preview"
|
||||||
initialTab?: "edit" | "preview"
|
id?: string
|
||||||
skeleton?: boolean
|
content?: string
|
||||||
id: string
|
preview?: string
|
||||||
content: string
|
|
||||||
preview: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Props = (
|
||||||
|
| {
|
||||||
|
skeleton?: true
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
skeleton?: false
|
||||||
|
}
|
||||||
|
) &
|
||||||
|
SharedProps
|
||||||
|
|
||||||
const DownloadButtons = ({ rawLink }: { rawLink?: string }) => {
|
const DownloadButtons = ({ rawLink }: { rawLink?: string }) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.actionWrapper}>
|
<div className={styles.actionWrapper}>
|
||||||
|
@ -51,32 +61,29 @@ const DownloadButtons = ({ rawLink }: { rawLink?: string }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Document = ({
|
const Document = ({ skeleton, ...props }: Props) => {
|
||||||
content,
|
|
||||||
preview,
|
|
||||||
title,
|
|
||||||
initialTab = "edit",
|
|
||||||
skeleton,
|
|
||||||
id
|
|
||||||
}: Props) => {
|
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<div className={styles.fileNameContainer}>
|
<div className={styles.fileNameContainer}>
|
||||||
<Skeleton width={275} height={36} />
|
<Skeleton width={'100%'} height={36} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.descriptionContainer}>
|
<div className={styles.descriptionContainer}>
|
||||||
<div style={{ flexDirection: "row", display: "flex" }}>
|
<Skeleton width={145} height={36} borderRadius={"4px 4px 0 0"} />
|
||||||
<Skeleton width={125} height={36} />
|
<Skeleton
|
||||||
</div>
|
width={"100%"}
|
||||||
<Skeleton width={"100%"} height={350} />
|
height={350}
|
||||||
|
borderRadius={"0 0 4px 4px"}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { title, initialTab, id, content, preview } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
|
@ -8,7 +8,12 @@ export default async function Head({
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
const post = await getPostById(params.id)
|
const post = await getPostById(params.id, {
|
||||||
|
select: {
|
||||||
|
title: true,
|
||||||
|
description: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
.root {
|
.root {
|
||||||
padding-bottom: 200px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
159
src/app/(posts)/post/[id]/layout.tsx
Normal file
159
src/app/(posts)/post/[id]/layout.tsx
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
import { notFound, redirect } from "next/navigation"
|
||||||
|
import {
|
||||||
|
getPostById,
|
||||||
|
Post,
|
||||||
|
PostWithFilesAndAuthor
|
||||||
|
} from "@lib/server/prisma"
|
||||||
|
import { getCurrentUser } from "@lib/server/session"
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function PostLayout({
|
||||||
|
children,
|
||||||
|
params
|
||||||
|
}: {
|
||||||
|
params: {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const { post, isAuthor } = await getPost(params.id)
|
||||||
|
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { PostButtons } from "./components/header/post-buttons"
|
import { PostButtons } from "./components/header/post-buttons"
|
||||||
import { PostTitle } from "./components/header/title"
|
import { PostTitle } from "./components/header/title"
|
||||||
import styles from "./styles.module.css"
|
import DocumentComponent from "./components/post-files/view-document"
|
||||||
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
export default function PostLoading() {
|
export default function PostLoading() {
|
||||||
return (
|
return (
|
||||||
|
@ -8,6 +9,7 @@ export default function PostLoading() {
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<PostButtons loading title="" />
|
<PostButtons loading title="" />
|
||||||
<PostTitle title="" loading />
|
<PostTitle title="" loading />
|
||||||
|
<DocumentComponent skeleton initialTab="preview" />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,55 +1,59 @@
|
||||||
import PostPage from "./components/post-page"
|
import { getPostById } from "@lib/server/prisma"
|
||||||
import { notFound, redirect } from "next/navigation"
|
|
||||||
import { getAllPosts, getPostById, Post, PostWithFilesAndAuthor } from "@lib/server/prisma"
|
|
||||||
import { getCurrentUser } from "@lib/server/session"
|
import { getCurrentUser } from "@lib/server/session"
|
||||||
import ScrollToTop from "@components/scroll-to-top"
|
import { notFound, redirect } from "next/navigation"
|
||||||
import { title } from "process"
|
import PostFiles from "./components/post-files"
|
||||||
import { PostButtons } from "./components/header/post-buttons"
|
|
||||||
import styles from "./styles.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 fetchOptions = {
|
|
||||||
withFiles: true,
|
|
||||||
withAuthor: true
|
|
||||||
}
|
|
||||||
|
|
||||||
const getPost = async (id: string) => {
|
const getPost = async (id: string) => {
|
||||||
const post = (await getPostById(id, fetchOptions)) as PostWithFilesAndAuthor
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).then((post) => {
|
||||||
|
if (!post) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
|
return post
|
||||||
|
}),
|
||||||
|
getCurrentUser()
|
||||||
|
])
|
||||||
|
|
||||||
if (!post) {
|
const isAuthorOrAdmin = user?.id === post?.authorId || user?.role === "admin"
|
||||||
return notFound()
|
|
||||||
|
// if expired
|
||||||
|
if (post.expiresAt && !isAuthorOrAdmin) {
|
||||||
|
const expirationDate = new Date(post.expiresAt)
|
||||||
|
if (expirationDate < new Date()) {
|
||||||
|
return redirect("/expired")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (post.visibility === "public" || post.visibility === "unlisted") {
|
if (post.visibility === "public" || post.visibility === "unlisted") {
|
||||||
return { post }
|
return { post, isAuthorOrAdmin }
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await getCurrentUser()
|
|
||||||
const isAuthorOrAdmin = user?.id === post?.authorId || user?.role === "admin"
|
|
||||||
|
|
||||||
|
|
||||||
if (post.visibility === "private" && !isAuthorOrAdmin) {
|
if (post.visibility === "private" && !isAuthorOrAdmin) {
|
||||||
return redirect("/signin")
|
return redirect("/signin")
|
||||||
}
|
}
|
||||||
|
@ -71,70 +75,21 @@ const getPost = async (id: string) => {
|
||||||
description: "",
|
description: "",
|
||||||
authorId: ""
|
authorId: ""
|
||||||
},
|
},
|
||||||
isProtected: true,
|
|
||||||
isAuthor: isAuthorOrAdmin
|
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 }
|
return { post, isAuthor: isAuthorOrAdmin }
|
||||||
}
|
}
|
||||||
|
|
||||||
const PostView = async ({
|
export default async function PostPage({
|
||||||
params
|
params
|
||||||
}: {
|
}: {
|
||||||
params: {
|
params: {
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
}) => {
|
}) {
|
||||||
const { post, isProtected, isAuthor } = await getPost(params.id)
|
const { post, isAuthor } = await getPost(params.id)
|
||||||
// TODO: serialize dates in prisma middleware instead of passing as JSON
|
|
||||||
const stringifiedPost = JSON.stringify(post)
|
const stringifiedPost = JSON.stringify(post)
|
||||||
|
return <PostFiles post={stringifiedPost} isAuthor={isAuthor} />
|
||||||
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}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{post.description && (
|
|
||||||
<div>
|
|
||||||
<p>{post.description}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<PostPage
|
|
||||||
isAuthor={isAuthor}
|
|
||||||
isProtected={isProtected}
|
|
||||||
post={stringifiedPost}
|
|
||||||
/>
|
|
||||||
{isAuthor && (
|
|
||||||
<span className={styles.controls}>
|
|
||||||
<VisibilityControl postId={post.id} visibility={post.visibility} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<ScrollToTop />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PostView
|
|
||||||
|
|
|
@ -41,7 +41,11 @@ const Button = forwardRef<HTMLButtonElement, Props>(
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={`${styles.button} ${styles[type]} ${className || ""}`}
|
className={clsx(
|
||||||
|
styles.button,
|
||||||
|
type === "primary" || (type === "secondary" && styles[type]),
|
||||||
|
className
|
||||||
|
)}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
style={{ height, width, margin, padding }}
|
style={{ height, width, margin, padding }}
|
||||||
|
|
|
@ -5,7 +5,7 @@ export default function Card({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children?: React.ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
} & React.ComponentProps<"div">) {
|
} & React.ComponentProps<"div">) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 24px;
|
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
"use client"
|
|
||||||
import Card from "@components/card"
|
import Card from "@components/card"
|
||||||
import styles from "./settings-group.module.css"
|
import styles from "./settings-group.module.css"
|
||||||
|
|
||||||
type Props = {
|
type Props =
|
||||||
title: string
|
| {
|
||||||
children: React.ReactNode | React.ReactNode[]
|
skeleton: true
|
||||||
}
|
title?: string
|
||||||
|
children?: React.ReactNode
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
skeleton?: false
|
||||||
|
title: string
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const SettingsGroup = ({ title, children, skeleton }: Props) => {
|
||||||
|
if (skeleton) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
height: 365
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const SettingsGroup = ({ title, children }: Props) => {
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<h4>{title}</h4>
|
<h4>{title}</h4>
|
||||||
|
|
|
@ -2,10 +2,12 @@ import styles from "./skeleton.module.css"
|
||||||
|
|
||||||
export default function Skeleton({
|
export default function Skeleton({
|
||||||
width = 100,
|
width = 100,
|
||||||
height = 24
|
height = 24,
|
||||||
|
borderRadius = 4,
|
||||||
}: {
|
}: {
|
||||||
width?: number | string
|
width?: number | string
|
||||||
height?: number | string
|
height?: number | string,
|
||||||
|
borderRadius?: number | string
|
||||||
}) {
|
}) {
|
||||||
return <div className={styles.skeleton} style={{ width, height }} />
|
return <div className={styles.skeleton} style={{ width, height, borderRadius }} />
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import styles from "./profile.module.css"
|
||||||
const Profile = ({ user }: { user: User }) => {
|
const Profile = ({ user }: { user: User }) => {
|
||||||
// TODO: make this displayName, requires fetching user from DB as session doesnt have it
|
// TODO: make this displayName, requires fetching user from DB as session doesnt have it
|
||||||
const [name, setName] = useState<string>(user.name || "")
|
const [name, setName] = useState<string>(user.name || "")
|
||||||
const [bio, setBio] = useState<string>()
|
const [submitting, setSubmitting] = useState<boolean>(false)
|
||||||
|
|
||||||
const { setToast } = useToasts()
|
const { setToast } = useToasts()
|
||||||
|
|
||||||
|
@ -19,23 +19,19 @@ const Profile = ({ user }: { user: User }) => {
|
||||||
setName(e.target.value)
|
setName(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBioChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
setBio(e.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!name && !bio) {
|
if (!name) {
|
||||||
setToast({
|
setToast({
|
||||||
message: "Please fill out at least one field",
|
message: "Please fill out at least one field",
|
||||||
type: "error"
|
type: "error"
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
setSubmitting(true)
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
displayName: name,
|
displayName: name,
|
||||||
bio
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(`/api/user/${user.id}`, {
|
const res = await fetch(`/api/user/${user.id}`, {
|
||||||
|
@ -46,6 +42,8 @@ const Profile = ({ user }: { user: User }) => {
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setSubmitting(false)
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
setToast({
|
setToast({
|
||||||
message: "Profile updated",
|
message: "Profile updated",
|
||||||
|
@ -90,7 +88,7 @@ const Profile = ({ user }: { user: User }) => {
|
||||||
aria-label="Email"
|
aria-label="Email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit">Submit</Button>
|
<Button type="submit" loading={submitting}>Submit</Button>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
21
src/app/settings/layout.tsx
Normal file
21
src/app/settings/layout.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export default function SettingsLayout({
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Settings</h1>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "var(--gap)",
|
||||||
|
marginBottom: "var(--gap)"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
5
src/app/settings/loading.tsx
Normal file
5
src/app/settings/loading.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import SettingsGroup from "@components/settings-group"
|
||||||
|
|
||||||
|
export default function SettingsLoading() {
|
||||||
|
return <SettingsGroup skeleton />
|
||||||
|
}
|
|
@ -12,21 +12,8 @@ export default async function SettingsPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<SettingsGroup title="Profile">
|
||||||
style={{
|
<Profile user={user} />
|
||||||
display: "flex",
|
</SettingsGroup>
|
||||||
flexDirection: "column",
|
|
||||||
gap: "var(--gap)",
|
|
||||||
marginBottom: "var(--gap)"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h1>Settings</h1>
|
|
||||||
<SettingsGroup title="Profile">
|
|
||||||
<Profile user={user} />
|
|
||||||
</SettingsGroup>
|
|
||||||
{/* <SettingsGroup title="Password">
|
|
||||||
<Password />
|
|
||||||
</SettingsGroup> */}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,10 +138,11 @@ export const createUser = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetPostByIdOptions = {
|
// all of prisma.post.findUnique
|
||||||
withFiles: boolean
|
type GetPostByIdOptions = Pick<
|
||||||
withAuthor: boolean
|
Prisma.PostFindUniqueArgs,
|
||||||
}
|
"include" | "rejectOnNotFound" | "select"
|
||||||
|
>
|
||||||
|
|
||||||
export const getPostById = async (
|
export const getPostById = async (
|
||||||
postId: Post["id"],
|
postId: Post["id"],
|
||||||
|
@ -151,17 +152,7 @@ export const getPostById = async (
|
||||||
where: {
|
where: {
|
||||||
id: postId
|
id: postId
|
||||||
},
|
},
|
||||||
include: {
|
...options
|
||||||
files: options?.withFiles,
|
|
||||||
author: options?.withAuthor
|
|
||||||
? {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
displayName: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return post
|
return post
|
||||||
|
|
|
@ -5,7 +5,8 @@ const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
experimental: {
|
experimental: {
|
||||||
// esmExternals: true,
|
// esmExternals: true,
|
||||||
appDir: true
|
appDir: true,
|
||||||
|
serverComponentsExternalPackages: ['prisma'],
|
||||||
},
|
},
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
"client-zip": "2.2.1",
|
"client-zip": "2.2.1",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"next": "13.0.7-canary.4",
|
"next": "13.0.8-canary.0",
|
||||||
"next-auth": "^4.18.4",
|
"next-auth": "^4.18.4",
|
||||||
"prisma": "^4.7.1",
|
"prisma": "^4.7.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
|
|
@ -23,8 +23,10 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const post = await getPostById(id, {
|
const post = await getPostById(id, {
|
||||||
withFiles: Boolean(files),
|
include: {
|
||||||
withAuthor: true
|
files: true,
|
||||||
|
author: true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ specifiers:
|
||||||
eslint-config-next: 13.0.3
|
eslint-config-next: 13.0.3
|
||||||
jest: ^29.3.1
|
jest: ^29.3.1
|
||||||
lodash.debounce: ^4.0.8
|
lodash.debounce: ^4.0.8
|
||||||
next: 13.0.7-canary.4
|
next: 13.0.8-canary.0
|
||||||
next-auth: ^4.18.4
|
next-auth: ^4.18.4
|
||||||
next-unused: 0.0.6
|
next-unused: 0.0.6
|
||||||
prettier: 2.6.2
|
prettier: 2.6.2
|
||||||
|
@ -56,14 +56,14 @@ dependencies:
|
||||||
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
|
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
|
||||||
'@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
'@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
|
||||||
'@wcj/markdown-to-html': 2.1.2
|
'@wcj/markdown-to-html': 2.1.2
|
||||||
'@wits/next-themes': 0.2.14_cn2wvw6rkm76gy2h2qfs2itv2u
|
'@wits/next-themes': 0.2.14_rhfownvlqkszea7w3lnpwl7bzy
|
||||||
bcrypt: 5.1.0
|
bcrypt: 5.1.0
|
||||||
client-only: 0.0.1
|
client-only: 0.0.1
|
||||||
client-zip: 2.2.1
|
client-zip: 2.2.1
|
||||||
jest: 29.3.1_@types+node@17.0.23
|
jest: 29.3.1_@types+node@17.0.23
|
||||||
lodash.debounce: 4.0.8
|
lodash.debounce: 4.0.8
|
||||||
next: 13.0.7-canary.4_biqbaboplfbrettd7655fr4n2y
|
next: 13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
next-auth: 4.18.4_cn2wvw6rkm76gy2h2qfs2itv2u
|
next-auth: 4.18.4_rhfownvlqkszea7w3lnpwl7bzy
|
||||||
prisma: 4.7.1
|
prisma: 4.7.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
|
||||||
|
@ -800,7 +800,7 @@ packages:
|
||||||
next-auth: ^4
|
next-auth: ^4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/client': 4.7.1_prisma@4.7.1
|
'@prisma/client': 4.7.1_prisma@4.7.1
|
||||||
next-auth: 4.18.4_cn2wvw6rkm76gy2h2qfs2itv2u
|
next-auth: 4.18.4_rhfownvlqkszea7w3lnpwl7bzy
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/bundle-analyzer/13.0.7-canary.4:
|
/@next/bundle-analyzer/13.0.7-canary.4:
|
||||||
|
@ -812,8 +812,8 @@ packages:
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@next/env/13.0.7-canary.4:
|
/@next/env/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-PEFzHZkan5SJtxAL+0TmL4Vx3BJp4tDbCnba/2H3CeW0hVEck0e+UY7UGSXwyBaojD6DwUtgHHN3tu2yd2x51w==}
|
resolution: {integrity: sha512-IiZM9mAUE9F3p9q/ydZBGlvmleOaMO6fBDBJzvQa4t3Ezg5e3NfGlTO01MTWvKPEKYPeAwFp+tcVh9ivA28+Dw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/eslint-plugin-next/13.0.3:
|
/@next/eslint-plugin-next/13.0.3:
|
||||||
|
@ -828,8 +828,8 @@ packages:
|
||||||
glob: 7.1.7
|
glob: 7.1.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@next/swc-android-arm-eabi/13.0.7-canary.4:
|
/@next/swc-android-arm-eabi/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-mXpIUvBXaxYD/tI6FUuRKF0JPs03dBCQAtmZjG7hRISpnFWij1wgm+NewmXEZ7EmWIIstc+vTgP0Akzqfz6abg==}
|
resolution: {integrity: sha512-U6nayRvWuASLLBwqG4nN9540ako+JEBblN8479BpGvW1F2FyQPUx/zq+WO0b47KPyJI2XNPBIenHGvtSY7yN/Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
@ -837,8 +837,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-android-arm64/13.0.7-canary.4:
|
/@next/swc-android-arm64/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-sht0AvPUp4667+QwWLj4zAcTEFoxmAH5sYRddSIrBpMa2fE0FT6P4ZInJ5eSlxrS+Ag9ehRq09kb+y9j8Ci/ZQ==}
|
resolution: {integrity: sha512-GtUW5CCIfN1FUln+pRm0rAWe8k957rcKhYDPGBrfr+jaKvUgjI4NgMcXRJ0R83j+vcM4+DIhIkIO+OYQ1vU4RA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
@ -846,8 +846,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-arm64/13.0.7-canary.4:
|
/@next/swc-darwin-arm64/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-8WhDzOW2byCRde6bgMigqzVE0Uihhg9hicm2IzR81quoLuPU9UBbk7OCFSUg3OqQjjmzo2ZChYq85bouRMjpJg==}
|
resolution: {integrity: sha512-dqUn4ERXHT+g/L+paIi+IhNP3P7HiF95ZBIjQvn++n0IhdT8rRfaQK3ubps/NopL14jHA33J7HnK73vgUBIvwg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
@ -855,8 +855,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-darwin-x64/13.0.7-canary.4:
|
/@next/swc-darwin-x64/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-MWItc0vAwDFpW6kR+aPWhTj2Q0VaqKrWOemv28wv3Wv3kwCxheSzWImOkcGPn5eTnCRfn0Cn2b/+VHQ1tH79tg==}
|
resolution: {integrity: sha512-jGaI2idOd2Z0Dvlnz0WYHC+hbqQPIhaso/upJQebknWeu1VsSrwH5oDbCgMBaXLkHO7rMOITWC5FjxdXjSGK6g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
@ -864,8 +864,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-freebsd-x64/13.0.7-canary.4:
|
/@next/swc-freebsd-x64/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-mFoYF/rEi6Vd1RF6L3rNdYzwXATwqXUmbhY9Brav4JrJ9TQmf8GiZz0itn65J1QDuRw3rD+czKJ/HxwSvCNUMA==}
|
resolution: {integrity: sha512-ieM8XwqX9m/frFGpSwrXubzZYPT+ZzOEJsDgCNo3CD0DGHu8hZz1XLRym0Nl2mZAnBlxgENN+RlGwutWKBQMHg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
@ -873,8 +873,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm-gnueabihf/13.0.7-canary.4:
|
/@next/swc-linux-arm-gnueabihf/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-+uxKr1/BXvZjTeIFnue85pdDxnneD9lCvvfkIjcY2EIToTMOhTtqlyosXblENTL7uM+q26lhKOepRDttG9L2cQ==}
|
resolution: {integrity: sha512-/9CnPhcqu/kudpk07zCkApFRUwF0wbwdFm5CqtguZ6yubqhoAV1Wjmrs1gnt+MUBHsVnKRdoGkz6KupQEZqz7g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -882,8 +882,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-gnu/13.0.7-canary.4:
|
/@next/swc-linux-arm64-gnu/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-WVqK6/jXVjWePvIaOOgNHO6P8LUntvLuay6clhdBzAzArEStG1RRoupAnuqz9VXyFtHguRthejQhFAqYLq9jqw==}
|
resolution: {integrity: sha512-KUQs6KdX3lMxJu4ym/jNzotQvbkpXD/ne8KgjUuzTdgw3LYSfEMsTzORj71IR48H5yMDSLGPvCJA+B8FuVzS8Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -891,8 +891,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-arm64-musl/13.0.7-canary.4:
|
/@next/swc-linux-arm64-musl/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-LEu82umP+iLyP710qYA37UT6FFhcKZ7z9GmitFZxE9Jj6wenOFZHkkFDm624pJyAhfLcbLdT5MYqIgXIuwloDg==}
|
resolution: {integrity: sha512-bisV2RUrsQMJodK2xGszfqK9G/BuDlqVLeDZVrOENWaZnOVDtrP+WlqrN0vS1r8xn/OepJWKkMnibO4aLCruvw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -900,8 +900,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-gnu/13.0.7-canary.4:
|
/@next/swc-linux-x64-gnu/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-ycoLEMKaNM/T/iplgVsjYFpYE7tTh/UNaBxuFnsxBLuQJJET26mCWNPjM7CpiDWZVhdFwD5ad9PI3+HeOnSgSg==}
|
resolution: {integrity: sha512-X8pcTN7nPZ7gDXb04oGWOS/qPvPaPK5x753AmevQgVa7FwqXQ6IkJeD3sr8ergmu6Fcfr6c4LcnBEQzpIxOxYA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -909,8 +909,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-linux-x64-musl/13.0.7-canary.4:
|
/@next/swc-linux-x64-musl/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-xR22ldRvmg0l5bJbuK11iNFXAKS8+cgbkQZeHizYM5ngWOIEz8WQWn01+9sBeydtQZbvySpWHfm/Ev3XFEJHOw==}
|
resolution: {integrity: sha512-Kg+tsnDmQ21qYfLik3YH+ZOYMmoNyhYqLMZE6qSASA5uN448J1cJUHIdpJxUpidZHtWBV+kTVR2Hw7+We+BiKQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
@ -918,8 +918,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-arm64-msvc/13.0.7-canary.4:
|
/@next/swc-win32-arm64-msvc/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-psFkg4qGIx85tqDu9f7vl1jgLumzGsCdVL+OvLWY9opASDNaSYMp0xcwKT1BvGYpY8sGs81Q21yVrGl4sa8vvA==}
|
resolution: {integrity: sha512-tde5+ZQFT0+Pr/BKINQ32+8C/AEaZLzU69AvpD7dvbUEJ5fReIiSBPIL1ov3pZYR+EPwl7wFPoj7NLxTU1E8WA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
@ -927,8 +927,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-ia32-msvc/13.0.7-canary.4:
|
/@next/swc-win32-ia32-msvc/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-4b0snVdqN8wDQMucJDbM759sC1txN/PwtaTn/rgcBaybXBFlYBc01mj3ePWcGcUj0378+FSKZWfo1+ldYBurFw==}
|
resolution: {integrity: sha512-CKs0Os7cDKa9GZANf4HbOgkQodjQ2GtJZBBwdZ7OaFMWmWet/0JCkakaF/+EUl0vx0QP83qpIK8LHEkYXxJItg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
@ -936,8 +936,8 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@next/swc-win32-x64-msvc/13.0.7-canary.4:
|
/@next/swc-win32-x64-msvc/13.0.8-canary.0:
|
||||||
resolution: {integrity: sha512-RbxWU5fswTBBuI2o7hHJ2KgSUAS73pw0RNMI387eGfh7Nmo5QQg9DCUJFXQlxCWUmvC+mvYPwMFWsolYdEd++A==}
|
resolution: {integrity: sha512-DTICRWenuqExpO3WmFzkhvYwKgLuPweb3eWiYeybSwHB6ji/cC5ZQjh3AvGbff548Ye8Z1bf4SUAIjdcg0Y/fA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
@ -1729,14 +1729,14 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@wits/next-themes/0.2.14_cn2wvw6rkm76gy2h2qfs2itv2u:
|
/@wits/next-themes/0.2.14_rhfownvlqkszea7w3lnpwl7bzy:
|
||||||
resolution: {integrity: sha512-fHKb/tRcWbYNblGHZtfvAQztDhzUB9d7ZkYOny0BisSPh6EABcsqxKB48ABUQztcmKywlp2zEMkLcSRj/PQBSw==}
|
resolution: {integrity: sha512-fHKb/tRcWbYNblGHZtfvAQztDhzUB9d7ZkYOny0BisSPh6EABcsqxKB48ABUQztcmKywlp2zEMkLcSRj/PQBSw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
next: '*'
|
next: '*'
|
||||||
react: '*'
|
react: '*'
|
||||||
react-dom: '*'
|
react-dom: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 13.0.7-canary.4_biqbaboplfbrettd7655fr4n2y
|
next: 13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -5229,7 +5229,7 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/next-auth/4.18.4_cn2wvw6rkm76gy2h2qfs2itv2u:
|
/next-auth/4.18.4_rhfownvlqkszea7w3lnpwl7bzy:
|
||||||
resolution: {integrity: sha512-tvXOabxv5U/y6ib56XPkOnc/48tYc+xT6GNOLREIme8WVGYHDTc3CGEfe2+0bVCWAm0ax/GYXH0By5NFoaJDww==}
|
resolution: {integrity: sha512-tvXOabxv5U/y6ib56XPkOnc/48tYc+xT6GNOLREIme8WVGYHDTc3CGEfe2+0bVCWAm0ax/GYXH0By5NFoaJDww==}
|
||||||
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
|
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -5245,7 +5245,7 @@ packages:
|
||||||
'@panva/hkdf': 1.0.2
|
'@panva/hkdf': 1.0.2
|
||||||
cookie: 0.5.0
|
cookie: 0.5.0
|
||||||
jose: 4.11.0
|
jose: 4.11.0
|
||||||
next: 13.0.7-canary.4_biqbaboplfbrettd7655fr4n2y
|
next: 13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y
|
||||||
oauth: 0.9.15
|
oauth: 0.9.15
|
||||||
openid-client: 5.3.0
|
openid-client: 5.3.0
|
||||||
preact: 10.11.2
|
preact: 10.11.2
|
||||||
|
@ -5266,8 +5266,8 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/next/13.0.7-canary.4_biqbaboplfbrettd7655fr4n2y:
|
/next/13.0.8-canary.0_biqbaboplfbrettd7655fr4n2y:
|
||||||
resolution: {integrity: sha512-cx0ST4A/ZYB5eqygzx59cW4/xhVBanPhDr7JytkzZJ/HUCX67VR/ho+rcekB/C/ZNsefYukhMrep6vwfidV95A==}
|
resolution: {integrity: sha512-+LP4KZGBp+97TRgYExChOvoONZY1qfJmtB6IjG2HXDshgYpQmsAPEMy9r0rWbvhOveChCJ6sv+yEFAOCNc4yKQ==}
|
||||||
engines: {node: '>=14.6.0'}
|
engines: {node: '>=14.6.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -5284,7 +5284,7 @@ packages:
|
||||||
sass:
|
sass:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 13.0.7-canary.4
|
'@next/env': 13.0.8-canary.0
|
||||||
'@swc/helpers': 0.4.14
|
'@swc/helpers': 0.4.14
|
||||||
caniuse-lite: 1.0.30001431
|
caniuse-lite: 1.0.30001431
|
||||||
postcss: 8.4.14
|
postcss: 8.4.14
|
||||||
|
@ -5292,19 +5292,19 @@ packages:
|
||||||
react-dom: 18.2.0_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.0_react@18.2.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@next/swc-android-arm-eabi': 13.0.7-canary.4
|
'@next/swc-android-arm-eabi': 13.0.8-canary.0
|
||||||
'@next/swc-android-arm64': 13.0.7-canary.4
|
'@next/swc-android-arm64': 13.0.8-canary.0
|
||||||
'@next/swc-darwin-arm64': 13.0.7-canary.4
|
'@next/swc-darwin-arm64': 13.0.8-canary.0
|
||||||
'@next/swc-darwin-x64': 13.0.7-canary.4
|
'@next/swc-darwin-x64': 13.0.8-canary.0
|
||||||
'@next/swc-freebsd-x64': 13.0.7-canary.4
|
'@next/swc-freebsd-x64': 13.0.8-canary.0
|
||||||
'@next/swc-linux-arm-gnueabihf': 13.0.7-canary.4
|
'@next/swc-linux-arm-gnueabihf': 13.0.8-canary.0
|
||||||
'@next/swc-linux-arm64-gnu': 13.0.7-canary.4
|
'@next/swc-linux-arm64-gnu': 13.0.8-canary.0
|
||||||
'@next/swc-linux-arm64-musl': 13.0.7-canary.4
|
'@next/swc-linux-arm64-musl': 13.0.8-canary.0
|
||||||
'@next/swc-linux-x64-gnu': 13.0.7-canary.4
|
'@next/swc-linux-x64-gnu': 13.0.8-canary.0
|
||||||
'@next/swc-linux-x64-musl': 13.0.7-canary.4
|
'@next/swc-linux-x64-musl': 13.0.8-canary.0
|
||||||
'@next/swc-win32-arm64-msvc': 13.0.7-canary.4
|
'@next/swc-win32-arm64-msvc': 13.0.8-canary.0
|
||||||
'@next/swc-win32-ia32-msvc': 13.0.7-canary.4
|
'@next/swc-win32-ia32-msvc': 13.0.8-canary.0
|
||||||
'@next/swc-win32-x64-msvc': 13.0.7-canary.4
|
'@next/swc-win32-x64-msvc': 13.0.8-canary.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
Loading…
Reference in a new issue