ぼざクリタグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

109 lines
3.1 KiB

  1. import { useQuery } from '@tanstack/react-query'
  2. import { useLayoutEffect, useRef, useState } from 'react'
  3. import { Helmet } from 'react-helmet-async'
  4. import { useLocation } from 'react-router-dom'
  5. import PostList from '@/components/PostList'
  6. import PrefetchLink from '@/components/PrefetchLink'
  7. import TagSidebar from '@/components/TagSidebar'
  8. import WikiBody from '@/components/WikiBody'
  9. import Pagination from '@/components/common/Pagination'
  10. import TabGroup, { Tab } from '@/components/common/TabGroup'
  11. import MainArea from '@/components/layout/MainArea'
  12. import { SITE_TITLE } from '@/config'
  13. import { fetchPosts } from '@/lib/posts'
  14. import { postsKeys } from '@/lib/queryKeys'
  15. import { fetchWikiPageByTitle } from '@/lib/wiki'
  16. import type { FC } from 'react'
  17. import type { WikiPage } from '@/types'
  18. export default (() => {
  19. const containerRef = useRef<HTMLDivElement | null> (null)
  20. const [wikiPage, setWikiPage] = useState<WikiPage | null> (null)
  21. const location = useLocation ()
  22. const query = new URLSearchParams (location.search)
  23. const tagsQuery = query.get ('tags') ?? ''
  24. const anyFlg = query.get ('match') === 'any'
  25. const match = anyFlg ? 'any' : 'all'
  26. const tags = tagsQuery.split (' ').filter (e => e !== '')
  27. const tagsKey = tags.join (' ')
  28. const page = Number (query.get ('page') ?? 1)
  29. const limit = Number (query.get ('limit') ?? 20)
  30. const { data, isLoading: loading } = useQuery ({
  31. queryKey: postsKeys.index ({ tags: tagsKey, match, page, limit }),
  32. queryFn: () => fetchPosts ({ tags: tagsKey, match, page, limit }) })
  33. const posts = data?.posts ?? []
  34. const cursor = ''
  35. const totalPages = data ? Math.ceil (data.count / limit) : 0
  36. useLayoutEffect (() => {
  37. scroll (0, 0)
  38. setWikiPage (null)
  39. if (tags.length !== 1)
  40. return
  41. void (async () => {
  42. try
  43. {
  44. const tagName = tags[0]
  45. setWikiPage (await fetchWikiPageByTitle (tagName, { }))
  46. }
  47. catch
  48. {
  49. ;
  50. }
  51. }) ()
  52. }, [location.search])
  53. return (
  54. <div className="md:flex md:flex-1" ref={containerRef}>
  55. <Helmet>
  56. <title>
  57. {tags.length
  58. ? `${ tags.join (anyFlg ? ' or ' : ' and ') } | ${ SITE_TITLE }`
  59. : `${ SITE_TITLE } 〜 ぼざろクリーチャーシリーズ綜合リンク集サイト`}
  60. </title>
  61. </Helmet>
  62. <TagSidebar posts={posts.slice (0, 20)} onClick={() => {
  63. const statesToSave = {
  64. posts, cursor,
  65. scroll: containerRef.current?.scrollTop ?? 0 }
  66. sessionStorage.setItem (`posts:${ tagsQuery }`,
  67. JSON.stringify (statesToSave))
  68. }}/>
  69. <MainArea>
  70. <TabGroup>
  71. <Tab name="広場">
  72. {posts.length > 0
  73. ? (
  74. <>
  75. <PostList posts={posts}/>
  76. <Pagination page={page} totalPages={totalPages}/>
  77. </>)
  78. : !(loading) && '広場には何もありませんよ.'}
  79. {loading && 'Loading...'}
  80. </Tab>
  81. {tags.length === 1 && (
  82. <Tab name="Wiki">
  83. <WikiBody title={tags[0]} body={wikiPage?.body}/>
  84. <div className="my-2">
  85. <PrefetchLink to={`/wiki/${ encodeURIComponent (tags[0]) }`}>
  86. Wiki を見る
  87. </PrefetchLink>
  88. </div>
  89. </Tab>)}
  90. </TabGroup>
  91. </MainArea>
  92. </div>)
  93. }) satisfies FC