181 lines
6.3 KiB
JavaScript
181 lines
6.3 KiB
JavaScript
import { useEffect, useState } from 'react'
|
|
import { Button, Card, Col, Container, Form, Row } from 'react-bootstrap'
|
|
import { Link, useParams } from 'react-router-dom'
|
|
import { createThread, getForum, listForumsByParent, listThreadsByForum } from '../api/client'
|
|
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 [title, setTitle] = useState('')
|
|
const [body, setBody] = useState('')
|
|
const [saving, setSaving] = useState(false)
|
|
const { t } = useTranslation()
|
|
|
|
useEffect(() => {
|
|
let active = true
|
|
|
|
const loadData = async () => {
|
|
setLoading(true)
|
|
setError('')
|
|
try {
|
|
const forumData = await getForum(id)
|
|
if (!active) return
|
|
setForum(forumData)
|
|
const childData = await listForumsByParent(id)
|
|
if (!active) return
|
|
setChildren(childData)
|
|
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)
|
|
} catch (err) {
|
|
setError(err.message)
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Container className="py-5">
|
|
{loading && <p className="bb-muted">{t('forum.loading')}</p>}
|
|
{error && <p className="text-danger">{error}</p>}
|
|
{forum && (
|
|
<>
|
|
<div className="bb-hero mb-4">
|
|
<p className="bb-chip">
|
|
{forum.type === 'forum' ? t('forum.type_forum') : t('forum.type_category')}
|
|
</p>
|
|
<h2 className="mt-3">{forum.name}</h2>
|
|
<p className="bb-muted mb-0">
|
|
{forum.description || t('forum.no_description')}
|
|
</p>
|
|
</div>
|
|
|
|
<Row className="g-4">
|
|
<Col lg={7}>
|
|
<h4 className="bb-section-title mb-3">{t('forum.children')}</h4>
|
|
{children.length === 0 && (
|
|
<p className="bb-muted">{t('forum.empty_children')}</p>
|
|
)}
|
|
{children.map((child) => (
|
|
<Card className="bb-card mb-3" key={child.id}>
|
|
<Card.Body>
|
|
<Card.Title>{child.name}</Card.Title>
|
|
<Card.Text className="bb-muted">
|
|
{child.description || t('forum.no_description')}
|
|
</Card.Text>
|
|
<Link to={`/forum/${child.id}`} className="stretched-link">
|
|
{t('forum.open')}
|
|
</Link>
|
|
</Card.Body>
|
|
</Card>
|
|
))}
|
|
|
|
{forum.type === 'forum' && (
|
|
<>
|
|
<h4 className="bb-section-title mb-3 mt-4">{t('forum.threads')}</h4>
|
|
{threads.length === 0 && (
|
|
<p className="bb-muted">{t('forum.empty_threads')}</p>
|
|
)}
|
|
{threads.map((thread) => (
|
|
<Card className="bb-card mb-3" key={thread.id}>
|
|
<Card.Body>
|
|
<Card.Title>{thread.title}</Card.Title>
|
|
<Card.Text className="bb-muted">
|
|
{thread.body.length > 160
|
|
? `${thread.body.slice(0, 160)}...`
|
|
: thread.body}
|
|
</Card.Text>
|
|
<Link to={`/thread/${thread.id}`} className="stretched-link">
|
|
{t('thread.view')}
|
|
</Link>
|
|
</Card.Body>
|
|
</Card>
|
|
))}
|
|
</>
|
|
)}
|
|
</Col>
|
|
<Col lg={5}>
|
|
<h4 className="bb-section-title mb-3">{t('forum.start_thread')}</h4>
|
|
<div className="bb-form">
|
|
{forum.type !== 'forum' && (
|
|
<p className="bb-muted mb-3">{t('forum.only_forums')}</p>
|
|
)}
|
|
{forum.type === 'forum' && !token && (
|
|
<p className="bb-muted mb-3">{t('forum.login_hint')}</p>
|
|
)}
|
|
<Form onSubmit={handleSubmit}>
|
|
<Form.Group className="mb-3">
|
|
<Form.Label>{t('form.title')}</Form.Label>
|
|
<Form.Control
|
|
type="text"
|
|
placeholder={t('form.thread_title_placeholder')}
|
|
value={title}
|
|
onChange={(event) => setTitle(event.target.value)}
|
|
disabled={!token || saving || forum.type !== 'forum'}
|
|
required
|
|
/>
|
|
</Form.Group>
|
|
<Form.Group className="mb-3">
|
|
<Form.Label>{t('form.body')}</Form.Label>
|
|
<Form.Control
|
|
as="textarea"
|
|
rows={5}
|
|
placeholder={t('form.thread_body_placeholder')}
|
|
value={body}
|
|
onChange={(event) => setBody(event.target.value)}
|
|
disabled={!token || saving || forum.type !== 'forum'}
|
|
required
|
|
/>
|
|
</Form.Group>
|
|
<Button
|
|
type="submit"
|
|
variant="dark"
|
|
disabled={!token || saving || forum.type !== 'forum'}
|
|
>
|
|
{saving ? t('form.posting') : t('form.create_thread')}
|
|
</Button>
|
|
</Form>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</>
|
|
)}
|
|
</Container>
|
|
)
|
|
}
|