ぼざクリ タグ広場 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.
 
 
 
 
 
 

137 lines
3.7 KiB

  1. import axios from 'axios'
  2. import toCamel from 'camelcase-keys'
  3. import { useEffect, useState } from 'react'
  4. import { Helmet } from 'react-helmet-async'
  5. import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
  6. import PostList from '@/components/PostList'
  7. import TagLink from '@/components/TagLink'
  8. import WikiBody from '@/components/WikiBody'
  9. import PageTitle from '@/components/common/PageTitle'
  10. import TabGroup, { Tab } from '@/components/common/TabGroup'
  11. import MainArea from '@/components/layout/MainArea'
  12. import { API_BASE_URL, SITE_TITLE } from '@/config'
  13. import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
  14. import type { Post, Tag, WikiPage } from '@/types'
  15. export default () => {
  16. const params = useParams ()
  17. const title = params.title ?? ''
  18. const location = useLocation ()
  19. const navigate = useNavigate ()
  20. const defaultTag = { name: title, category: 'general' } as Tag
  21. const [posts, setPosts] = useState<Post[]> ([])
  22. const [tag, setTag] = useState (defaultTag)
  23. const [wikiPage, setWikiPage] = useState<WikiPage | null | undefined> (undefined)
  24. const query = new URLSearchParams (location.search)
  25. const version = query.get ('version')
  26. useEffect (() => {
  27. if (/^\d+$/.test (title))
  28. {
  29. void (async () => {
  30. const res = await axios.get (`${ API_BASE_URL }/wiki/${ title }`)
  31. const data = res.data as WikiPage
  32. navigate (`/wiki/${ data.title }`, { replace: true })
  33. }) ()
  34. return
  35. }
  36. void (async () => {
  37. try
  38. {
  39. const res = await axios.get (
  40. `${ API_BASE_URL }/wiki/title/${ encodeURIComponent (title) }`,
  41. { params: version ? { version } : { } })
  42. const data = toCamel (res.data as any, { deep: true }) as WikiPage
  43. setWikiPage (data)
  44. WikiIdBus.set (data.id)
  45. }
  46. catch
  47. {
  48. setWikiPage (null)
  49. }
  50. }) ()
  51. void (async () => {
  52. try
  53. {
  54. const res = await axios.get (
  55. `${ API_BASE_URL }/posts?${ new URLSearchParams ({ tags: title,
  56. limit: '8' }) }`)
  57. const data = toCamel (res.data as any,
  58. { deep: true }) as { posts: Post[]
  59. nextCursor: string }
  60. setPosts (data.posts)
  61. }
  62. catch
  63. {
  64. setPosts ([])
  65. }
  66. }) ()
  67. void (async () => {
  68. try
  69. {
  70. const res = await axios.get (
  71. `${ API_BASE_URL }/tags/name/${ encodeURIComponent (title) }`)
  72. setTag (toCamel (res.data as any, { deep: true }) as Tag)
  73. }
  74. catch
  75. {
  76. setTag (defaultTag)
  77. }
  78. }) ()
  79. return () => WikiIdBus.set (null)
  80. }, [title, location.search])
  81. return (
  82. <MainArea>
  83. <Helmet>
  84. <title>{`${ title } Wiki | ${ SITE_TITLE }`}</title>
  85. </Helmet>
  86. {(wikiPage && version) && (
  87. <div className="text-sm flex gap-3 items-center justify-center border border-gray-700 rounded px-2 py-1 mb-4">
  88. {wikiPage.pred ? (
  89. <Link to={`/wiki/${ encodeURIComponent (title) }?version=${ wikiPage.pred }`}>
  90. &lt; 古
  91. </Link>) : '(最古)'}
  92. <span>{wikiPage.updatedAt}</span>
  93. {wikiPage.succ ? (
  94. <Link to={`/wiki/${ encodeURIComponent (title) }?version=${ wikiPage.succ }`}>
  95. 新 &gt;
  96. </Link>) : '(最新)'}
  97. </div>)}
  98. <PageTitle>
  99. <TagLink tag={tag}
  100. withWiki={false}
  101. withCount={false}
  102. {...(version && { to: `/wiki/${ encodeURIComponent (title) }` })} />
  103. </PageTitle>
  104. <div className="prose mx-auto p-4">
  105. {wikiPage === undefined
  106. ? 'Loading...'
  107. : <WikiBody body={wikiPage?.body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`} />}
  108. </div>
  109. {(!(version) && posts.length > 0) && (
  110. <TabGroup>
  111. <Tab name="広場">
  112. <PostList posts={posts} />
  113. </Tab>
  114. </TabGroup>)}
  115. </MainArea>)
  116. }