Foro: selector para fijar el personaje del autor

- Modal "Cambiar personaje" (diseño de la web) con filtro, avatar de raza,
  nombre en color de clase y subtítulo nivel/raza/clase.
- Barra "Publicas como: X · Cambiar personaje" sobre el formulario de respuesta
  (solo con sesión iniciada).
- Preferencia persistente en acore_web.forum_character (por cuenta), validada.
- resolvePosters usa el personaje fijado si existe; si no, el de más nivel.
- API /api/forum/character (GET lista + actual, POST fijar), gated por sesión.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 08:14:54 +00:00
parent bf9931e042
commit 0f6e0d514f
7 changed files with 311 additions and 4 deletions
@@ -7,6 +7,8 @@ import {
countPosts,
resolvePosters,
getUserPostCount,
getAccountCharactersForForum,
getForumCharacterPick,
} from '@/lib/forum'
import { formatForumDate } from '@/lib/forum-format'
import { localizeWowheadLinks } from '@/lib/forum-wowhead'
@@ -17,6 +19,7 @@ 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 { EditPostForm } from '@/components/EditPostForm'
import { ForumModActions } from '@/components/ForumModActions'
import { WowheadRefresh } from '@/components/WowheadRefresh'
@@ -58,6 +61,10 @@ export default async function TopicPage({
await Promise.all(uniquePosters.map(async (pid) => [pid, await getUserPostCount(pid)] as const)),
)
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)
@@ -199,7 +206,22 @@ export default async function TopicPage({
{canReply ? (
page === totalPages ? (
<ReplyForm topicId={id} />
<>
<ForumCharacterPicker
locale={locale}
initialChars={forumChars}
initialCurrent={forumPick}
labels={{
postingAs: t('postingAs'),
change: t('changeCharacter'),
title: t('changeCharacter'),
filter: t('filter'),
empty: t('noCharacters'),
auto: t('autoCharacter'),
}}
/>
<ReplyForm topicId={id} />
</>
) : (
<p className="forum-empty">
<a href={`/forum/topic/${id}?page=${totalPages}`}>{t('replyOnLastPage')}</a>