2022-11-10 02:11:36 -05:00
|
|
|
"use client"
|
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
import styles from "./post-list.module.css"
|
2022-03-09 20:11:37 -05:00
|
|
|
import ListItemSkeleton from "./list-item-skeleton"
|
|
|
|
import ListItem from "./list-item"
|
2022-11-10 02:11:36 -05:00
|
|
|
import { ChangeEvent, useCallback, useEffect, useState } from "react"
|
2022-04-12 02:07:06 -04:00
|
|
|
import useDebounce from "@lib/hooks/use-debounce"
|
2022-11-12 04:28:06 -05:00
|
|
|
import Link from "@components/link"
|
2022-11-11 22:17:44 -05:00
|
|
|
import type { PostWithFiles } from "@lib/server/prisma"
|
2022-11-16 01:52:25 -05:00
|
|
|
import Input from "@components/input"
|
|
|
|
import Button from "@components/button"
|
2022-03-06 19:46:59 -05:00
|
|
|
|
|
|
|
type Props = {
|
2022-11-14 02:02:31 -05:00
|
|
|
initialPosts: string | PostWithFiles[]
|
2022-04-09 20:48:19 -04:00
|
|
|
morePosts: boolean
|
2022-11-14 02:02:31 -05:00
|
|
|
userId?: string
|
2022-03-06 19:46:59 -05:00
|
|
|
}
|
|
|
|
|
2022-11-14 02:02:31 -05:00
|
|
|
const PostList = ({
|
|
|
|
morePosts,
|
|
|
|
initialPosts: initialPostsMaybeJSON,
|
|
|
|
userId
|
|
|
|
}: Props) => {
|
|
|
|
const initialPosts =
|
|
|
|
typeof initialPostsMaybeJSON === "string"
|
|
|
|
? JSON.parse(initialPostsMaybeJSON)
|
|
|
|
: initialPostsMaybeJSON
|
2022-04-09 20:48:19 -04:00
|
|
|
const [search, setSearchValue] = useState("")
|
2022-11-14 02:02:31 -05:00
|
|
|
const [posts, setPosts] = useState<PostWithFiles[]>(initialPosts)
|
2022-04-09 20:48:19 -04:00
|
|
|
const [searching, setSearching] = useState(false)
|
|
|
|
const [hasMorePosts, setHasMorePosts] = useState(morePosts)
|
2022-04-12 02:07:06 -04:00
|
|
|
|
|
|
|
const debouncedSearchValue = useDebounce(search, 200)
|
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
const loadMoreClick = useCallback(
|
|
|
|
(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
|
|
e.preventDefault()
|
|
|
|
if (hasMorePosts) {
|
|
|
|
async function fetchPosts() {
|
|
|
|
const res = await fetch(`/server-api/posts/mine`, {
|
|
|
|
method: "GET",
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"x-page": `${posts.length / 10 + 1}`
|
|
|
|
}
|
|
|
|
})
|
|
|
|
const json = await res.json()
|
|
|
|
setPosts([...posts, ...json.posts])
|
|
|
|
setHasMorePosts(json.morePosts)
|
|
|
|
}
|
|
|
|
fetchPosts()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[posts, hasMorePosts]
|
|
|
|
)
|
2022-03-24 18:35:59 -04:00
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
// update posts on search
|
|
|
|
useEffect(() => {
|
2022-04-12 02:07:06 -04:00
|
|
|
if (debouncedSearchValue) {
|
2022-11-14 02:02:31 -05:00
|
|
|
setSearching(true)
|
|
|
|
async function fetchPosts() {
|
2022-04-09 20:48:19 -04:00
|
|
|
const res = await fetch(
|
2022-11-14 02:02:31 -05:00
|
|
|
`/api/post/search?q=${encodeURIComponent(
|
2022-04-12 02:07:06 -04:00
|
|
|
debouncedSearchValue
|
2022-11-14 02:02:31 -05:00
|
|
|
)}&userId=${userId}`,
|
2022-04-09 20:48:19 -04:00
|
|
|
{
|
|
|
|
method: "GET",
|
|
|
|
headers: {
|
2022-11-12 21:39:03 -05:00
|
|
|
"Content-Type": "application/json"
|
2022-04-09 20:48:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2022-11-14 02:02:31 -05:00
|
|
|
const json = await res.json()
|
|
|
|
setPosts(json.posts)
|
2022-04-09 20:48:19 -04:00
|
|
|
setSearching(false)
|
|
|
|
}
|
2022-11-14 02:02:31 -05:00
|
|
|
fetchPosts()
|
2022-04-09 20:48:19 -04:00
|
|
|
} else {
|
|
|
|
setPosts(initialPosts)
|
|
|
|
}
|
2022-11-14 02:02:31 -05:00
|
|
|
// TODO: fix cyclical dependency issue
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [debouncedSearchValue, userId])
|
2022-03-24 18:35:59 -04:00
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
|
|
setSearchValue(e.target.value)
|
|
|
|
}
|
2022-03-24 18:35:59 -04:00
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
const deletePost = useCallback(
|
|
|
|
(postId: string) => async () => {
|
2022-11-14 02:02:31 -05:00
|
|
|
const res = await fetch(`/api/post/${postId}`, {
|
2022-04-09 20:48:19 -04:00
|
|
|
method: "DELETE",
|
|
|
|
})
|
2022-03-26 03:24:18 -04:00
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
if (!res.ok) {
|
|
|
|
console.error(res)
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
setPosts((posts) => posts.filter((post) => post.id !== postId))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[]
|
|
|
|
)
|
2022-03-26 03:24:18 -04:00
|
|
|
|
2022-04-09 20:48:19 -04:00
|
|
|
return (
|
|
|
|
<div className={styles.container}>
|
|
|
|
<div className={styles.searchContainer}>
|
|
|
|
<Input
|
|
|
|
placeholder="Search..."
|
2022-04-12 02:07:06 -04:00
|
|
|
onChange={handleSearchChange}
|
2022-11-12 02:59:33 -05:00
|
|
|
disabled={Boolean(!posts?.length)}
|
2022-11-16 01:52:25 -05:00
|
|
|
style={{ maxWidth: 300 }}
|
2022-04-09 20:48:19 -04:00
|
|
|
/>
|
|
|
|
</div>
|
2022-11-16 01:52:25 -05:00
|
|
|
{!posts && <p style={{color: 'var(--warning)'}}>Failed to load.</p>}
|
2022-11-14 02:02:31 -05:00
|
|
|
{!posts?.length && searching && (
|
2022-04-09 20:48:19 -04:00
|
|
|
<ul>
|
|
|
|
<li>
|
|
|
|
<ListItemSkeleton />
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<ListItemSkeleton />
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
)}
|
2022-11-10 02:11:36 -05:00
|
|
|
{posts?.length === 0 && posts && (
|
2022-11-16 01:52:25 -05:00
|
|
|
<p>
|
2022-04-09 20:48:19 -04:00
|
|
|
No posts found. Create one{" "}
|
2022-11-08 03:23:28 -05:00
|
|
|
<Link colored href="/new">
|
|
|
|
here
|
|
|
|
</Link>
|
2022-04-09 20:48:19 -04:00
|
|
|
.
|
2022-11-16 01:52:25 -05:00
|
|
|
</p>
|
2022-04-09 20:48:19 -04:00
|
|
|
)}
|
|
|
|
{posts?.length > 0 && (
|
|
|
|
<div>
|
|
|
|
<ul>
|
|
|
|
{posts.map((post) => {
|
|
|
|
return (
|
|
|
|
<ListItem
|
|
|
|
deletePost={deletePost(post.id)}
|
|
|
|
post={post}
|
|
|
|
key={post.id}
|
|
|
|
/>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
{hasMorePosts && !setSearchValue && (
|
|
|
|
<div className={styles.moreContainer}>
|
|
|
|
<Button width={"100%"} onClick={loadMoreClick}>
|
|
|
|
Load more
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
2022-03-06 19:46:59 -05:00
|
|
|
}
|
|
|
|
|
2022-03-09 20:11:37 -05:00
|
|
|
export default PostList
|