import { Flame, ListTree, MessageCircle, Tags, Sparkles } from 'lucide-react';
import { useLocation, useSearchParams } from 'react-router-dom';
import { Skeleton } from '@/components/ui/skeleton';
import type { PostItem, RecentComment, TagCount, User } from '../api/types';
import type { PostHeading } from '../utils/postHeadings';
import { useSiteBranding } from '../hooks/useSiteBranding';
import { formatShortDateTime } from '../utils/content';
import TagCloud from './TagCloud';
import UserLink from './UserLink';
import ArticleOutline from './ArticleOutline';
import PostAuthorCard from './PostAuthorCard';
export type PostDetailAside = {
author?: User | null;
publishedAt?: string;
viewCount?: number;
headings: PostHeading[];
scrollRoot?: HTMLElement | null;
outlineTitle?: string;
};
interface Props {
hot: PostItem[];
recentComments: RecentComment[];
tags?: TagCount[];
tagsLoading?: boolean;
onPostClick: (id: number, opts?: { floor?: number }) => void;
/** 首次拉取中,显示骨架避免空态闪烁 */
loading?: boolean;
/** 帖子详情:右侧顶部展示作者与目录 */
postDetail?: PostDetailAside | null;
}
function hotRankClass(index: number): string {
if (index === 0) return 'widget-rank widget-rank--1';
if (index === 1) return 'widget-rank widget-rank--2';
if (index === 2) return 'widget-rank widget-rank--3';
return 'widget-rank';
}
function HotSkeleton() {
return (
{Array.from({ length: 6 }, (_, i) => (
))}
);
}
function CommentSkeleton() {
return (
{Array.from({ length: 5 }, (_, i) => (
))}
);
}
export default function RightPanel({
hot,
recentComments,
tags = [],
tagsLoading = false,
onPostClick,
loading = false,
postDetail = null,
}: Props) {
const { branding } = useSiteBranding();
const loc = useLocation();
const [params] = useSearchParams();
const activeTag = params.get('tag') || '';
const hotList = hot?.slice(0, 8) ?? [];
const commentList = recentComments?.slice(0, 6) ?? [];
// 站点首页:右侧品牌块承担唯一 h1;板块/搜索等页面由 Feed 标题作 h1
const isSiteHome = loc.pathname === '/'
&& !params.get('board')
&& !params.get('keyword')
&& !params.get('tag');
const description = branding.description?.trim() || '';
const slogan = branding.slogan?.trim() || '';
// 有独立简介时展示简介;否则用欢迎语,避免与页脚 slogan 三连重复
const aboutText = description || '欢迎参与讨论,发帖、评论,一起把小圈子聊热。';
// 帖子很少时热门几乎等于主列表,改显示欢迎引导
const showHot = loading || hotList.length >= 4;
const showWelcome = !loading && hotList.length > 0 && hotList.length < 4;
const isPostDetail = !!postDetail;
return (
{isPostDetail && (
<>
{postDetail.outlineTitle || '文章目录'}
>
)}
{!isPostDetail && showWelcome && (
加入讨论
社区还在起步,每条回复都很珍贵。
- 逛逛板块,找到感兴趣的话题
- 游客也能评论,登录可点赞收藏
- 发一篇帖,留下你的痕迹
)}
{!isPostDetail && showHot && (
热门帖子
{loading && hotList.length === 0 ? (
) : hotList.length === 0 ? (
暂无数据
) : hotList.map((item, i) => (
))}
)}
{!isPostDetail && (
)}
{!isPostDetail && (
最新评论
{loading && commentList.length === 0 ? (
) : commentList.length === 0 ? (
暂无评论
) : commentList.map(item => (
{item.user_id ? (
{item.avatar
?
: (item.author?.[0] || '?')}
) : (
{item.avatar
?
: (item.author?.[0] || '?')}
)}
))}
)}
{!isPostDetail && (
{isSiteHome ? (
{branding.name}
) : (
{branding.name}
)}
{aboutText}
{description && slogan && slogan !== description && (
{slogan}
)}
)}
);
}