diff --git a/frontend/src/components/PostList.tsx b/frontend/src/components/PostList.tsx index 4390acc..10fdd29 100644 --- a/frontend/src/components/PostList.tsx +++ b/frontend/src/components/PostList.tsx @@ -1,16 +1,19 @@ import { Link } from 'react-router-dom' +import type { MouseEvent } from 'react' import type { Post } from '@/types' -type Props = { posts: Post[] } +type Props = { posts: Post[] + onClick?: (event: MouseEvent) => void } -export default ({ posts }: Props) => ( +export default ({ posts, onClick }: Props) => (
{posts.map ((post, i) => ( + className="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg" + onClick={onClick}> {post.title - - - - ) + + + ) diff --git a/frontend/src/pages/posts/PostListPage.tsx b/frontend/src/pages/posts/PostListPage.tsx index 51556b8..a375014 100644 --- a/frontend/src/pages/posts/PostListPage.tsx +++ b/frontend/src/pages/posts/PostListPage.tsx @@ -1,8 +1,8 @@ import axios from 'axios' import toCamel from 'camelcase-keys' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useLayoutEffect, useRef, useState } from 'react' import { Helmet } from 'react-helmet-async' -import { Link, useLocation } from 'react-router-dom' +import { Link, useLocation, useNavigationType } from 'react-router-dom' import PostList from '@/components/PostList' import TagSidebar from '@/components/TagSidebar' @@ -15,23 +15,29 @@ import type { Post, WikiPage } from '@/types' export default () => { + const navigationType = useNavigationType () + + const containerRef = useRef (null) + const loaderRef = useRef (null) + const [cursor, setCursor] = useState ('') const [loading, setLoading] = useState (false) const [posts, setPosts] = useState ([]) const [wikiPage, setWikiPage] = useState (null) - const loaderRef = useRef (null) - const loadMore = async (withCursor: boolean) => { setLoading (true) + const res = await axios.get (`${ API_BASE_URL }/posts`, { params: { tags: tags.join (' '), match: anyFlg ? 'any' : 'all', - ...(withCursor ? { cursor } : { }) } }) + limit: '20', + ...(withCursor && { cursor }) } }) const data = toCamel (res.data as any, { deep: true }) as { posts: Post[] nextCursor: string } setPosts (posts => [...(withCursor ? posts : []), ...data.posts]) setCursor (data.nextCursor) + setLoading (false) } @@ -55,9 +61,25 @@ export default () => { } }, [loaderRef, loading]) - useEffect (() => { - setPosts ([]) - loadMore (false) + useLayoutEffect (() => { + const savedState = sessionStorage.getItem ('posts') + if (savedState && navigationType === 'POP') + { + const { posts, cursor, scroll } = JSON.parse (savedState) + setPosts (posts) + setCursor (cursor) + + if (containerRef.current) + containerRef.current.scrollTop = scroll + + loadMore (true) + } + else + { + setPosts ([]) + loadMore (false) + } + sessionStorage.removeItem ('posts') setWikiPage (null) if (tags.length === 1) @@ -78,7 +100,7 @@ export default () => { }, [location.search]) return ( -
+
{tags.length @@ -93,7 +115,13 @@ export default () => { <TabGroup> <Tab name="広場"> {posts.length - ? <PostList posts={posts} /> + ? ( + <PostList posts={posts} onClick={() => { + const statesToSave = { + posts, cursor, + scroll: containerRef.current?.scrollTop ?? 0 } + sessionStorage.setItem ('posts', JSON.stringify (statesToSave)) + }} />) : !(loading) && '広場には何もありませんよ.'} {loading && 'Loading...'} <div ref={loaderRef} className="h-12"></div>