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

139 lines
4.7 KiB
TypeScript
Raw Permalink Normal View History

import { Button, Code, Dot, Input, Note, Text } from "@geist-ui/core"
import NextLink from "next/link"
2022-03-06 20:20:01 -05:00
import Link from '../Link'
2022-03-06 19:46:59 -05: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"
import { Post } from "@lib/types"
2022-03-26 03:24:18 -04:00
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react"
import debounce from "lodash.debounce"
2022-03-24 21:03:57 -04:00
import Cookies from "js-cookie"
2022-03-06 19:46:59 -05:00
type Props = {
2022-03-24 21:03:57 -04:00
initialPosts: Post[]
error: boolean
morePosts: boolean
2022-03-06 19:46:59 -05:00
}
2022-03-24 21:03:57 -04:00
const PostList = ({ morePosts, initialPosts, error }: Props) => {
const [search, setSearchValue] = useState('')
2022-03-24 21:03:57 -04:00
const [posts, setPosts] = useState<Post[]>(initialPosts)
const [searching, setSearching] = useState(false)
const [hasMorePosts, setHasMorePosts] = useState(morePosts)
const loadMoreClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
2022-03-24 21:03:57 -04:00
e.preventDefault()
if (hasMorePosts) {
async function fetchPosts() {
const res = await fetch(`/server-api/posts/mine`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${Cookies.get('drift-token')}`,
"x-page": `${posts.length / 10 + 1}`,
}
}
)
2022-03-24 21:03:57 -04:00
const json = await res.json()
setPosts([...posts, ...json.posts])
setHasMorePosts(json.morePosts)
}
fetchPosts()
}
}, [posts, hasMorePosts])
// update posts on search
useEffect(() => {
if (search) {
2022-03-24 21:03:57 -04:00
// fetch results from /server-api/posts/search
const fetchResults = async () => {
setSearching(true)
//encode search
const res = await fetch(`/server-api/posts/search?q=${encodeURIComponent(search)}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${Cookies.get("drift-token")}`,
// "tok": process.env.SECRET_KEY || ''
}
2022-03-24 21:03:57 -04:00
})
const data = await res.json()
setPosts(data)
setSearching(false)
}
fetchResults()
} else {
2022-03-24 21:03:57 -04:00
setPosts(initialPosts)
}
2022-03-24 21:03:57 -04:00
}, [initialPosts, search])
const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
setSearchValue(e.target.value)
}
const debouncedSearchHandler = useMemo(
() => debounce(handleSearchChange, 300)
, []);
useEffect(() => {
return () => {
debouncedSearchHandler.cancel();
}
}, [debouncedSearchHandler]);
2022-03-26 03:24:18 -04:00
const deletePost = useCallback((postId: string) => async () => {
const res = await fetch(`/server-api/posts/${postId}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${Cookies.get("drift-token")}`
},
})
if (!res.ok) {
console.error(res)
return
} else {
setPosts((posts) => posts.filter(post => post.id !== postId))
}
}, [])
2022-03-06 19:46:59 -05:00
return (
<div className={styles.container}>
<div className={styles.searchContainer}>
<Input scale={3 / 2}
clearable
2022-03-24 21:03:57 -04:00
placeholder="Search..."
onChange={debouncedSearchHandler} />
</div>
2022-03-06 19:46:59 -05:00
{error && <Text type='error'>Failed to load.</Text>}
2022-03-26 03:24:18 -04:00
{!posts.length && searching && <ul>
2022-03-24 21:03:57 -04:00
<li>
<ListItemSkeleton />
</li>
<li>
<ListItemSkeleton />
</li>
</ul>}
{posts?.length === 0 && !error && <Text type='secondary'>No posts found. Create one <NextLink passHref={true} href="/new"><Link color>here</Link></NextLink>.</Text>}
2022-03-06 19:46:59 -05:00
{
2022-03-24 21:03:57 -04:00
posts?.length > 0 && <div>
2022-03-06 19:46:59 -05:00
<ul>
2022-03-24 21:03:57 -04:00
{posts.map((post) => {
2022-03-26 03:24:18 -04:00
return <ListItem deletePost={deletePost(post.id)} post={post} key={post.id} />
2022-03-06 19:46:59 -05:00
})}
</ul>
</div>
}
{hasMorePosts && !setSearchValue && <div className={styles.moreContainer}>
<Button width={"100%"} onClick={loadMoreClick}>
Load more
</Button>
2022-03-24 21:03:57 -04:00
</div>}
</div>
2022-03-06 19:46:59 -05:00
)
}
2022-03-09 20:11:37 -05:00
export default PostList