2022-03-26 03:05:05 -04:00
|
|
|
import { Button, Code, Dot, Input, Note, Text } from "@geist-ui/core"
|
2022-03-10 02:46:59 -05:00
|
|
|
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"
|
2022-03-24 18:35:59 -04:00
|
|
|
import { Post } from "@lib/types"
|
2022-03-26 03:24:18 -04:00
|
|
|
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react"
|
2022-03-24 18:35:59 -04:00
|
|
|
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) => {
|
2022-03-24 18:35:59 -04:00
|
|
|
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)
|
2022-03-26 03:05:05 -04:00
|
|
|
const loadMoreClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
|
2022-03-24 21:03:57 -04:00
|
|
|
e.preventDefault()
|
|
|
|
if (hasMorePosts) {
|
|
|
|
async function fetchPosts() {
|
2022-03-26 03:05:05 -04:00
|
|
|
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])
|
2022-03-24 18:35:59 -04:00
|
|
|
|
|
|
|
// 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 18:35:59 -04:00
|
|
|
}
|
2022-03-24 21:03:57 -04:00
|
|
|
})
|
|
|
|
const data = await res.json()
|
|
|
|
setPosts(data)
|
|
|
|
setSearching(false)
|
|
|
|
}
|
|
|
|
fetchResults()
|
2022-03-24 18:35:59 -04:00
|
|
|
} else {
|
2022-03-24 21:03:57 -04:00
|
|
|
setPosts(initialPosts)
|
2022-03-24 18:35:59 -04:00
|
|
|
}
|
2022-03-24 21:03:57 -04:00
|
|
|
}, [initialPosts, search])
|
2022-03-24 18:35:59 -04:00
|
|
|
|
|
|
|
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}>
|
2022-03-24 18:35:59 -04:00
|
|
|
<div className={styles.searchContainer}>
|
|
|
|
<Input scale={3 / 2}
|
|
|
|
clearable
|
2022-03-24 21:03:57 -04:00
|
|
|
placeholder="Search..."
|
2022-03-24 18:35:59 -04:00
|
|
|
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>}
|
2022-03-29 03:11:02 -04:00
|
|
|
{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>
|
|
|
|
}
|
2022-03-26 03:37:01 -04:00
|
|
|
{hasMorePosts && !setSearchValue && <div className={styles.moreContainer}>
|
2022-03-26 03:05:05 -04:00
|
|
|
<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
|