feat(content): add rich content rendering with @mentions and vote segments
Some checks failed
Frontend CI / build-and-push-web (push) Failing after 8m30s
Frontend CI / ota-android (push) Successful in 10m43s
Frontend CI / build-android-apk (push) Successful in 1h24m38s

- Add PostContentRenderer component for rendering posts/comments with segments
- Add PostMentionInput component for @mention support in post/comment creation
- Add VoteSegmentData and vote segment type for unified post creation API
- Update post and comment services to support segments in API requests
- Fix auth service to clear tokens before login/register
- Improve database initialization with proper close/retry logic
- Add verification check before allowing post creation
- Add metro web shims for react-native-webrtc full package and requireNativeComponent
- Update build config with git hash based version numbers
This commit is contained in:
lafay
2026-04-23 22:29:58 +08:00
parent eb4e1c080d
commit b2c3d5e54e
23 changed files with 797 additions and 127 deletions

View File

@@ -4,7 +4,7 @@
*/
import { api, PaginatedData } from '../core/api';
import { Comment, CreateCommentInput } from '@/types';
import { Comment, CreateCommentInput, MessageSegment } from '@/types';
import { CursorPaginationRequest, CursorPaginationResponse, CommentDTO } from '@/types/dto';
// 评论列表响应
@@ -23,8 +23,9 @@ type CommentResponse = Comment;
interface CreateCommentRequest {
postId: string;
content: string;
segments?: MessageSegment[];
parentId?: string;
images?: string[]; // 图片URL列表
images?: string[];
replyToUserId?: string;
}
@@ -130,12 +131,14 @@ class CommentService {
content: data.content,
};
// 如果有父评论ID才添加 parent_id 字段
if (data.segments && data.segments.length > 0) {
requestBody.segments = data.segments;
}
if (data.parentId) {
requestBody.parent_id = data.parentId;
}
// 如果有图片,添加 images 字段
if (data.images && data.images.length > 0) {
requestBody.images = data.images;
}

View File

@@ -5,7 +5,7 @@
import { api, PaginatedData } from '../core/api';
import { Post, CreatePostInput } from '@/types';
import { CursorPaginationRequest, CursorPaginationResponse } from '@/types/dto';
import { CursorPaginationRequest, CursorPaginationResponse, MessageSegment } from '@/types/dto';
// 帖子列表响应
interface PostListResponse {
@@ -22,7 +22,8 @@ type PostResponse = Post;
// 创建帖子请求
interface CreatePostRequest {
title: string;
content: string;
content?: string;
segments?: MessageSegment[];
images?: string[];
channel_id?: string;
}
@@ -31,6 +32,7 @@ interface CreatePostRequest {
interface UpdatePostRequest {
title?: string;
content?: string;
segments?: MessageSegment[];
images?: string[];
}

View File

@@ -4,7 +4,7 @@
*/
import { api } from '../core/api';
import { Post, VoteResultDTO, CreateVotePostRequest } from '@/types';
import { Post, VoteResultDTO, CreateVotePostRequest, MessageSegment } from '@/types';
// 投票响应
interface VoteResponse {
@@ -23,12 +23,9 @@ interface UpdateVoteOptionResponse {
*/
class VoteService {
/**
* 创建投票帖子
* @param data 创建投票帖子请求数据
* @returns 创建的帖子或null
* 创建投票帖子(通过统一的 POST /posts 接口,投票选项作为 vote segment
*/
async createVotePost(data: CreateVotePostRequest): Promise<Post | null> {
// 验证投票选项数量
if (!data.vote_options || data.vote_options.length < 2) {
console.error('[VoteService] 投票选项至少需要2个');
return null;
@@ -38,7 +35,30 @@ class VoteService {
return null;
}
const response = await api.post<Post>('/posts/vote', data);
// 构建 segments先放文本内容再放投票 segment
const segments: MessageSegment[] = [];
if (data.content) {
segments.push({ type: 'text', data: { text: data.content } });
}
segments.push({
type: 'vote',
data: {
options: data.vote_options.map((opt, i) => ({
id: `opt_${i + 1}`,
content: typeof opt === 'string' ? opt : opt.content,
})),
max_choices: 1,
is_multi_choice: false,
},
});
const response = await api.post<Post>('/posts', {
title: data.title,
content: data.content || '',
segments,
images: data.images,
channel_id: data.channel_id,
});
return response.data;
}