Add public post listing to home page
This commit is contained in:
parent
5918b13867
commit
7ef45c28f0
13 changed files with 115 additions and 70 deletions
|
@ -19,7 +19,17 @@ const FileDropdown = ({
|
|||
loading?: boolean
|
||||
}) => {
|
||||
if (loading) {
|
||||
return <Spinner />
|
||||
return (
|
||||
<Popover>
|
||||
<Popover.Trigger className={buttonStyles.button}>
|
||||
<div
|
||||
style={{ minWidth: 125 }}
|
||||
>
|
||||
<Spinner />
|
||||
</div>
|
||||
</Popover.Trigger>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
const items = files.map((file) => {
|
||||
|
|
|
@ -26,13 +26,15 @@ export const PostTitle = ({
|
|||
}: TitleProps) => {
|
||||
return (
|
||||
<span className={styles.title}>
|
||||
<h3>
|
||||
<h1 style={{
|
||||
fontSize: "1.175rem"
|
||||
}}>
|
||||
{title}{" "}
|
||||
<span style={{ color: "var(--gray)" }}>
|
||||
by{" "}
|
||||
<Link href={`/author/${authorId}`}>{displayName || "anonymous"}</Link>
|
||||
</span>
|
||||
</h3>
|
||||
</h1>
|
||||
{!loading && (
|
||||
<span className={styles.badges}>
|
||||
{visibility && <VisibilityBadge visibility={visibility} />}
|
||||
|
|
|
@ -87,12 +87,14 @@ const Document = ({
|
|||
height={"2rem"}
|
||||
style={{ borderRadius: 0 }}
|
||||
value={title || "Untitled"}
|
||||
onChange={() => {}}
|
||||
disabled
|
||||
aria-label="Document title"
|
||||
/>
|
||||
</Link>
|
||||
<div className={styles.descriptionContainer}>
|
||||
<DownloadButtons rawLink={`/api/file/raw/${id}`} />
|
||||
{/* Not /api/ because of rewrites defined in next.config.mjs */}
|
||||
<DownloadButtons rawLink={`/file/raw/${id}`} />
|
||||
<DocumentTabs
|
||||
defaultTab={initialTab}
|
||||
preview={preview}
|
||||
|
|
|
@ -100,7 +100,7 @@ const PostView = async ({
|
|||
const stringifiedPost = JSON.stringify(post)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.root}>
|
||||
<div className={styles.header}>
|
||||
<PostButtons
|
||||
parentId={post.parentId || undefined}
|
||||
|
@ -133,7 +133,7 @@ const PostView = async ({
|
|||
</span>
|
||||
)}
|
||||
<ScrollToTop />
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
.root {
|
||||
padding-bottom: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
|
|
|
@ -34,7 +34,7 @@ export default async function UserPage({
|
|||
|
||||
return (
|
||||
<>
|
||||
<h1>{user?.displayName}'s public posts</h1>
|
||||
<h1>Public posts by {user?.displayName || "Anonymous"}</h1>
|
||||
<Suspense fallback={<PostList initialPosts={JSON.stringify({})} />}>
|
||||
{/* @ts-ignore because TS async JSX support is iffy */}
|
||||
<PostListWrapper posts={posts} userId={id} />
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.textarea {
|
||||
height: 100%;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import Image from "next/image"
|
||||
import Card from "./card"
|
||||
import DocumentTabs from "app/(posts)/components/tabs"
|
||||
const Home = ({
|
||||
introTitle,
|
||||
introContent,
|
||||
rendered
|
||||
}: {
|
||||
introTitle: string
|
||||
introContent: string
|
||||
rendered: string
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "row", alignItems: "center" }}
|
||||
>
|
||||
<Image
|
||||
src={"/assets/logo-optimized.svg"}
|
||||
width={48}
|
||||
height={48}
|
||||
alt=""
|
||||
priority
|
||||
/>
|
||||
<h1 style={{ marginLeft: "var(--gap)" }}>{introTitle}</h1>
|
||||
</div>
|
||||
<Card>
|
||||
<DocumentTabs
|
||||
defaultTab="preview"
|
||||
isEditing={false}
|
||||
content={introContent}
|
||||
preview={rendered}
|
||||
title={introTitle}
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
|
@ -5,8 +5,6 @@ import ListItem from "./list-item"
|
|||
import {
|
||||
ChangeEvent,
|
||||
useCallback,
|
||||
useDeferredValue,
|
||||
useEffect,
|
||||
useState
|
||||
} from "react"
|
||||
import Link from "@components/link"
|
||||
|
@ -21,12 +19,14 @@ type Props = {
|
|||
initialPosts: string | PostWithFiles[]
|
||||
morePosts?: boolean
|
||||
userId?: string
|
||||
hideSearch?: boolean
|
||||
}
|
||||
|
||||
const PostList = ({
|
||||
morePosts,
|
||||
initialPosts: initialPostsMaybeJSON,
|
||||
userId
|
||||
userId,
|
||||
hideSearch
|
||||
}: Props) => {
|
||||
const initialPosts =
|
||||
typeof initialPostsMaybeJSON === "string"
|
||||
|
@ -108,7 +108,7 @@ const PostList = ({
|
|||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.searchContainer}>
|
||||
{!hideSearch && <div className={styles.searchContainer}>
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
onChange={handleSearchChange}
|
||||
|
@ -117,7 +117,7 @@ const PostList = ({
|
|||
aria-label="Search"
|
||||
value={search}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
{!posts && <p style={{ color: "var(--warning)" }}>Failed to load.</p>}
|
||||
{searching && (
|
||||
<ul>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import Image from "next/image"
|
||||
import Card from "@components/card"
|
||||
import { getWelcomeContent } from "pages/api/welcome"
|
||||
import Home from "./components/home"
|
||||
import DocumentTabs from "./(posts)/components/tabs"
|
||||
import { getAllPosts, Post } from "@lib/server/prisma"
|
||||
import PostList from "@components/post-list"
|
||||
|
||||
const getWelcomeData = async () => {
|
||||
const welcomeContent = await getWelcomeContent()
|
||||
|
@ -8,12 +12,58 @@ const getWelcomeData = async () => {
|
|||
|
||||
export default async function Page() {
|
||||
const { content, rendered, title } = await getWelcomeData()
|
||||
const getPostsPromise = getAllPosts({
|
||||
where: { visibility: "public" }
|
||||
})
|
||||
|
||||
return (
|
||||
<Home
|
||||
rendered={rendered as string}
|
||||
introContent={content}
|
||||
introTitle={title}
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", gap: "var(--gap)" }}
|
||||
>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "row", alignItems: "center" }}
|
||||
>
|
||||
<Image
|
||||
src={"/assets/logo-optimized.svg"}
|
||||
width={48}
|
||||
height={48}
|
||||
alt=""
|
||||
priority
|
||||
/>
|
||||
<h1 style={{ marginLeft: "var(--gap)" }}>{title}</h1>
|
||||
</div>
|
||||
<Card>
|
||||
<DocumentTabs
|
||||
defaultTab="preview"
|
||||
isEditing={false}
|
||||
content={content}
|
||||
preview={rendered as string}
|
||||
title={title}
|
||||
/>
|
||||
</Card>
|
||||
<div>
|
||||
<h2>Recent public posts</h2>
|
||||
{/* @ts-ignore because of async RSC */}
|
||||
<PublicPostList getPostsPromise={getPostsPromise} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
async function PublicPostList({
|
||||
getPostsPromise
|
||||
}: {
|
||||
getPostsPromise: Promise<Post[]>
|
||||
}) {
|
||||
const posts = await getPostsPromise
|
||||
return (
|
||||
<PostList
|
||||
userId={undefined}
|
||||
morePosts={false}
|
||||
initialPosts={JSON.stringify(posts)}
|
||||
hideSearch
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const revalidate = 60
|
||||
|
|
|
@ -4,10 +4,22 @@ import bundleAnalyzer from "@next/bundle-analyzer"
|
|||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
// outputStandalone: true,
|
||||
// esmExternals: true,
|
||||
appDir: true
|
||||
},
|
||||
output: "standalone",
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/file/raw/:id",
|
||||
destination: `/api/raw/:id`
|
||||
},
|
||||
{
|
||||
source: "/home",
|
||||
destination: "/"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
"ts-jest": "^29.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.1.6",
|
||||
"@next/bundle-analyzer": "13.0.7-canary.1",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "17.0.23",
|
||||
|
|
|
@ -2,7 +2,7 @@ lockfileVersion: 5.4
|
|||
|
||||
specifiers:
|
||||
'@next-auth/prisma-adapter': ^1.0.5
|
||||
'@next/bundle-analyzer': 12.1.6
|
||||
'@next/bundle-analyzer': 13.0.7-canary.1
|
||||
'@next/eslint-plugin-next': 13.0.5-canary.3
|
||||
'@prisma/client': ^4.7.1
|
||||
'@radix-ui/react-dialog': ^1.0.2
|
||||
|
@ -75,7 +75,7 @@ optionalDependencies:
|
|||
sharp: 0.31.2
|
||||
|
||||
devDependencies:
|
||||
'@next/bundle-analyzer': 12.1.6
|
||||
'@next/bundle-analyzer': 13.0.7-canary.1
|
||||
'@types/bcrypt': 5.0.0
|
||||
'@types/lodash.debounce': 4.0.7
|
||||
'@types/node': 17.0.23
|
||||
|
@ -799,10 +799,10 @@ packages:
|
|||
next-auth: 4.18.0_ihvxcpofhpc4k2aqfys2drrlkq
|
||||
dev: false
|
||||
|
||||
/@next/bundle-analyzer/12.1.6:
|
||||
resolution: {integrity: sha512-WLydwytAeHoC/neXsiIgK+a6Me12PuSpwopnsZgX5JFNwXQ9MlwPeMGS3aTZkYsv8QmSm0Ns9Yh9FkgLKYaUuQ==}
|
||||
/@next/bundle-analyzer/13.0.7-canary.1:
|
||||
resolution: {integrity: sha512-3CKGOK1Fp5mhCQ001h/GIj/ceZa4IfljWAkxRkX4uAOUAyyQ9MNNLNUX9H95/+oO7k2YS/Z71wXRk/xDaxM3Jw==}
|
||||
dependencies:
|
||||
webpack-bundle-analyzer: 4.3.0
|
||||
webpack-bundle-analyzer: 4.7.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
@ -2302,6 +2302,11 @@ packages:
|
|||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/commander/7.2.0:
|
||||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||
engines: {node: '>= 10'}
|
||||
dev: true
|
||||
|
||||
/commander/8.3.0:
|
||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||
engines: {node: '>= 12'}
|
||||
|
@ -7181,15 +7186,15 @@ packages:
|
|||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/webpack-bundle-analyzer/4.3.0:
|
||||
resolution: {integrity: sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==}
|
||||
/webpack-bundle-analyzer/4.7.0:
|
||||
resolution: {integrity: sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
acorn: 8.8.1
|
||||
acorn-walk: 8.2.0
|
||||
chalk: 4.1.2
|
||||
commander: 6.2.1
|
||||
commander: 7.2.0
|
||||
gzip-size: 6.0.0
|
||||
lodash: 4.17.21
|
||||
opener: 1.5.2
|
||||
|
|
Loading…
Reference in a new issue