feat: 添加评论、反应、离线编辑及主题定制功能
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good

- 实现评论系统,包括评论输入、列表展示和集成指南
- 添加反应功能组件(ReactionBar、ReactionButton、ReactionPicker)
- 实现离线编辑支持,包括同步状态管理和冲突解决
- 添加主题定制功能,支持多种配色方案和主题预览
- 新增多视图布局选项(时间线、分组、砌体视图)
- 实现个人资料编辑器,支持头像、简介和自定义字段编辑
- 添加统计页面,展示存储使用情况和上传趋势
- 新增相册管理功能,支持相册创建、编辑和照片管理
- 实现响应式设计和加载骨架屏组件
- 扩展国际化支持,新增孟加拉语、波斯语、印尼语、日语、葡萄牙语等语言
- 添加错误边界组件和离线指示器
- 更新配置文件、路由和依赖项
- 新增完整的文档、测试用例和集成指南
This commit is contained in:
2026-02-25 15:02:05 +08:00
parent 97a5ad3a00
commit 5a0aa2b3c1
210 changed files with 23556 additions and 300 deletions

View File

@@ -0,0 +1,129 @@
/**
* CommentList Component
* Feature: personal-user-enhancements
*
* Displays a list of comments with virtualization for long lists
*/
import React, { useMemo } from 'react';
import { Empty, Divider, Spin } from 'antd';
import { FixedSizeList as List } from 'react-window';
import CommentItem from './CommentItem';
export interface CommentListProps {
/** Array of comments to display */
comments: API.Comment[];
/** Loading state */
loading?: boolean;
/** Callback when comment is edited */
onEdit?: (id: string, text: string) => void | Promise<void>;
/** Callback when comment is deleted */
onDelete?: (id: string) => void | Promise<void>;
/** Loading state for edit operation */
editLoading?: boolean;
/** Loading state for delete operation */
deleteLoading?: boolean;
/** Enable virtualization (default: true for >20 comments) */
enableVirtualization?: boolean;
/** Height of the list container (for virtualization) */
height?: number;
/** Item height estimate (for virtualization) */
itemHeight?: number;
}
const VIRTUALIZATION_THRESHOLD = 20;
const DEFAULT_ITEM_HEIGHT = 100;
const DEFAULT_LIST_HEIGHT = 600;
const CommentList: React.FC<CommentListProps> = ({
comments,
loading = false,
onEdit,
onDelete,
editLoading = false,
deleteLoading = false,
enableVirtualization,
height = DEFAULT_LIST_HEIGHT,
itemHeight = DEFAULT_ITEM_HEIGHT,
}) => {
// Sort comments chronologically (oldest first)
const sortedComments = useMemo(() => {
return [...comments].sort((a, b) => {
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
});
}, [comments]);
// Determine if virtualization should be used
const shouldVirtualize = useMemo(() => {
if (enableVirtualization !== undefined) {
return enableVirtualization;
}
return sortedComments.length > VIRTUALIZATION_THRESHOLD;
}, [enableVirtualization, sortedComments.length]);
// Show loading state
if (loading) {
return (
<div style={{ textAlign: 'center', padding: '40px 0' }}>
<Spin size="large" />
</div>
);
}
// Show empty state
if (sortedComments.length === 0) {
return (
<Empty
description="No comments yet"
style={{ padding: '40px 0' }}
/>
);
}
// Render a single comment item
const renderCommentItem = (comment: API.Comment, index: number) => (
<div key={comment.id}>
<CommentItem
comment={comment}
onEdit={onEdit}
onDelete={onDelete}
editLoading={editLoading}
deleteLoading={deleteLoading}
/>
{index < sortedComments.length - 1 && <Divider style={{ margin: '8px 0' }} />}
</div>
);
// Render with virtualization for long lists
if (shouldVirtualize) {
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => {
const comment = sortedComments[index];
return (
<div style={style}>
{renderCommentItem(comment, index)}
</div>
);
};
return (
<List
height={height}
itemCount={sortedComments.length}
itemSize={itemHeight}
width="100%"
overscanCount={5}
>
{Row}
</List>
);
}
// Render without virtualization for short lists
return (
<div>
{sortedComments.map((comment, index) => renderCommentItem(comment, index))}
</div>
);
};
export default CommentList;