CoastalCommitsPastes/client/app/components/post-list/index.tsx

164 lines
3.6 KiB
TypeScript
Raw Normal View History

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 ListItem from "./list-item"
import {
ChangeEvent,
useCallback,
useDeferredValue,
useEffect,
useState
} from "react"
2022-11-12 04:28:06 -05:00
import Link from "@components/link"
import type { PostWithFiles } from "@lib/server/prisma"
import Input from "@components/input"
import Button from "@components/button"
import { useToasts } from "@components/toasts"
2022-12-04 04:55:20 -05:00
import { ListItemSkeleton } from "./list-item-skeleton"
import debounce from "lodash.debounce"
2022-03-06 19:46:59 -05:00
type Props = {
initialPosts: string | PostWithFiles[]
2022-12-04 04:55:20 -05:00
morePosts?: boolean
userId?: string
2022-03-06 19:46:59 -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("")
const [posts, setPosts] = useState<PostWithFiles[]>(initialPosts)
2022-04-09 20:48:19 -04:00
const [searching, setSearching] = useState(false)
const [hasMorePosts, setHasMorePosts] = useState(morePosts)
const { setToast } = useToasts()
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]
)
const onSearch = (query: string) => {
setSearching(true)
async function fetchPosts() {
const res = await fetch(
`/api/post/search?q=${encodeURIComponent(query)}&userId=${userId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json"
2022-04-09 20:48:19 -04:00
}
}
)
const json = await res.json()
setPosts(json)
setSearching(false)
2022-04-09 20:48:19 -04:00
}
fetchPosts()
}
const debouncedSearch = debounce(onSearch, 500)
2022-04-09 20:48:19 -04:00
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
setSearchValue(e.target.value)
debouncedSearch(e.target.value)
2022-04-09 20:48:19 -04:00
}
2022-04-09 20:48:19 -04:00
const deletePost = useCallback(
(postId: string) => async () => {
const res = await fetch(`/api/post/${postId}`, {
2022-11-18 01:36:53 -05:00
method: "DELETE"
2022-04-09 20:48:19 -04:00
})
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))
setToast({
message: "Post deleted",
type: "success"
})
2022-04-09 20:48:19 -04:00
}
},
[setToast]
2022-04-09 20:48:19 -04:00
)
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..."
onChange={handleSearchChange}
disabled={!posts}
style={{ maxWidth: 300 }}
aria-label="Search"
value={search}
2022-04-09 20:48:19 -04:00
/>
</div>
2022-11-18 01:36:53 -05:00
{!posts && <p style={{ color: "var(--warning)" }}>Failed to load.</p>}
{searching && (
2022-04-09 20:48:19 -04:00
<ul>
<ListItemSkeleton />
<ListItemSkeleton />
2022-04-09 20:48:19 -04:00
</ul>
2022-12-04 04:55:20 -05:00
)}
2022-11-10 02:11:36 -05:00
{posts?.length === 0 && posts && (
<p>
2022-04-09 20:48:19 -04:00
No posts found. Create one{" "}
<Link colored href="/new">
here
</Link>
2022-04-09 20:48:19 -04: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