import { notFound } from 'next/navigation' import { getTranslations, setRequestLocale } from 'next-intl/server' import { getTopic, getTopicPath, getPosts, countPosts, resolvePosters, getUserPostCount, getAccountCharactersForForum, getForumCharacterPick, } from '@/lib/forum' import { getPostsSocial, REPORT_REASONS } from '@/lib/forum-social' import { formatForumDate } from '@/lib/forum-format' import { localizeWowheadLinks } from '@/lib/forum-wowhead' import { getSession } from '@/lib/session' import { className, classColor } from '@/lib/wow-data' import { forumIsModerator, canEditPost } from '@/lib/forum-perm' import { PageShell } from '@/components/PageShell' import { ForumBreadcrumb } from '@/components/ForumBreadcrumb' import { Pagination } from '@/components/Pagination' import { ReplyForm } from '@/components/ReplyForm' import { ForumCharacterPicker } from '@/components/ForumCharacterPicker' import { PostActions } from '@/components/PostActions' import { EditPostForm } from '@/components/EditPostForm' import { ForumModActions } from '@/components/ForumModActions' import { WowheadRefresh } from '@/components/WowheadRefresh' export const dynamic = 'force-dynamic' const PER_PAGE = 5 export default async function TopicPage({ params, searchParams, }: { params: Promise<{ locale: string; topicId: string }> searchParams: Promise<{ page?: string }> }) { const { locale, topicId } = await params setRequestLocale(locale) const t = await getTranslations('Forum') const id = Number(topicId) const session = await getSession() const isMod = await forumIsModerator(session) const topic = await getTopic(id, isMod) if (!topic) notFound() const total = await countPosts(id, isMod) const totalPages = Math.max(1, Math.ceil(total / PER_PAGE)) const page = Math.min(Math.max(1, Number((await searchParams).page) || 1), totalPages) const rawPosts = await getPosts(id, (page - 1) * PER_PAGE, PER_PAGE, isMod) // Enlaces de wowhead en el idioma de QUIEN LEE (no de quien posteó). const posts = await Promise.all( rawPosts.map(async (p) => ({ ...p, text: await localizeWowheadLinks(p.text, locale) })), ) const posters = await resolvePosters(posts.map((p) => p.poster)) // Nº de posts por autor visible (para el panel lateral), en paralelo. const uniquePosters = [...new Set(posts.map((p) => p.poster))] const counts = new Map( await Promise.all(uniquePosters.map(async (pid) => [pid, await getUserPostCount(pid)] as const)), ) const social = await getPostsSocial(posts.map((p) => p.id), session.accountId ?? 0) const REASON_DESC: Record = { troll: t('rTrollDesc'), threat: t('rThreatDesc'), username: t('rUsernameDesc'), offtopic: t('rOfftopicDesc'), inappropriate: t('rInappropriateDesc'), spam: t('rSpamDesc'), violence: t('rViolenceDesc'), child: t('rChildDesc'), illegal: t('rIllegalDesc'), } const REASON_LABEL: Record = { troll: t('rTroll'), threat: t('rThreat'), username: t('rUsername'), offtopic: t('rOfftopic'), inappropriate: t('rInappropriate'), spam: t('rSpam'), violence: t('rViolence'), child: t('rChild'), illegal: t('rIllegal'), } const reportReasons = REPORT_REASONS.map((k) => ({ key: k, label: REASON_LABEL[k] || k, desc: REASON_DESC[k] || '' })) const paLabels = { like: t('like'), copyLink: t('copyLink'), copied: t('copied'), report: t('report'), bookmark: t('bookmark'), bookmarked: t('bookmarked'), loginRequired: t('loginToPost'), reportTitle: t('reportTitle'), reportIntro: t('reportIntro'), reportDetail: t('reportDetail'), reportSubmit: t('reportSubmit'), reportSent: t('reportSent'), reminderTitle: t('reminderTitle'), savedBookmark: t('savedBookmark'), remind2h: t('remind2h'), remindTomorrow: t('remindTomorrow'), remind3days: t('remind3days'), remindMore: t('remindMore'), editTitle: t('editBookmark'), notePlaceholder: t('notePlaceholder'), save: t('save'), cancel: t('cancel'), remove: t('removeBookmark'), reminderLabel: t('reminderLabel'), rNone: t('rNone'), rTomorrow: t('rmTomorrow'), rNextWeek: t('rmNextWeek'), rNextMonth: t('rmNextMonth'), rCustom: t('rmCustom'), } const paCanAct = Boolean(session.accountId) const paLoginHref = `/${locale}/my-account` const [forumChars, forumPick] = session.accountId ? await Promise.all([getAccountCharactersForForum(session.accountId), getForumCharacterPick(session.accountId)]) : [[], null] const path = await getTopicPath(id, locale) const canReply = Boolean(session.username) && (!topic.locked || isMod) return ( {topic.sticky && `[${t('sticky')}] `} {topic.locked && `[${t('locked')}] `} {topic.deleted && `[${t('deleted')}] `} {topic.name} } >
{path && ( )}

{topic.name}

{formatForumDate(topic.created, locale)}

{total} {t('posts')}

`/forum/topic/${id}?page=${p}`} />
{posts.map((post) => { const author = posters.get(post.poster) const isStaff = author?.isStaff ?? false return (
{author?.charName ?? author?.name ?? `#${post.poster}`} {author?.guildName ? <{author.guildName}> : null}

{isStaff ? t('staff') : t('member')}

{formatForumDate(author?.joindate ?? null, locale) || '—'}

{author?.charName ? ( <>

{(author.achPoints ?? 0).toLocaleString(locale)}

{t('level')} {author.level}

{className(author.classId, locale)}

) : null}

{t('posts')}: {counts.get(post.poster) ?? 0}

  • {formatForumDate(post.time, locale)}
  • {canEditPost(session, isMod, post.poster) && (
  • )} {isMod && (
  • )}
) })}
`/forum/topic/${id}?page=${p}`} />
{isMod && (
)} {canReply ? ( page === totalPages ? ( <> ) : (

{t('replyOnLastPage')}

) ) : ( !session.username &&

{t('loginToPost')}

)}
) }