This commit is contained in:
@@ -1,16 +1,19 @@
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
import type { MouseEvent } from 'react'
|
||||||
import type { Post } from '@/types'
|
import type { Post } from '@/types'
|
||||||
|
|
||||||
type Props = { posts: Post[] }
|
type Props = { posts: Post[]
|
||||||
|
onClick?: (event: MouseEvent<HTMLElement>) => void }
|
||||||
|
|
||||||
|
|
||||||
export default ({ posts }: Props) => (
|
export default ({ posts, onClick }: Props) => (
|
||||||
<div className="flex flex-wrap gap-6 p-4">
|
<div className="flex flex-wrap gap-6 p-4">
|
||||||
{posts.map ((post, i) => (
|
{posts.map ((post, i) => (
|
||||||
<Link to={`/posts/${ post.id }`}
|
<Link to={`/posts/${ post.id }`}
|
||||||
key={i}
|
key={i}
|
||||||
className="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg">
|
className="w-40 h-40 overflow-hidden rounded-lg shadow-md hover:shadow-lg"
|
||||||
|
onClick={onClick}>
|
||||||
<img src={post.thumbnail || post.thumbnailBase || undefined}
|
<img src={post.thumbnail || post.thumbnailBase || undefined}
|
||||||
alt={post.title || post.url}
|
alt={post.title || post.url}
|
||||||
title={post.title || post.url || undefined}
|
title={post.title || post.url || undefined}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { StrictMode } from 'react'
|
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { HelmetProvider } from 'react-helmet-async'
|
import { HelmetProvider } from 'react-helmet-async'
|
||||||
|
|
||||||
@@ -8,8 +7,6 @@ import App from '@/App'
|
|||||||
const helmetContext = { }
|
const helmetContext = { }
|
||||||
|
|
||||||
createRoot (document.getElementById ('root')!).render (
|
createRoot (document.getElementById ('root')!).render (
|
||||||
<StrictMode>
|
<HelmetProvider context={helmetContext}>
|
||||||
<HelmetProvider context={helmetContext}>
|
<App />
|
||||||
<App />
|
</HelmetProvider>)
|
||||||
</HelmetProvider>
|
|
||||||
</StrictMode>)
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import toCamel from 'camelcase-keys'
|
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 { 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 PostList from '@/components/PostList'
|
||||||
import TagSidebar from '@/components/TagSidebar'
|
import TagSidebar from '@/components/TagSidebar'
|
||||||
@@ -15,23 +15,29 @@ import type { Post, WikiPage } from '@/types'
|
|||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
const navigationType = useNavigationType ()
|
||||||
|
|
||||||
|
const containerRef = useRef<HTMLDivElement | null> (null)
|
||||||
|
const loaderRef = useRef<HTMLDivElement | null> (null)
|
||||||
|
|
||||||
const [cursor, setCursor] = useState ('')
|
const [cursor, setCursor] = useState ('')
|
||||||
const [loading, setLoading] = useState (false)
|
const [loading, setLoading] = useState (false)
|
||||||
const [posts, setPosts] = useState<Post[]> ([])
|
const [posts, setPosts] = useState<Post[]> ([])
|
||||||
const [wikiPage, setWikiPage] = useState<WikiPage | null> (null)
|
const [wikiPage, setWikiPage] = useState<WikiPage | null> (null)
|
||||||
|
|
||||||
const loaderRef = useRef<HTMLDivElement | null> (null)
|
|
||||||
|
|
||||||
const loadMore = async (withCursor: boolean) => {
|
const loadMore = async (withCursor: boolean) => {
|
||||||
setLoading (true)
|
setLoading (true)
|
||||||
|
|
||||||
const res = await axios.get (`${ API_BASE_URL }/posts`, {
|
const res = await axios.get (`${ API_BASE_URL }/posts`, {
|
||||||
params: { tags: tags.join (' '),
|
params: { tags: tags.join (' '),
|
||||||
match: anyFlg ? 'any' : 'all',
|
match: anyFlg ? 'any' : 'all',
|
||||||
...(withCursor ? { cursor } : { }) } })
|
limit: '20',
|
||||||
|
...(withCursor && { cursor }) } })
|
||||||
const data = toCamel (res.data as any, { deep: true }) as { posts: Post[]
|
const data = toCamel (res.data as any, { deep: true }) as { posts: Post[]
|
||||||
nextCursor: string }
|
nextCursor: string }
|
||||||
setPosts (posts => [...(withCursor ? posts : []), ...data.posts])
|
setPosts (posts => [...(withCursor ? posts : []), ...data.posts])
|
||||||
setCursor (data.nextCursor)
|
setCursor (data.nextCursor)
|
||||||
|
|
||||||
setLoading (false)
|
setLoading (false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +61,25 @@ export default () => {
|
|||||||
}
|
}
|
||||||
}, [loaderRef, loading])
|
}, [loaderRef, loading])
|
||||||
|
|
||||||
useEffect (() => {
|
useLayoutEffect (() => {
|
||||||
setPosts ([])
|
const savedState = sessionStorage.getItem ('posts')
|
||||||
loadMore (false)
|
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)
|
setWikiPage (null)
|
||||||
if (tags.length === 1)
|
if (tags.length === 1)
|
||||||
@@ -78,7 +100,7 @@ export default () => {
|
|||||||
}, [location.search])
|
}, [location.search])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="md:flex md:flex-1">
|
<div className="md:flex md:flex-1" ref={containerRef}>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>
|
<title>
|
||||||
{tags.length
|
{tags.length
|
||||||
@@ -93,7 +115,13 @@ export default () => {
|
|||||||
<TabGroup>
|
<TabGroup>
|
||||||
<Tab name="広場">
|
<Tab name="広場">
|
||||||
{posts.length
|
{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 && 'Loading...'}
|
{loading && 'Loading...'}
|
||||||
<div ref={loaderRef} className="h-12"></div>
|
<div ref={loaderRef} className="h-12"></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user