upstream #1
| @@ -1,5 +1,3 @@ | ||||
| 'use client' | ||||
|  | ||||
| import Link from '@/components/Link' | ||||
| // import { PageSEO } from '@/components/SEO' | ||||
| import Tag from '@/components/Tag' | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { MDXLayoutRenderer } from 'pliny/mdx-components' | ||||
| import { MDXComponents } from '@/components/MDXComponents' | ||||
|  | ||||
| const DEFAULT_LAYOUT = 'AuthorLayout' | ||||
|  | ||||
| export default function About({ author }) { | ||||
|   return ( | ||||
|     <MDXLayoutRenderer | ||||
|       layout={author.layout || DEFAULT_LAYOUT} | ||||
|       content={author} | ||||
|       MDXComponents={MDXComponents} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
| @@ -1,7 +1,17 @@ | ||||
| import { allAuthors } from 'contentlayer/generated' | ||||
| import About from './About' | ||||
| import { Authors, allAuthors } from 'contentlayer/generated' | ||||
| import { Mdx } from '@/components/MDXComponents' | ||||
| import AuthorLayout from '@/layouts/AuthorLayout' | ||||
| import { coreContent } from 'pliny/utils/contentlayer' | ||||
|  | ||||
| export default function Page() { | ||||
|   const author = allAuthors.find((p) => p.slug === 'default') | ||||
|   return <About author={author} /> | ||||
|   const author = allAuthors.find((p) => p.slug === 'default') as Authors | ||||
|   const mainContent = coreContent(author) | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <AuthorLayout content={mainContent}> | ||||
|         <Mdx code={author.body.code} /> | ||||
|       </AuthorLayout> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { MDXLayoutRenderer } from 'pliny/mdx-components' | ||||
| import { MDXComponents } from '@/components/MDXComponents' | ||||
| import type { Blog } from 'contentlayer/generated' | ||||
|  | ||||
| const DEFAULT_LAYOUT = 'PostLayout' | ||||
|  | ||||
| export default function Blog({ post, authorDetails, prev, next }) { | ||||
|   return ( | ||||
|     <MDXLayoutRenderer | ||||
|       layout={post.layout || DEFAULT_LAYOUT} | ||||
|       content={post} | ||||
|       MDXComponents={MDXComponents} | ||||
|       toc={post.toc} | ||||
|       authorDetails={authorDetails} | ||||
|       prev={prev} | ||||
|       next={next} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
| @@ -1,8 +1,9 @@ | ||||
| import PageTitle from '@/components/PageTitle' | ||||
| import { Mdx } from '@/components/MDXComponents' | ||||
| import { sortedBlogPost, coreContent } from 'pliny/utils/contentlayer' | ||||
| import { allBlogs, allAuthors } from 'contentlayer/generated' | ||||
| import type { Authors, Blog } from 'contentlayer/generated' | ||||
| import BlogPage from './Blog' | ||||
| import PostLayout from '@/layouts/PostLayout' | ||||
|  | ||||
| export const generateStaticParams = async () => { | ||||
|   const paths = allBlogs.map((p) => ({ slug: p.slug.split('/') })) | ||||
| @@ -10,20 +11,19 @@ export const generateStaticParams = async () => { | ||||
|   return paths | ||||
| } | ||||
|  | ||||
| export default function Page({ params }: { params: { slug: string[] } }) { | ||||
| export default async function Page({ params }: { params: { slug: string[] } }) { | ||||
|   const slug = params.slug.join('/') | ||||
|   const sortedPosts = sortedBlogPost(allBlogs) as Blog[] | ||||
|   const postIndex = sortedPosts.findIndex((p) => p.slug === slug) | ||||
|   const prevContent = sortedPosts[postIndex + 1] || null | ||||
|   const prev = prevContent ? coreContent(prevContent) : null | ||||
|   const nextContent = sortedPosts[postIndex - 1] || null | ||||
|   const next = nextContent ? coreContent(nextContent) : null | ||||
|   const post = sortedPosts.find((p) => p.slug === slug) | ||||
|   const prev = coreContent(sortedPosts[postIndex + 1]) | ||||
|   const next = coreContent(sortedPosts[postIndex - 1]) | ||||
|   const post = sortedPosts.find((p) => p.slug === slug) as Blog | ||||
|   const authorList = post?.authors || ['default'] | ||||
|   const authorDetails = authorList.map((author) => { | ||||
|     const authorResults = allAuthors.find((p) => p.slug === author) | ||||
|     return coreContent(authorResults as Authors) | ||||
|   }) | ||||
|   const mainContent = coreContent(post) | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
| @@ -37,7 +37,9 @@ export default function Page({ params }: { params: { slug: string[] } }) { | ||||
|           </PageTitle> | ||||
|         </div> | ||||
|       ) : ( | ||||
|         <BlogPage post={post} authorDetails={authorDetails} prev={prev} next={next} /> | ||||
|         <PostLayout content={mainContent} authorDetails={authorDetails} next={next} prev={prev}> | ||||
|           <Mdx code={post.body.code} toc={post.toc} /> | ||||
|         </PostLayout> | ||||
|       )} | ||||
|     </> | ||||
|   ) | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import projectsData from '@/data/projectsData' | ||||
| import Card from '@/components/Card' | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								components/BlogNewsletterForm.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								components/BlogNewsletterForm.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| 'use client' | ||||
|  | ||||
| // dot notation breaks RSC - https://github.com/vercel/next.js/issues/51593 | ||||
| // temporarily workaround to re-export | ||||
| import { BlogNewsletterForm } from 'pliny/ui/NewsletterForm' | ||||
|  | ||||
| export default BlogNewsletterForm | ||||
							
								
								
									
										13
									
								
								components/ClientComponent.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								components/ClientComponent.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import { useState } from 'react' | ||||
|  | ||||
| export default function ClientComponent() { | ||||
|   const [count, setCount] = useState(0) | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <h1>Client Component</h1> | ||||
|       <p>Count: {count}</p> | ||||
|       <button onClick={() => setCount(count + 1)}>Inc</button> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
| @@ -2,7 +2,7 @@ | ||||
| import Link from 'next/link' | ||||
| import { AnchorHTMLAttributes, DetailedHTMLProps } from 'react' | ||||
|  | ||||
| const CustomLink = ({ | ||||
| const CustomLink = async ({ | ||||
|   href, | ||||
|   ...rest | ||||
| }: DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>) => { | ||||
|   | ||||
| @@ -1,16 +1,17 @@ | ||||
| /* eslint-disable react/display-name */ | ||||
| import React from 'react' | ||||
| import { MDXLayout, ComponentMap } from 'pliny/mdx-components' | ||||
| import { useMDXComponent } from 'next-contentlayer/hooks' | ||||
| import { ComponentMap } from 'pliny/mdx-components' | ||||
| import { TOCInline } from 'pliny/ui/TOCInline' | ||||
| import { Pre } from 'pliny/ui/Pre' | ||||
| import { BlogNewsletterForm } from 'pliny/ui/NewsletterForm' | ||||
| import Pre from './Pre' | ||||
| import BlogNewsletterForm from './BlogNewsletterForm' | ||||
| // import { BlogNewsletterForm } from 'pliny/ui/NewsletterForm' | ||||
|  | ||||
| import Image from './Image' | ||||
| import CustomLink from './Link' | ||||
|  | ||||
| export const Wrapper = ({ layout, content, ...rest }: MDXLayout) => { | ||||
|   const Layout = require(`../layouts/${layout}`).default | ||||
|   return <Layout content={content} {...rest} /> | ||||
| interface MdxProps { | ||||
|   code: string | ||||
|   [key: string]: any | ||||
| } | ||||
|  | ||||
| export const MDXComponents: ComponentMap = { | ||||
| @@ -18,6 +19,11 @@ export const MDXComponents: ComponentMap = { | ||||
|   TOCInline, | ||||
|   a: CustomLink, | ||||
|   pre: Pre, | ||||
|   wrapper: Wrapper, | ||||
|   BlogNewsletterForm, | ||||
| } | ||||
|  | ||||
| export function Mdx({ code, ...rest }: MdxProps) { | ||||
|   const Component = useMDXComponent(code) | ||||
|  | ||||
|   return <Component components={{ ...MDXComponents }} {...rest} /> | ||||
| } | ||||
|   | ||||
							
								
								
									
										72
									
								
								components/Pre.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								components/Pre.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| 'use client' | ||||
| import { useState, useRef, ReactNode } from 'react' | ||||
|  | ||||
| const Pre = ({ children }: { children: ReactNode }) => { | ||||
|   const textInput = useRef(null) | ||||
|   const [hovered, setHovered] = useState(false) | ||||
|   const [copied, setCopied] = useState(false) | ||||
|  | ||||
|   const onEnter = () => { | ||||
|     setHovered(true) | ||||
|   } | ||||
|   const onExit = () => { | ||||
|     setHovered(false) | ||||
|     setCopied(false) | ||||
|   } | ||||
|   const onCopy = () => { | ||||
|     setCopied(true) | ||||
|     // @ts-ignore | ||||
|     navigator.clipboard.writeText(textInput.current.textContent) | ||||
|     setTimeout(() => { | ||||
|       setCopied(false) | ||||
|     }, 2000) | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div ref={textInput} onMouseEnter={onEnter} onMouseLeave={onExit} className="relative"> | ||||
|       {hovered && ( | ||||
|         <button | ||||
|           aria-label="Copy code" | ||||
|           className={`absolute right-2 top-2 h-8 w-8 rounded border-2 bg-gray-700 p-1 dark:bg-gray-800 ${ | ||||
|             copied | ||||
|               ? 'border-green-400 focus:border-green-400 focus:outline-none' | ||||
|               : 'border-gray-300' | ||||
|           }`} | ||||
|           onClick={onCopy} | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             className={copied ? 'text-green-400' : 'text-gray-300'} | ||||
|           > | ||||
|             {copied ? ( | ||||
|               <> | ||||
|                 <path | ||||
|                   strokeLinecap="round" | ||||
|                   strokeLinejoin="round" | ||||
|                   strokeWidth={2} | ||||
|                   d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" | ||||
|                 /> | ||||
|               </> | ||||
|             ) : ( | ||||
|               <> | ||||
|                 <path | ||||
|                   strokeLinecap="round" | ||||
|                   strokeLinejoin="round" | ||||
|                   strokeWidth={2} | ||||
|                   d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" | ||||
|                 /> | ||||
|               </> | ||||
|             )} | ||||
|           </svg> | ||||
|         </button> | ||||
|       )} | ||||
|  | ||||
|       <pre>{children}</pre> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export default Pre | ||||
| @@ -1,3 +1,5 @@ | ||||
| 'use client' | ||||
|  | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import { useEffect, useState } from 'react' | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import SectionContainer from '@/components/SectionContainer' | ||||
| import Image from '@/components/Image' | ||||
| import Tag from '@/components/Tag' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import ScrollTopAndComment from '@/components/ScrollTopAndComment' | ||||
| // import ScrollTopAndComment from '@/components/ScrollTopAndComment' | ||||
|  | ||||
| const editUrl = (path) => `${siteMetadata.siteRepo}/blob/master/data/${path}` | ||||
| const discussUrl = (path) => | ||||
| @@ -33,12 +33,12 @@ interface LayoutProps { | ||||
| export default function PostLayout({ content, authorDetails, next, prev, children }: LayoutProps) { | ||||
|   const { filePath, path, slug, date, title, tags } = content | ||||
|   const basePath = path.split('/')[0] | ||||
|   const [loadComments, setLoadComments] = useState(false) | ||||
|   // const [loadComments, setLoadComments] = useState(false) | ||||
|  | ||||
|   return ( | ||||
|     <SectionContainer> | ||||
|       {/* <BlogSEO url={`${siteMetadata.siteUrl}/${path}`} authorDetails={authorDetails} {...content} /> */} | ||||
|       <ScrollTopAndComment /> | ||||
|       {/* <ScrollTopAndComment /> */} | ||||
|       <article> | ||||
|         <div className="xl:divide-y xl:divide-gray-200 xl:dark:divide-gray-700"> | ||||
|           <header className="pt-6 xl:pb-6"> | ||||
| @@ -108,10 +108,10 @@ export default function PostLayout({ content, authorDetails, next, prev, childre | ||||
|                   className="pb-6 pt-6 text-center text-gray-700 dark:text-gray-300" | ||||
|                   id="comment" | ||||
|                 > | ||||
|                   {!loadComments && ( | ||||
|                   {/* {!loadComments && ( | ||||
|                     <button onClick={() => setLoadComments(true)}>Load Comments</button> | ||||
|                   )} | ||||
|                   {loadComments && <Comments commentsConfig={siteMetadata.comments} slug={slug} />} | ||||
|                   {loadComments && <Comments commentsConfig={siteMetadata.comments} slug={slug} />} */} | ||||
|                 </div> | ||||
|               )} | ||||
|             </div> | ||||
|   | ||||
| @@ -1,89 +1,89 @@ | ||||
| import { useState, ReactNode } from 'react' | ||||
| import { Comments } from 'pliny/comments' | ||||
| import { formatDate } from 'pliny/utils/formatDate' | ||||
| import { CoreContent } from 'pliny/utils/contentlayer' | ||||
| import type { Blog } from 'contentlayer/generated' | ||||
| import Link from '@/components/Link' | ||||
| import PageTitle from '@/components/PageTitle' | ||||
| import SectionContainer from '@/components/SectionContainer' | ||||
| // import { BlogSEO } from '@/components/SEO' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import ScrollTopAndComment from '@/components/ScrollTopAndComment' | ||||
| // import { useState, ReactNode } from 'react' | ||||
| // import { Comments } from 'pliny/comments' | ||||
| // import { formatDate } from 'pliny/utils/formatDate' | ||||
| // import { CoreContent } from 'pliny/utils/contentlayer' | ||||
| // import type { Blog } from 'contentlayer/generated' | ||||
| // import Link from '@/components/Link' | ||||
| // import PageTitle from '@/components/PageTitle' | ||||
| // import SectionContainer from '@/components/SectionContainer' | ||||
| // // import { BlogSEO } from '@/components/SEO' | ||||
| // import siteMetadata from '@/data/siteMetadata' | ||||
| // import ScrollTopAndComment from '@/components/ScrollTopAndComment' | ||||
|  | ||||
| interface LayoutProps { | ||||
|   content: CoreContent<Blog> | ||||
|   children: ReactNode | ||||
|   next?: { path: string; title: string } | ||||
|   prev?: { path: string; title: string } | ||||
| } | ||||
| // interface LayoutProps { | ||||
| //   content: CoreContent<Blog> | ||||
| //   children: ReactNode | ||||
| //   next?: { path: string; title: string } | ||||
| //   prev?: { path: string; title: string } | ||||
| // } | ||||
|  | ||||
| export default function PostLayout({ content, next, prev, children }: LayoutProps) { | ||||
|   const [loadComments, setLoadComments] = useState(false) | ||||
| // export default function PostLayout({ content, next, prev, children }: LayoutProps) { | ||||
| //   const [loadComments, setLoadComments] = useState(false) | ||||
|  | ||||
|   const { path, slug, date, title } = content | ||||
| //   const { path, slug, date, title } = content | ||||
|  | ||||
|   return ( | ||||
|     <SectionContainer> | ||||
|       {/* <BlogSEO url={`${siteMetadata.siteUrl}/${path}`} {...content} /> */} | ||||
|       <ScrollTopAndComment /> | ||||
|       <article> | ||||
|         <div> | ||||
|           <header> | ||||
|             <div className="space-y-1 border-b border-gray-200 pb-10 text-center dark:border-gray-700"> | ||||
|               <dl> | ||||
|                 <div> | ||||
|                   <dt className="sr-only">Published on</dt> | ||||
|                   <dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400"> | ||||
|                     <time dateTime={date}>{formatDate(date, siteMetadata.locale)}</time> | ||||
|                   </dd> | ||||
|                 </div> | ||||
|               </dl> | ||||
|               <div> | ||||
|                 <PageTitle>{title}</PageTitle> | ||||
|               </div> | ||||
|             </div> | ||||
|           </header> | ||||
|           <div className="grid-rows-[auto_1fr] divide-y divide-gray-200 pb-8 dark:divide-gray-700 xl:divide-y-0"> | ||||
|             <div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0"> | ||||
|               <div className="prose max-w-none pb-8 pt-10 dark:prose-dark">{children}</div> | ||||
|             </div> | ||||
|             {siteMetadata.comments && ( | ||||
|               <div className="pb-6 pt-6 text-center text-gray-700 dark:text-gray-300" id="comment"> | ||||
|                 {!loadComments && ( | ||||
|                   <button onClick={() => setLoadComments(true)}>Load Comments</button> | ||||
|                 )} | ||||
|                 {loadComments && <Comments commentsConfig={siteMetadata.comments} slug={slug} />} | ||||
|               </div> | ||||
|             )} | ||||
|             <footer> | ||||
|               <div className="flex flex-col text-sm font-medium sm:flex-row sm:justify-between sm:text-base"> | ||||
|                 {prev && ( | ||||
|                   <div className="pt-4 xl:pt-8"> | ||||
|                     <Link | ||||
|                       href={`/${prev.path}`} | ||||
|                       className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400" | ||||
|                       aria-label={`Previous post: ${prev.title}`} | ||||
|                     > | ||||
|                       ← {prev.title} | ||||
|                     </Link> | ||||
|                   </div> | ||||
|                 )} | ||||
|                 {next && ( | ||||
|                   <div className="pt-4 xl:pt-8"> | ||||
|                     <Link | ||||
|                       href={`/${next.path}`} | ||||
|                       className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400" | ||||
|                       aria-label={`Next post: ${next.title}`} | ||||
|                     > | ||||
|                       {next.title} → | ||||
|                     </Link> | ||||
|                   </div> | ||||
|                 )} | ||||
|               </div> | ||||
|             </footer> | ||||
|           </div> | ||||
|         </div> | ||||
|       </article> | ||||
|     </SectionContainer> | ||||
|   ) | ||||
| } | ||||
| //   return ( | ||||
| //     <SectionContainer> | ||||
| //       {/* <BlogSEO url={`${siteMetadata.siteUrl}/${path}`} {...content} /> */} | ||||
| //       <ScrollTopAndComment /> | ||||
| //       <article> | ||||
| //         <div> | ||||
| //           <header> | ||||
| //             <div className="space-y-1 border-b border-gray-200 pb-10 text-center dark:border-gray-700"> | ||||
| //               <dl> | ||||
| //                 <div> | ||||
| //                   <dt className="sr-only">Published on</dt> | ||||
| //                   <dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400"> | ||||
| //                     <time dateTime={date}>{formatDate(date, siteMetadata.locale)}</time> | ||||
| //                   </dd> | ||||
| //                 </div> | ||||
| //               </dl> | ||||
| //               <div> | ||||
| //                 <PageTitle>{title}</PageTitle> | ||||
| //               </div> | ||||
| //             </div> | ||||
| //           </header> | ||||
| //           <div className="grid-rows-[auto_1fr] divide-y divide-gray-200 pb-8 dark:divide-gray-700 xl:divide-y-0"> | ||||
| //             <div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0"> | ||||
| //               <div className="prose max-w-none pb-8 pt-10 dark:prose-dark">{children}</div> | ||||
| //             </div> | ||||
| //             {siteMetadata.comments && ( | ||||
| //               <div className="pb-6 pt-6 text-center text-gray-700 dark:text-gray-300" id="comment"> | ||||
| //                 {!loadComments && ( | ||||
| //                   <button onClick={() => setLoadComments(true)}>Load Comments</button> | ||||
| //                 )} | ||||
| //                 {loadComments && <Comments commentsConfig={siteMetadata.comments} slug={slug} />} | ||||
| //               </div> | ||||
| //             )} | ||||
| //             <footer> | ||||
| //               <div className="flex flex-col text-sm font-medium sm:flex-row sm:justify-between sm:text-base"> | ||||
| //                 {prev && ( | ||||
| //                   <div className="pt-4 xl:pt-8"> | ||||
| //                     <Link | ||||
| //                       href={`/${prev.path}`} | ||||
| //                       className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400" | ||||
| //                       aria-label={`Previous post: ${prev.title}`} | ||||
| //                     > | ||||
| //                       ← {prev.title} | ||||
| //                     </Link> | ||||
| //                   </div> | ||||
| //                 )} | ||||
| //                 {next && ( | ||||
| //                   <div className="pt-4 xl:pt-8"> | ||||
| //                     <Link | ||||
| //                       href={`/${next.path}`} | ||||
| //                       className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400" | ||||
| //                       aria-label={`Next post: ${next.title}`} | ||||
| //                     > | ||||
| //                       {next.title} → | ||||
| //                     </Link> | ||||
| //                   </div> | ||||
| //                 )} | ||||
| //               </div> | ||||
| //             </footer> | ||||
| //           </div> | ||||
| //         </div> | ||||
| //       </article> | ||||
| //     </SectionContainer> | ||||
| //   ) | ||||
| // } | ||||
|   | ||||
							
								
								
									
										1
									
								
								next-env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								next-env.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,5 @@ | ||||
| /// <reference types="next" /> | ||||
| /// <reference types="next/image-types/global" /> | ||||
| /// <reference types="next/navigation-types/compat/navigation" /> | ||||
|  | ||||
| // NOTE: This file should not be edited | ||||
| // see https://nextjs.org/docs/basic-features/typescript for more information. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user