refactor(message): modularize MessageManager architecture
重构消息管理模块,将单体 MessageManager 拆分为职责单一的子模块: - MessageStateManager: 纯状态管理 - MessageDeduplication: 消息去重服务 - UserCacheService: 用户缓存服务 - ReadReceiptManager: 已读回执管理 - ConversationOperations: 会话操作 - MessageSendService: 消息发送 - MessageSyncService: 消息同步 - WSMessageHandler: WebSocket 消息处理 新增类型定义和常量文件,保持原有 API 向后兼容。修复 CallScreen 状态值错误,补充 PostDetailScreen 缺失样式,优化 GroupInfoPanel 导入。
This commit is contained in:
@@ -1,221 +1,326 @@
|
||||
/**
|
||||
* MessageManager - 重构版
|
||||
* MessageManager - 消息管理核心模块(重构版)
|
||||
*
|
||||
* 作为协调者,整合各个专注的服务模块
|
||||
* 保持与原始版本完全兼容的 API
|
||||
*/
|
||||
|
||||
import { messageStateManager, MessageStateManager } from './MessageStateManager';
|
||||
import { wsMessageHandler, WSMessageHandler } from './WSMessageHandler';
|
||||
import { messageSyncService, MessageSyncService } from './MessageSyncService';
|
||||
import { readReceiptManager, ReadReceiptManager } from './ReadReceiptManager';
|
||||
import { messageRepository } from '../../data/repositories/MessageRepository';
|
||||
import type { ConversationResponse, MessageResponse, MessageSegment, UserDTO } from '../../types/dto';
|
||||
import { messageService } from '../../services/messageService';
|
||||
import {
|
||||
wsService,
|
||||
WSChatMessage,
|
||||
WSGroupChatMessage,
|
||||
WSReadMessage,
|
||||
WSGroupReadMessage,
|
||||
WSRecallMessage,
|
||||
WSGroupRecallMessage,
|
||||
WSGroupTypingMessage,
|
||||
WSGroupNoticeMessage,
|
||||
GroupNoticeType,
|
||||
} from '../../services/wsService';
|
||||
import {
|
||||
saveMessage,
|
||||
saveMessagesBatch,
|
||||
getMessagesByConversation,
|
||||
getMaxSeq,
|
||||
getMinSeq,
|
||||
getMessagesBeforeSeq,
|
||||
markConversationAsRead,
|
||||
updateConversationCacheUnreadCount,
|
||||
CachedMessage,
|
||||
getUserCache,
|
||||
saveUserCache,
|
||||
updateMessageStatus,
|
||||
deleteConversation as deleteConversationFromDb,
|
||||
saveConversationsWithRelatedCache,
|
||||
} from '../../services/database';
|
||||
import { api } from '../../services/api';
|
||||
import { vibrateOnMessage } from '../../services/messageVibrationService';
|
||||
import { useAuthStore } from '../authStore';
|
||||
import type { Message, Conversation } from '../../core/entities/Message';
|
||||
import type { ConversationResponse, MessageResponse } from '../../types/dto';
|
||||
import {
|
||||
type IConversationListPagedSource,
|
||||
SqliteConversationListPagedSource,
|
||||
createRemoteConversationListSource,
|
||||
CONVERSATION_LIST_PAGE_SIZE,
|
||||
} from '../conversationListSources';
|
||||
|
||||
// 导入新模块
|
||||
import { MessageStateManager } from './MessageStateManager';
|
||||
import { MessageDeduplication } from './MessageDeduplication';
|
||||
import { UserCacheService } from './UserCacheService';
|
||||
import { ReadReceiptManager } from './ReadReceiptManager';
|
||||
import { ConversationOperations } from './ConversationOperations';
|
||||
import { MessageSendService } from './MessageSendService';
|
||||
import { MessageSyncService } from './MessageSyncService';
|
||||
import { WSMessageHandler } from './WSMessageHandler';
|
||||
|
||||
// 重新导出类型,保持兼容性
|
||||
export type {
|
||||
MessageEventType,
|
||||
MessageEvent,
|
||||
MessageSubscriber,
|
||||
} from './MessageStateManager';
|
||||
MessageManagerState,
|
||||
ReadStateRecord,
|
||||
MessageManagerConversationListDeps,
|
||||
} from './types';
|
||||
|
||||
export class MessageManager {
|
||||
// ==================== MessageManager 类 ====================
|
||||
|
||||
class MessageManager {
|
||||
// 子模块
|
||||
private stateManager: MessageStateManager;
|
||||
private wsHandler: WSMessageHandler;
|
||||
private deduplication: MessageDeduplication;
|
||||
private userCacheService: UserCacheService;
|
||||
private readReceiptManager: ReadReceiptManager;
|
||||
private conversationOps: ConversationOperations;
|
||||
private sendService: MessageSendService;
|
||||
private syncService: MessageSyncService;
|
||||
private readManager: ReadReceiptManager;
|
||||
private wsHandler: WSMessageHandler;
|
||||
|
||||
private initialized: boolean = false;
|
||||
private authUnsubscribe: (() => void) | null = null;
|
||||
private wsUnsubscribe: (() => void) | null = null;
|
||||
// 本地状态
|
||||
private currentUserId: string | null = null;
|
||||
private authUnsubscribe: (() => void) | null = null;
|
||||
private initializePromise: Promise<void> | null = null;
|
||||
private activatingConversationTasks: Map<string, Promise<void>> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.stateManager = messageStateManager;
|
||||
this.wsHandler = wsMessageHandler;
|
||||
this.syncService = messageSyncService;
|
||||
this.readManager = readReceiptManager;
|
||||
constructor(deps?: import('./types').MessageManagerConversationListDeps) {
|
||||
// 初始化子模块
|
||||
this.stateManager = new MessageStateManager();
|
||||
this.deduplication = new MessageDeduplication();
|
||||
this.userCacheService = new UserCacheService();
|
||||
this.readReceiptManager = new ReadReceiptManager(this.stateManager);
|
||||
this.conversationOps = new ConversationOperations(this.stateManager);
|
||||
this.sendService = new MessageSendService(this.stateManager, () => this.getCurrentUserId());
|
||||
this.syncService = new MessageSyncService(
|
||||
this.stateManager,
|
||||
() => this.getCurrentUserId(),
|
||||
deps
|
||||
);
|
||||
this.wsHandler = new WSMessageHandler(
|
||||
this.stateManager,
|
||||
this.deduplication,
|
||||
this.userCacheService,
|
||||
() => this.getCurrentUserId(),
|
||||
{
|
||||
markAsRead: (id, seq) => this.markAsRead(id, seq),
|
||||
fetchConversations: (force, source) => this.fetchConversations(force, source),
|
||||
fetchUnreadCount: () => this.fetchUnreadCount(),
|
||||
fetchMessages: (id) => this.fetchMessages(id),
|
||||
}
|
||||
);
|
||||
|
||||
this.readManager.setStateManager(this.stateManager);
|
||||
this.setupWSHandlers();
|
||||
// 监听认证状态变化
|
||||
this.initAuthListener();
|
||||
}
|
||||
|
||||
private initAuthListener(): void {
|
||||
// ==================== 私有工具方法 ====================
|
||||
|
||||
private initAuthListener() {
|
||||
const authState = useAuthStore.getState();
|
||||
this.currentUserId = authState.currentUser?.id || null;
|
||||
|
||||
if (this.currentUserId && !this.initialized) {
|
||||
if (this.currentUserId && !this.stateManager.getState().isInitialized) {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
this.authUnsubscribe = useAuthStore.subscribe((state) => {
|
||||
const nextUserId = state.currentUser?.id || null;
|
||||
this.currentUserId = nextUserId;
|
||||
if (!this.authUnsubscribe) {
|
||||
this.authUnsubscribe = useAuthStore.subscribe((state) => {
|
||||
const nextUserId = state.currentUser?.id || null;
|
||||
const prevUserId = this.currentUserId;
|
||||
this.currentUserId = nextUserId;
|
||||
|
||||
if (nextUserId && !this.initialized) {
|
||||
this.initialize();
|
||||
}
|
||||
});
|
||||
if (nextUserId && !this.stateManager.getState().isInitialized) {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
if (nextUserId && this.stateManager.getActiveConversation()) {
|
||||
this.activateConversation(this.stateManager.getActiveConversation()!, { forceSync: true }).catch(error => {
|
||||
console.error('[MessageManager] 登录态就绪后重激活会话失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
if (!nextUserId && prevUserId && this.stateManager.getState().isInitialized) {
|
||||
this.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private setupWSHandlers(): void {
|
||||
this.wsUnsubscribe = this.wsHandler.subscribe((event) => {
|
||||
switch (event.type) {
|
||||
case 'chat_message':
|
||||
case 'group_message':
|
||||
this.handleNewMessage(event.payload);
|
||||
break;
|
||||
case 'read_receipt':
|
||||
case 'group_read_receipt':
|
||||
this.handleReadReceipt(event.payload);
|
||||
break;
|
||||
case 'message_recalled':
|
||||
case 'group_message_recalled':
|
||||
this.handleMessageRecall(event.payload);
|
||||
break;
|
||||
case 'typing':
|
||||
this.handleTypingStatus(event.payload);
|
||||
break;
|
||||
case 'group_notice':
|
||||
this.handleGroupNotice(event.payload);
|
||||
break;
|
||||
}
|
||||
});
|
||||
private getCurrentUserId(): string | null {
|
||||
if (!this.currentUserId) {
|
||||
this.currentUserId = useAuthStore.getState().currentUser?.id || null;
|
||||
}
|
||||
return this.currentUserId;
|
||||
}
|
||||
|
||||
private normalizeConversationId(conversationId: string | number | null | undefined): string {
|
||||
return conversationId == null ? '' : String(conversationId);
|
||||
}
|
||||
|
||||
// ==================== 初始化与销毁 ====================
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
if (this.initialized) return;
|
||||
if (this.stateManager.getState().isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.initializePromise) {
|
||||
await this.initializePromise;
|
||||
return;
|
||||
}
|
||||
|
||||
this.initializePromise = (async () => {
|
||||
try {
|
||||
this.currentUserId = this.getCurrentUserId();
|
||||
if (!this.currentUserId) {
|
||||
console.warn('[MessageManager] 用户未登录,延迟初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
this.wsHandler.setBootstrapping(true);
|
||||
|
||||
// 初始化SSE监听
|
||||
this.wsHandler.connect();
|
||||
|
||||
// 加载会话列表
|
||||
await this.fetchConversations(false, 'initialize');
|
||||
|
||||
// 加载未读数
|
||||
await this.fetchUnreadCount();
|
||||
|
||||
this.stateManager.setInitialized(true);
|
||||
|
||||
// 处理缓冲的 SSE 事件
|
||||
await this.wsHandler.flushBufferedSSEEvents();
|
||||
|
||||
this.wsHandler.setBootstrapping(false);
|
||||
|
||||
this.stateManager.notifySubscribers({
|
||||
type: 'conversations_updated',
|
||||
payload: { conversations: this.stateManager.getConversations() },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
if (this.stateManager.getActiveConversation()) {
|
||||
await this.fetchMessages(this.stateManager.getActiveConversation()!);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[MessageManager] 初始化失败:', error);
|
||||
this.wsHandler.setBootstrapping(false);
|
||||
this.stateManager.notifySubscribers({
|
||||
type: 'error',
|
||||
payload: { error, context: 'initialize' },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
try {
|
||||
console.log('[MessageManager] Initializing...');
|
||||
|
||||
this.wsHandler.connect();
|
||||
|
||||
const conversations = await this.syncService.syncConversations();
|
||||
this.stateManager.setConversations(conversations);
|
||||
|
||||
this.initialized = true;
|
||||
console.log('[MessageManager] Initialized successfully');
|
||||
} catch (error) {
|
||||
console.error('[MessageManager] Initialization failed:', error);
|
||||
throw error;
|
||||
await this.initializePromise;
|
||||
} finally {
|
||||
this.initializePromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
async activateConversation(conversationId: string): Promise<void> {
|
||||
this.stateManager.setCurrentConversation(conversationId);
|
||||
|
||||
const existing = this.stateManager.getMessages(conversationId);
|
||||
if (existing.length === 0) {
|
||||
await this.loadMessages(conversationId);
|
||||
}
|
||||
|
||||
const conversation = this.stateManager.getConversation(conversationId);
|
||||
if (conversation && conversation.unreadCount > 0) {
|
||||
await this.readManager.markAsRead(conversationId, conversation.lastSeq);
|
||||
}
|
||||
destroy(): void {
|
||||
this.wsHandler.disconnect();
|
||||
this.stateManager.reset();
|
||||
this.syncService.restartSources();
|
||||
this.readReceiptManager.reset();
|
||||
this.deduplication.reset();
|
||||
this.userCacheService.reset();
|
||||
this.activatingConversationTasks.clear();
|
||||
this.initializePromise = null;
|
||||
}
|
||||
|
||||
deactivateConversation(): void {
|
||||
this.stateManager.setCurrentConversation(null);
|
||||
// ==================== 数据获取方法(代理到 syncService)====================
|
||||
|
||||
async fetchConversations(forceRefresh = false, source: string = 'unknown'): Promise<void> {
|
||||
return this.syncService.fetchConversations(forceRefresh, source);
|
||||
}
|
||||
|
||||
async loadMessages(conversationId: string): Promise<Message[]> {
|
||||
const localMessages = await this.syncService.loadLocalMessages(conversationId);
|
||||
|
||||
if (localMessages.length > 0) {
|
||||
this.stateManager.setMessages(conversationId, localMessages);
|
||||
}
|
||||
|
||||
const syncResult = await this.syncService.syncMessages(conversationId);
|
||||
|
||||
if (syncResult.messages.length > 0) {
|
||||
this.stateManager.appendMessages(conversationId, syncResult.messages);
|
||||
return [...localMessages, ...syncResult.messages];
|
||||
}
|
||||
|
||||
return localMessages;
|
||||
async loadMoreConversations(): Promise<void> {
|
||||
return this.syncService.loadMoreConversations();
|
||||
}
|
||||
|
||||
async loadHistory(conversationId: string): Promise<Message[]> {
|
||||
const messages = this.stateManager.getMessages(conversationId);
|
||||
const oldestSeq = messages.length > 0 ? Math.min(...messages.map(m => m.seq)) : 0;
|
||||
|
||||
const history = await this.syncService.loadHistoryMessages(
|
||||
conversationId,
|
||||
oldestSeq,
|
||||
20
|
||||
);
|
||||
|
||||
if (history.length > 0) {
|
||||
this.stateManager.prependMessages(conversationId, history);
|
||||
}
|
||||
|
||||
return history;
|
||||
async fetchConversationDetail(conversationId: string): Promise<ConversationResponse | null> {
|
||||
return this.syncService.fetchConversationDetail(conversationId);
|
||||
}
|
||||
|
||||
async fetchMessages(conversationId: string, afterSeq?: number): Promise<void> {
|
||||
return this.syncService.fetchMessages(conversationId, afterSeq);
|
||||
}
|
||||
|
||||
async loadMoreMessages(conversationId: string, beforeSeq: number, limit = 20): Promise<MessageResponse[]> {
|
||||
return this.syncService.loadMoreMessages(conversationId, beforeSeq, limit);
|
||||
}
|
||||
|
||||
async fetchUnreadCount(): Promise<void> {
|
||||
return this.syncService.fetchUnreadCount();
|
||||
}
|
||||
|
||||
// ==================== 数据操作方法(代理到各服务)====================
|
||||
|
||||
async sendMessage(
|
||||
conversationId: string,
|
||||
content: string,
|
||||
type: 'private' | 'group' = 'private'
|
||||
): Promise<Message> {
|
||||
throw new Error('sendMessage not implemented - use existing implementation');
|
||||
segments: MessageSegment[],
|
||||
options?: { replyToId?: string }
|
||||
): Promise<MessageResponse | null> {
|
||||
return this.sendService.sendMessage(conversationId, segments, options);
|
||||
}
|
||||
|
||||
private async handleNewMessage(message: Message): Promise<void> {
|
||||
await messageRepository.saveMessage(message, true);
|
||||
|
||||
this.stateManager.addMessage(message.conversationId, message);
|
||||
|
||||
const currentConvId = this.stateManager.getCurrentConversationId();
|
||||
if (currentConvId !== message.conversationId) {
|
||||
const conv = this.stateManager.getConversation(message.conversationId);
|
||||
if (conv) {
|
||||
this.stateManager.updateConversation(message.conversationId, {
|
||||
unreadCount: (conv.unreadCount || 0) + 1,
|
||||
lastSeq: message.seq,
|
||||
lastMessageAt: message.createdAt,
|
||||
});
|
||||
}
|
||||
}
|
||||
async markAsRead(conversationId: string, seq: number): Promise<void> {
|
||||
return this.readReceiptManager.markAsRead(conversationId, seq);
|
||||
}
|
||||
|
||||
private handleReadReceipt(payload: any): void {
|
||||
const { conversationId, lastReadSeq } = payload;
|
||||
|
||||
this.stateManager.updateConversation(conversationId, {
|
||||
unreadCount: 0,
|
||||
});
|
||||
async markAllAsRead(): Promise<void> {
|
||||
return this.readReceiptManager.markAllAsRead();
|
||||
}
|
||||
|
||||
private async handleMessageRecall(payload: any): Promise<void> {
|
||||
const { messageId, conversationId } = payload;
|
||||
|
||||
await messageRepository.updateMessageStatus(messageId, 'recalled', true);
|
||||
|
||||
this.stateManager.updateMessage(conversationId, messageId, {
|
||||
status: 'recalled',
|
||||
});
|
||||
async createConversation(userId: string): Promise<ConversationResponse | null> {
|
||||
return this.conversationOps.createConversation(userId);
|
||||
}
|
||||
|
||||
private handleTypingStatus(payload: any): void {
|
||||
const { groupId, userIds } = payload;
|
||||
this.stateManager.setTypingUsers(groupId, userIds);
|
||||
updateConversation(conversationId: string, updates: Partial<ConversationResponse>): void {
|
||||
return this.conversationOps.updateConversation(conversationId, updates);
|
||||
}
|
||||
|
||||
private handleGroupNotice(notice: any): void {
|
||||
console.log('[MessageManager] Group notice:', notice);
|
||||
removeConversation(conversationId: string): void {
|
||||
return this.conversationOps.removeConversation(conversationId);
|
||||
}
|
||||
|
||||
subscribe(callback: any): () => void {
|
||||
return this.stateManager.subscribe(callback);
|
||||
clearConversations(): void {
|
||||
return this.conversationOps.clearConversations();
|
||||
}
|
||||
|
||||
getConversations(): Conversation[] {
|
||||
updateUnreadCount(conversationId: string, count: number): void {
|
||||
return this.conversationOps.updateUnreadCount(conversationId, count);
|
||||
}
|
||||
|
||||
setSystemUnreadCount(count: number): void {
|
||||
return this.conversationOps.setSystemUnreadCount(count);
|
||||
}
|
||||
|
||||
incrementSystemUnreadCount(): void {
|
||||
return this.conversationOps.incrementSystemUnreadCount();
|
||||
}
|
||||
|
||||
decrementSystemUnreadCount(count?: number): void {
|
||||
return this.conversationOps.decrementSystemUnreadCount(count);
|
||||
}
|
||||
|
||||
// ==================== 状态查询方法(代理到 stateManager)====================
|
||||
|
||||
getConversations(): ConversationResponse[] {
|
||||
return this.stateManager.getConversations();
|
||||
}
|
||||
|
||||
getMessages(conversationId: string): Message[] {
|
||||
getConversation(conversationId: string): ConversationResponse | null {
|
||||
return this.stateManager.getConversation(conversationId);
|
||||
}
|
||||
|
||||
getMessages(conversationId: string): MessageResponse[] {
|
||||
return this.stateManager.getMessages(conversationId);
|
||||
}
|
||||
|
||||
@@ -224,28 +329,172 @@ export class MessageManager {
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return this.wsHandler.isConnected();
|
||||
return this.stateManager.isConnected();
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.initialized = false;
|
||||
this.stateManager.reset();
|
||||
this.readManager.reset();
|
||||
this.wsHandler.disconnect();
|
||||
isLoading(): boolean {
|
||||
return this.stateManager.isLoading();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this.authUnsubscribe) {
|
||||
this.authUnsubscribe();
|
||||
this.authUnsubscribe = null;
|
||||
isLoadingMessages(conversationId: string): boolean {
|
||||
return this.stateManager.isLoadingMessages(conversationId);
|
||||
}
|
||||
|
||||
getTypingUsers(groupId: string): string[] {
|
||||
return this.stateManager.getTypingUsers(groupId);
|
||||
}
|
||||
|
||||
isMuted(groupId: string): boolean {
|
||||
return this.stateManager.isMuted(groupId);
|
||||
}
|
||||
|
||||
setMutedStatus(groupId: string, isMuted: boolean): void {
|
||||
return this.stateManager.setMutedStatus(groupId, isMuted);
|
||||
}
|
||||
|
||||
// ==================== 活动会话管理 ====================
|
||||
|
||||
setActiveConversation(conversationId: string | null): void {
|
||||
const normalizedId = conversationId ? this.normalizeConversationId(conversationId) : null;
|
||||
this.stateManager.setCurrentConversation(normalizedId);
|
||||
}
|
||||
|
||||
getActiveConversation(): string | null {
|
||||
return this.stateManager.getActiveConversation();
|
||||
}
|
||||
|
||||
async activateConversation(conversationId: string, options?: { forceSync?: boolean }): Promise<void> {
|
||||
const normalizedId = this.normalizeConversationId(conversationId);
|
||||
if (!normalizedId) return;
|
||||
|
||||
await this.initialize();
|
||||
this.setActiveConversation(normalizedId);
|
||||
|
||||
// 先下发当前内存快照
|
||||
const snapshot = this.getMessages(normalizedId);
|
||||
this.stateManager.notifySubscribers({
|
||||
type: 'messages_updated',
|
||||
payload: {
|
||||
conversationId: normalizedId,
|
||||
messages: [...snapshot],
|
||||
source: 'activation_snapshot',
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const existingTask = this.activatingConversationTasks.get(normalizedId);
|
||||
if (existingTask && !options?.forceSync) {
|
||||
await existingTask;
|
||||
return;
|
||||
}
|
||||
if (this.wsUnsubscribe) {
|
||||
this.wsUnsubscribe();
|
||||
this.wsUnsubscribe = null;
|
||||
|
||||
const task = (async () => {
|
||||
await this.fetchMessages(normalizedId);
|
||||
})();
|
||||
this.activatingConversationTasks.set(normalizedId, task);
|
||||
|
||||
try {
|
||||
await task;
|
||||
} finally {
|
||||
if (this.activatingConversationTasks.get(normalizedId) === task) {
|
||||
this.activatingConversationTasks.delete(normalizedId);
|
||||
}
|
||||
}
|
||||
this.reset();
|
||||
}
|
||||
|
||||
// ==================== 订阅机制 ====================
|
||||
|
||||
subscribe(subscriber: import('./types').MessageSubscriber): () => void {
|
||||
const unsubscribe = this.stateManager.subscribe(subscriber);
|
||||
|
||||
// 立即发送当前状态给新订阅者(与原始版本保持一致)
|
||||
subscriber({
|
||||
type: 'conversations_updated',
|
||||
payload: { conversations: this.stateManager.getConversations() },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
const unreadCount = this.stateManager.getUnreadCount();
|
||||
subscriber({
|
||||
type: 'unread_count_updated',
|
||||
payload: {
|
||||
totalUnreadCount: unreadCount.total,
|
||||
systemUnreadCount: unreadCount.system,
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
subscriber({
|
||||
type: 'connection_changed',
|
||||
payload: { connected: this.stateManager.isConnected() },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// 如果当前有活动会话,立即发送该会话的消息给新订阅者
|
||||
const activeConversationId = this.stateManager.getActiveConversation();
|
||||
if (activeConversationId) {
|
||||
const currentMessages = this.stateManager.getMessages(activeConversationId);
|
||||
if (currentMessages.length > 0) {
|
||||
subscriber({
|
||||
type: 'messages_updated',
|
||||
payload: {
|
||||
conversationId: activeConversationId,
|
||||
messages: [...currentMessages],
|
||||
source: 'initial_sync',
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return unsubscribe;
|
||||
}
|
||||
|
||||
subscribeToMessages(conversationId: string, callback: (messages: MessageResponse[]) => void): () => void {
|
||||
return this.stateManager.subscribe((event) => {
|
||||
if (event.type === 'messages_updated' && event.payload.conversationId === conversationId) {
|
||||
callback(event.payload.messages);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 其他方法 ====================
|
||||
|
||||
canLoadMoreConversations(): boolean {
|
||||
return this.syncService.canLoadMoreConversations();
|
||||
}
|
||||
|
||||
invalidateCache(_type?: 'all' | 'list' | 'unread'): void {
|
||||
// 兼容性方法,新架构不需要外部缓存
|
||||
}
|
||||
|
||||
async requestConversationListRefresh(
|
||||
source: string,
|
||||
options?: { force?: boolean; allowDefer?: boolean }
|
||||
): Promise<void> {
|
||||
const forceRefresh = options?.force ?? true;
|
||||
const allowDefer = options?.allowDefer ?? true;
|
||||
|
||||
if (allowDefer && this.shouldDeferConversationRefresh(source, forceRefresh)) {
|
||||
// 延迟刷新逻辑在 syncService 中处理
|
||||
return;
|
||||
}
|
||||
|
||||
await this.fetchConversations(forceRefresh, source);
|
||||
}
|
||||
|
||||
private shouldDeferConversationRefresh(source: string, forceRefresh: boolean): boolean {
|
||||
if (!this.stateManager.getActiveConversation()) return false;
|
||||
if (!forceRefresh) return false;
|
||||
return source === 'sse-reconnect' || source === 'prefetch' || source === 'hooks-initial-refresh';
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 单例导出 ====================
|
||||
|
||||
export const messageManager = new MessageManager();
|
||||
export default messageManager;
|
||||
|
||||
// 导出类以支持类型检查和测试
|
||||
export { MessageManager };
|
||||
|
||||
export default messageManager;
|
||||
|
||||
Reference in New Issue
Block a user