import { useEffect, useState } from 'react'
import { Button, Badge, Card, Col, Container, Form, Modal, Row } from 'react-bootstrap'
import { Link, useParams } from 'react-router-dom'
import { createThread, getForum, listAllForums, listThreadsByForum } from '../api/client'
import PortalTopicRow from '../components/PortalTopicRow'
import { useAuth } from '../context/AuthContext'
import { useTranslation } from 'react-i18next'
export default function ForumView() {
const { id } = useParams()
const { token } = useAuth()
const [forum, setForum] = useState(null)
const [children, setChildren] = useState([])
const [threads, setThreads] = useState([])
const [error, setError] = useState('')
const [loading, setLoading] = useState(true)
const [showModal, setShowModal] = useState(false)
const [title, setTitle] = useState('')
const [body, setBody] = useState('')
const [saving, setSaving] = useState(false)
const { t } = useTranslation()
const renderChildRows = (nodes) =>
nodes.map((node) => (
{node.name}
{node.description || ''}
{node.threads_count ?? 0}
{node.views_count ?? 0}
{node.last_post_at ? (
{t('thread.by')}{' '}
{node.last_post_user_id ? (
{node.last_post_user_name || t('thread.anonymous')}
) : (
{node.last_post_user_name || t('thread.anonymous')}
)}
{node.last_post_at.slice(0, 10)}
) : (
{t('thread.no_replies')}
)}
))
const getParentId = (node) => {
if (!node.parent) return null
if (typeof node.parent === 'string') {
return node.parent.split('/').pop()
}
return node.parent.id ?? null
}
const buildForumTree = (allForums) => {
const map = new Map()
const roots = []
allForums.forEach((item) => {
map.set(String(item.id), { ...item, children: [] })
})
allForums.forEach((item) => {
const parentId = getParentId(item)
const node = map.get(String(item.id))
if (parentId && map.has(String(parentId))) {
map.get(String(parentId)).children.push(node)
} else {
roots.push(node)
}
})
const aggregateNodes = (node) => {
if (!node.children?.length) {
return {
threads: node.threads_count ?? 0,
views: node.views_count ?? 0,
posts: node.posts_count ?? 0,
last: node.last_post_at ? { at: node.last_post_at, node } : null,
}
}
let threads = node.threads_count ?? 0
let views = node.views_count ?? 0
let posts = node.posts_count ?? 0
let last = node.last_post_at ? { at: node.last_post_at, node } : null
node.children.forEach((child) => {
const agg = aggregateNodes(child)
threads += agg.threads
views += agg.views
posts += agg.posts
if (agg.last && (!last || agg.last.at > last.at)) {
last = agg.last
}
})
node.threads_count = threads
node.views_count = views
node.posts_count = posts
if (last) {
const source = last.node
node.last_post_at = source.last_post_at
node.last_post_user_id = source.last_post_user_id
node.last_post_user_name = source.last_post_user_name
node.last_post_user_rank_color = source.last_post_user_rank_color
node.last_post_user_group_color = source.last_post_user_group_color
}
return { threads, views, posts, last }
}
roots.forEach((root) => aggregateNodes(root))
return map
}
useEffect(() => {
let active = true
const loadData = async () => {
setLoading(true)
setError('')
try {
const forumData = await getForum(id)
if (!active) return
setForum(forumData)
const allForums = await listAllForums()
if (!active) return
const treeMap = buildForumTree(allForums)
const currentNode = treeMap.get(String(forumData.id))
setChildren(currentNode?.children ?? [])
if (forumData.type === 'forum') {
const threadData = await listThreadsByForum(id)
if (!active) return
setThreads(threadData)
} else {
setThreads([])
}
} catch (err) {
if (active) setError(err.message)
} finally {
if (active) setLoading(false)
}
}
loadData()
return () => {
active = false
}
}, [id])
const handleSubmit = async (event) => {
event.preventDefault()
setSaving(true)
setError('')
try {
await createThread({ title, body, forumId: id })
setTitle('')
setBody('')
const updated = await listThreadsByForum(id)
setThreads(updated)
setShowModal(false)
} catch (err) {
setError(err.message)
} finally {
setSaving(false)
}
}
return (
{loading && {t('forum.loading')}
}
{error && {error}
}
{forum && (
<>
{forum.type !== 'forum' && (
{children.length > 0 ? (
renderChildRows(children)
) : (
{t('forum.empty_children')}
)}
)}
{forum.type === 'forum' && (
<>
{threads.length} {t('forum.threads').toLowerCase()}
{!token && {t('forum.login_hint')}
}
{t('portal.topic')}
{t('thread.replies')}
{t('thread.views')}
{t('thread.last_post')}
{threads.length === 0 && (
{t('forum.empty_threads')}
)}
{threads.map((thread) => (
))}
>
)}
>
)}
{forum?.type === 'forum' && (
setShowModal(false)} centered size="lg">
{t('forum.start_thread')}
{!token && {t('forum.login_hint')}
}
{t('form.title')}
setTitle(event.target.value)}
disabled={!token || saving}
required
/>
{t('form.body')}
setBody(event.target.value)}
disabled={!token || saving}
required
/>
)}
)
}