import { useMemo } from 'react'; import { ListTree, MessageCircle, Tags, Link2, UserPlus } from 'lucide-react'; import { useLocation, useSearchParams, useNavigate } from 'react-router-dom'; import type { AsideWidget, RecentComment, RecentUser, TagCount, User, ForumStats, FriendLink } from '../api/types'; import type { PostHeading } from '../utils/postHeadings'; import { useSiteBranding } from '../hooks/useSiteBranding'; import { formatShortDateTime, formatTime } from '../utils/content'; import { resolveAsideWidgets } from '../utils/asideWidgets'; import TagCloud from './TagCloud'; import UserLink from './UserLink'; import ArticleOutline from './ArticleOutline'; import PostAuthorCard from './PostAuthorCard'; import AsideCheckInStrip from './AsideCheckInStrip'; import ShowcaseAsideWidget from './ShowcaseAsideWidget'; export type PostDetailAside = { author?: User | null; publishedAt?: string; viewCount?: number; headings: PostHeading[]; scrollRoot?: HTMLElement | null; outlineTitle?: string; }; interface Props { recentComments: RecentComment[]; recentUsers: RecentUser[]; tags?: TagCount[]; tagsLoading?: boolean; stats?: ForumStats | null; onPostClick: (id: number, opts?: { floor?: number }) => void; /** 首次拉取中:不渲染该块内容(由冷启动门闩保证首屏已齐或空白) */ loading?: boolean; /** 右侧栏可选组件顺序与开关 */ asideWidgets: AsideWidget[]; /** 帖子详情:右侧顶部展示作者与目录 */ postDetail?: PostDetailAside | null; } export default function RightPanel({ recentComments, recentUsers, tags = [], tagsLoading = false, stats = null, onPostClick, loading = false, asideWidgets, postDetail = null, }: Props) { const { branding } = useSiteBranding(); const nav = useNavigate(); const loc = useLocation(); const [params] = useSearchParams(); const activeTag = params.get('tag') || ''; const commentList = recentComments?.slice(0, 6) ?? []; const userList = recentUsers?.slice(0, 8) ?? []; const friendLinks = (branding.friend_links ?? []).filter( (l: FriendLink) => l.name?.trim() && l.url?.trim(), ); const isSiteHome = loc.pathname === '/' && !params.get('board') && !params.get('keyword') && !params.get('tag') && !params.get('author'); const description = branding.description?.trim() || ''; const slogan = branding.slogan?.trim() || ''; const introText = description || slogan; const isPostDetail = !!postDetail; const enabledWidgets = useMemo( () => resolveAsideWidgets({ aside_widgets: asideWidgets, aside_show_tag_cloud: false, aside_show_recent_comments: false, aside_show_friend_links: false, }).filter(w => w.enabled), [asideWidgets], ); const renderWidget = (widget: AsideWidget) => { switch (widget.id) { case 'showcase': return ; case 'friend_links': return (
申请
{friendLinks.length === 0 ? (
暂无友情链接
) : ( <>
    {friendLinks.slice(0, 8).map((link: FriendLink) => (
  • {link.name}
  • ))}
{friendLinks.length > 8 && ( )} )}
); case 'tag_cloud': return (
标签云
); case 'recent_comments': return (
最新评论
{loading && commentList.length === 0 ? null : commentList.length === 0 ? (
暂无评论
) : commentList.map(item => (
{item.user_id ? ( {item.avatar ? : (item.author?.[0] || '?')} ) : ( {item.avatar ? : (item.author?.[0] || '?')} )}
))}
); case 'recent_users': return (
最新注册
{loading && userList.length === 0 ? null : userList.length === 0 ? (
暂无用户
) : (
{userList.map(item => ( {item.avatar ? : (item.nickname?.[0] || '?')} {item.nickname} ))}
)}
); default: return null; } }; return (
{isPostDetail && ( <> {postDetail.author?.id ? ( ) : null}
{postDetail.outlineTitle || '文章目录'}
)} {!isPostDetail && (
{isSiteHome ? (

{branding.name}

) : (

{branding.name}

)} {introText && (

{introText}

)}
{stats && (
{stats.posts} 帖子
{stats.comments} 回复
{stats.users} 用户
)}
)} {!isPostDetail && enabledWidgets.map(renderWidget)}
); }