diff --git a/resources/js/pages/BoardIndex.jsx b/resources/js/pages/BoardIndex.jsx index a171a85..8497ef9 100644 --- a/resources/js/pages/BoardIndex.jsx +++ b/resources/js/pages/BoardIndex.jsx @@ -96,7 +96,48 @@ export default function BoardIndex() { nodes.forEach((node) => sortNodes(node.children)) } + 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 } + } + sortNodes(roots) + roots.forEach((root) => aggregateNodes(root)) return roots }, [forums]) diff --git a/resources/js/pages/ForumView.jsx b/resources/js/pages/ForumView.jsx index ce9e15c..c6ff2bf 100644 --- a/resources/js/pages/ForumView.jsx +++ b/resources/js/pages/ForumView.jsx @@ -1,7 +1,7 @@ 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, listForumsByParent, listThreadsByForum } from '../api/client' +import { createThread, getForum, listAllForums, listThreadsByForum } from '../api/client' import PortalTopicRow from '../components/PortalTopicRow' import { useAuth } from '../context/AuthContext' import { useTranslation } from 'react-i18next' @@ -71,6 +71,77 @@ export default function ForumView() { )) + 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 @@ -81,9 +152,11 @@ export default function ForumView() { const forumData = await getForum(id) if (!active) return setForum(forumData) - const childData = await listForumsByParent(id) + const allForums = await listAllForums() if (!active) return - setChildren(childData) + 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