|
- import axios from 'axios'
- import toCamel from 'camelcase-keys'
- import { useEffect, useLayoutEffect, useRef, useState } from 'react'
- import { Helmet } from 'react-helmet-async'
- import { Link, useLocation, useNavigationType } from 'react-router-dom'
-
- import PostList from '@/components/PostList'
- import TagSidebar from '@/components/TagSidebar'
- import WikiBody from '@/components/WikiBody'
- import Pagination from '@/components/common/Pagination'
- import TabGroup, { Tab } from '@/components/common/TabGroup'
- import MainArea from '@/components/layout/MainArea'
- import { API_BASE_URL, SITE_TITLE } from '@/config'
-
- import type { Post, WikiPage } from '@/types'
-
-
- export default () => {
- const navigationType = useNavigationType ()
-
- const containerRef = useRef<HTMLDivElement | null> (null)
- const loaderRef = useRef<HTMLDivElement | null> (null)
-
- const [cursor, setCursor] = useState ('')
- const [loading, setLoading] = useState (false)
- const [posts, setPosts] = useState<Post[]> ([])
- const [totalPages, setTotalPages] = useState (0)
- const [wikiPage, setWikiPage] = useState<WikiPage | null> (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',
- ...(page && { page }),
- ...(limit && { limit }),
- ...(withCursor && { cursor }) } })
- const data = toCamel (res.data as any, { deep: true }) as {
- posts: Post[]
- count: number
- nextCursor: string }
- setPosts (posts => (
- [...((new Map ([...(withCursor ? posts : []), ...data.posts]
- .map (post => [post.id, post])))
- .values ())]))
- setCursor (data.nextCursor)
- setTotalPages (Math.ceil (data.count / limit))
-
- setLoading (false)
- }
-
- const location = useLocation ()
- const query = new URLSearchParams (location.search)
- const tagsQuery = query.get ('tags') ?? ''
- const anyFlg = query.get ('match') === 'any'
- const tags = tagsQuery.split (' ').filter (e => e !== '')
- const page = Number (query.get ('page') ?? 1)
- const limit = Number (query.get ('limit') ?? 20)
-
- useEffect(() => {
- const observer = new IntersectionObserver (entries => {
- if (entries[0].isIntersecting && !(loading) && cursor)
- loadMore (true)
- }, { threshold: 1 })
-
- const target = loaderRef.current
- target && observer.observe (target)
-
- return () => {
- target && observer.unobserve (target)
- }
- }, [loaderRef, loading])
-
- useLayoutEffect (() => {
- // TODO: 無限ロード用
- const savedState = /* sessionStorage.getItem (`posts:${ tagsQuery }`) */ null
- 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)
- }
-
- setWikiPage (null)
- if (tags.length === 1)
- {
- void (async () => {
- try
- {
- const tagName = tags[0]
- const res = await axios.get (`${ API_BASE_URL }/wiki/title/${ tagName }`)
- setWikiPage (toCamel (res.data as any, { deep: true }) as WikiPage)
- }
- catch
- {
- ;
- }
- }) ()
- }
- }, [location.search])
-
- return (
- <div className="md:flex md:flex-1" ref={containerRef}>
- <Helmet>
- <title>
- {tags.length
- ? `${ tags.join (anyFlg ? ' or ' : ' and ') } | ${ SITE_TITLE }`
- : `${ SITE_TITLE } 〜 ぼざろクリーチャーシリーズ綜合リンク集サイト`}
- </title>
- </Helmet>
-
- <TagSidebar posts={posts.slice (0, 20)}/>
-
- <MainArea>
- <TabGroup>
- <Tab name="広場">
- {posts.length > 0
- ? (
- <>
- <PostList posts={posts} onClick={() => {
- // TODO: 無限ロード用なので復活時に戻す.
- // const statesToSave = {
- // posts, cursor,
- // scroll: containerRef.current?.scrollTop ?? 0 }
- // sessionStorage.setItem (`posts:${ tagsQuery }`,
- // JSON.stringify (statesToSave))
- }}/>
- <Pagination page={page} totalPages={totalPages}/>
- </>)
- : !(loading) && '広場には何もありませんよ.'}
- {loading && 'Loading...'}
- {/* TODO: 無限ローディング復活までコメント・アウト */}
- {/* <div ref={loaderRef} className="h-12"/> */}
- </Tab>
- {tags.length === 1 && (
- <Tab name="Wiki">
- <WikiBody title={tags[0]} body={wikiPage?.body}/>
- <div className="my-2">
- <Link to={`/wiki/${ encodeURIComponent (tags[0]) }`}>
- Wiki を見る
- </Link>
- </div>
- </Tab>)}
- </TabGroup>
- </MainArea>
- </div>)
- }
|