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.
 
 
 
 
 
 

94 lines
2.7 KiB

  1. import axios from 'axios'
  2. import toCamel from 'camelcase-keys'
  3. import { useEffect, useState } from 'react'
  4. import { Accordion,
  5. AccordionItem,
  6. AccordionItemButton,
  7. AccordionItemHeading,
  8. AccordionItemPanel } from 'react-accessible-accordion'
  9. import { FaChevronDown, FaChevronUp } from 'react-icons/fa'
  10. import { Link } from 'react-router-dom'
  11. import ThreadNewForm from '@/components/threads/ThreadNewForm'
  12. import { API_BASE_URL } from '@/config'
  13. import type { Thread } from '@/types'
  14. export default () => {
  15. const [formOpen, setFormOpen] = useState (false)
  16. const [loading, setLoading] = useState (true)
  17. const [threads, setThreads] = useState<Thread[]> ([])
  18. const fetchThreads = async () => {
  19. setLoading (true)
  20. try
  21. {
  22. const res = await axios.get (`${ API_BASE_URL }/threads`)
  23. const data = toCamel (res.data as any, { deep: true }) as Thread[]
  24. const threads = data.filter (t => t.id !== 2)
  25. setThreads (threads.map (t => ({
  26. ...t,
  27. createdAt: (new Date (t.createdAt)).toLocaleString ('ja-JP-u-ca-japanese'),
  28. updatedAt: (new Date (t.updatedAt)).toLocaleString ('ja-JP-u-ca-japanese') })))
  29. }
  30. catch
  31. {
  32. ;
  33. }
  34. setLoading (false)
  35. }
  36. useEffect (() => {
  37. document.title = 'キケッツチャンネル お絵描き掲示板'
  38. fetchThreads ()
  39. }, [])
  40. return (
  41. <>
  42. <Accordion allowZeroExpanded
  43. onClick={() => setFormOpen (!formOpen)}
  44. className="mb-4 mx-auto">
  45. <AccordionItem>
  46. <AccordionItemHeading>
  47. <AccordionItemButton className="flex items-center">
  48. {formOpen ? <FaChevronUp /> : <FaChevronDown />}
  49. <span className="ml-2">新規スレッド</span>
  50. </AccordionItemButton>
  51. </AccordionItemHeading>
  52. <AccordionItemPanel>
  53. <ThreadNewForm />
  54. </AccordionItemPanel>
  55. </AccordionItem>
  56. </Accordion>
  57. <div>
  58. {loading ? 'Loading...' : (
  59. threads.length > 0
  60. ? threads.map (thread => (
  61. <div key={thread.id}
  62. className="bg-white dark:bg-gray-800 p-3 m-4
  63. border border-gray-400 rounded-xl">
  64. <div>
  65. <Link to={`/threads/${ thread.id }`}>
  66. <h3>{thread.name}</h3>
  67. </Link>
  68. <div className="my-2">
  69. {thread.description?.replaceAll ('\r\n', '\n')
  70. .replaceAll ('\r', '\n')
  71. .split ('\n')
  72. .map ((l, i) => <p key={i}>{l}</p>)}
  73. </div>
  74. </div>
  75. <div className="grid grid-cols-3 justify-between text-sm
  76. text-gray-600 dark:text-gray-300">
  77. <span className="text-left">{thread.postCount} レス</span>
  78. <span className="text-center">{thread.updatedAt} 更新</span>
  79. <span className="text-right">{thread.createdAt} 作成</span>
  80. </div>
  81. </div>))
  82. : 'スレないよ(笑).')}
  83. </div>
  84. </>)
  85. }