2026-03-18 12:11:49 +08:00
|
|
|
|
/**
|
2026-03-31 16:13:24 +08:00
|
|
|
|
* MessageManager - 消息管理核心模块(重构版)
|
|
|
|
|
|
*
|
2026-03-31 18:22:24 +08:00
|
|
|
|
* 作为轻量级协调者,整合各个专注的服务模块
|
|
|
|
|
|
* 直接使用 zustand store 进行状态管理
|
|
|
|
|
|
*
|
|
|
|
|
|
* 重构说明:
|
|
|
|
|
|
* - 移除了 MessageStateManager 包装层
|
|
|
|
|
|
* - 直接使用 zustand store
|
|
|
|
|
|
* - 服务层移至 services/ 目录
|
2026-03-18 12:11:49 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
import type { ConversationResponse, MessageResponse, MessageSegment, UserDTO } from '../../types/dto';
|
2026-03-19 00:56:41 +08:00
|
|
|
|
import { useAuthStore } from '../authStore';
|
2026-03-31 18:22:24 +08:00
|
|
|
|
import type { MessageManagerConversationListDeps } from './types';
|
|
|
|
|
|
|
|
|
|
|
|
// 导入服务
|
|
|
|
|
|
import { MessageDeduplication, messageDeduplication } from './services/MessageDeduplication';
|
|
|
|
|
|
import { UserCacheService, userCacheService } from './services/UserCacheService';
|
|
|
|
|
|
import { ReadReceiptManager } from './services/ReadReceiptManager';
|
|
|
|
|
|
import { ConversationOperations } from './services/ConversationOperations';
|
|
|
|
|
|
import { MessageSendService } from './services/MessageSendService';
|
|
|
|
|
|
import { MessageSyncService } from './services/MessageSyncService';
|
|
|
|
|
|
import { WSMessageHandler } from './services/WSMessageHandler';
|
|
|
|
|
|
|
|
|
|
|
|
// 导入 store
|
|
|
|
|
|
import { useMessageStore, subscriptionManager, normalizeConversationId } from './store';
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 重新导出类型,保持兼容性
|
2026-03-18 12:11:49 +08:00
|
|
|
|
export type {
|
|
|
|
|
|
MessageEventType,
|
|
|
|
|
|
MessageEvent,
|
|
|
|
|
|
MessageSubscriber,
|
2026-03-31 16:13:24 +08:00
|
|
|
|
MessageManagerState,
|
|
|
|
|
|
ReadStateRecord,
|
|
|
|
|
|
MessageManagerConversationListDeps,
|
|
|
|
|
|
} from './types';
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== MessageManager 类 ====================
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
class MessageManager {
|
|
|
|
|
|
// 子模块
|
|
|
|
|
|
private deduplication: MessageDeduplication;
|
|
|
|
|
|
private userCacheService: UserCacheService;
|
|
|
|
|
|
private readReceiptManager: ReadReceiptManager;
|
|
|
|
|
|
private conversationOps: ConversationOperations;
|
|
|
|
|
|
private sendService: MessageSendService;
|
2026-03-18 12:11:49 +08:00
|
|
|
|
private syncService: MessageSyncService;
|
2026-03-31 16:13:24 +08:00
|
|
|
|
private wsHandler: WSMessageHandler;
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// 本地状态
|
2026-03-18 12:11:49 +08:00
|
|
|
|
private currentUserId: string | null = null;
|
2026-03-31 16:13:24 +08:00
|
|
|
|
private authUnsubscribe: (() => void) | null = null;
|
|
|
|
|
|
private initializePromise: Promise<void> | null = null;
|
|
|
|
|
|
private activatingConversationTasks: Map<string, Promise<void>> = new Map();
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
constructor(deps?: MessageManagerConversationListDeps) {
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// 初始化子模块
|
|
|
|
|
|
this.deduplication = new MessageDeduplication();
|
|
|
|
|
|
this.userCacheService = new UserCacheService();
|
2026-03-31 18:22:24 +08:00
|
|
|
|
this.readReceiptManager = new ReadReceiptManager();
|
|
|
|
|
|
this.conversationOps = new ConversationOperations();
|
|
|
|
|
|
this.sendService = new MessageSendService(() => this.getCurrentUserId());
|
2026-03-31 16:13:24 +08:00
|
|
|
|
this.syncService = new MessageSyncService(
|
|
|
|
|
|
() => this.getCurrentUserId(),
|
2026-03-31 18:22:24 +08:00
|
|
|
|
this.readReceiptManager,
|
|
|
|
|
|
this.userCacheService,
|
2026-03-31 16:13:24 +08:00
|
|
|
|
deps
|
|
|
|
|
|
);
|
|
|
|
|
|
this.wsHandler = new WSMessageHandler(
|
|
|
|
|
|
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),
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// 监听认证状态变化
|
2026-03-18 12:11:49 +08:00
|
|
|
|
this.initAuthListener();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// ==================== 私有工具方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
private initAuthListener() {
|
2026-03-18 12:11:49 +08:00
|
|
|
|
const authState = useAuthStore.getState();
|
|
|
|
|
|
this.currentUserId = authState.currentUser?.id || null;
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const store = useMessageStore.getState();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
if (this.currentUserId && !store.isInitialized) {
|
2026-03-18 12:11:49 +08:00
|
|
|
|
this.initialize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
if (!this.authUnsubscribe) {
|
|
|
|
|
|
this.authUnsubscribe = useAuthStore.subscribe((state) => {
|
|
|
|
|
|
const nextUserId = state.currentUser?.id || null;
|
|
|
|
|
|
const prevUserId = this.currentUserId;
|
|
|
|
|
|
this.currentUserId = nextUserId;
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
if (nextUserId && !useMessageStore.getState().isInitialized) {
|
2026-03-31 16:13:24 +08:00
|
|
|
|
this.initialize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
if (nextUserId && useMessageStore.getState().currentConversationId) {
|
|
|
|
|
|
this.activateConversation(useMessageStore.getState().currentConversationId!, { forceSync: true }).catch(error => {
|
2026-03-31 16:13:24 +08:00
|
|
|
|
console.error('[MessageManager] 登录态就绪后重激活会话失败:', error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
if (!nextUserId && prevUserId && useMessageStore.getState().isInitialized) {
|
2026-03-31 16:13:24 +08:00
|
|
|
|
this.destroy();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
private getCurrentUserId(): string | null {
|
|
|
|
|
|
if (!this.currentUserId) {
|
|
|
|
|
|
this.currentUserId = useAuthStore.getState().currentUser?.id || null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.currentUserId;
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// ==================== 初始化与销毁 ====================
|
|
|
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
|
async initialize(): Promise<void> {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const store = useMessageStore.getState();
|
|
|
|
|
|
|
|
|
|
|
|
if (store.isInitialized) {
|
2026-03-31 16:13:24 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
if (this.initializePromise) {
|
|
|
|
|
|
await this.initializePromise;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
this.initializePromise = (async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.currentUserId = this.getCurrentUserId();
|
|
|
|
|
|
if (!this.currentUserId) {
|
|
|
|
|
|
console.warn('[MessageManager] 用户未登录,延迟初始化');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
this.wsHandler.setBootstrapping(true);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// 初始化SSE监听
|
|
|
|
|
|
this.wsHandler.connect();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// 加载会话列表
|
|
|
|
|
|
await this.fetchConversations(false, 'initialize');
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// 加载未读数
|
|
|
|
|
|
await this.fetchUnreadCount();
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
useMessageStore.getState().setInitialized(true);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理缓冲的 SSE 事件
|
|
|
|
|
|
await this.wsHandler.flushBufferedSSEEvents();
|
|
|
|
|
|
|
|
|
|
|
|
this.wsHandler.setBootstrapping(false);
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
subscriptionManager.notifySubscribers({
|
2026-03-31 16:13:24 +08:00
|
|
|
|
type: 'conversations_updated',
|
2026-03-31 18:22:24 +08:00
|
|
|
|
payload: { conversations: this.getConversations() },
|
2026-03-31 16:13:24 +08:00
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
if (useMessageStore.getState().currentConversationId) {
|
|
|
|
|
|
await this.fetchMessages(useMessageStore.getState().currentConversationId!);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[MessageManager] 初始化失败:', error);
|
|
|
|
|
|
this.wsHandler.setBootstrapping(false);
|
2026-03-31 18:22:24 +08:00
|
|
|
|
subscriptionManager.notifySubscribers({
|
2026-03-31 16:13:24 +08:00
|
|
|
|
type: 'error',
|
|
|
|
|
|
payload: { error, context: 'initialize' },
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
})();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await this.initializePromise;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.initializePromise = null;
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
destroy(): void {
|
|
|
|
|
|
this.wsHandler.disconnect();
|
2026-03-31 18:22:24 +08:00
|
|
|
|
useMessageStore.getState().reset();
|
|
|
|
|
|
subscriptionManager.clear();
|
2026-03-31 16:13:24 +08:00
|
|
|
|
this.syncService.restartSources();
|
|
|
|
|
|
this.readReceiptManager.reset();
|
|
|
|
|
|
this.deduplication.reset();
|
|
|
|
|
|
this.userCacheService.reset();
|
|
|
|
|
|
this.activatingConversationTasks.clear();
|
|
|
|
|
|
this.initializePromise = null;
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// ==================== 数据获取方法(代理到 syncService)====================
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async fetchConversations(forceRefresh = false, source: string = 'unknown'): Promise<void> {
|
|
|
|
|
|
return this.syncService.fetchConversations(forceRefresh, source);
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async loadMoreConversations(): Promise<void> {
|
|
|
|
|
|
return this.syncService.loadMoreConversations();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async fetchConversationDetail(conversationId: string): Promise<ConversationResponse | null> {
|
|
|
|
|
|
return this.syncService.fetchConversationDetail(conversationId);
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async fetchMessages(conversationId: string, afterSeq?: number): Promise<void> {
|
|
|
|
|
|
return this.syncService.fetchMessages(conversationId, afterSeq);
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async loadMoreMessages(conversationId: string, beforeSeq: number, limit = 20): Promise<MessageResponse[]> {
|
|
|
|
|
|
return this.syncService.loadMoreMessages(conversationId, beforeSeq, limit);
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async fetchUnreadCount(): Promise<void> {
|
|
|
|
|
|
return this.syncService.fetchUnreadCount();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// ==================== 数据操作方法(代理到各服务)====================
|
|
|
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
|
async sendMessage(
|
|
|
|
|
|
conversationId: string,
|
2026-03-31 16:13:24 +08:00
|
|
|
|
segments: MessageSegment[],
|
|
|
|
|
|
options?: { replyToId?: string }
|
|
|
|
|
|
): Promise<MessageResponse | null> {
|
|
|
|
|
|
return this.sendService.sendMessage(conversationId, segments, options);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async markAsRead(conversationId: string, seq: number): Promise<void> {
|
|
|
|
|
|
return this.readReceiptManager.markAsRead(conversationId, seq);
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async markAllAsRead(): Promise<void> {
|
|
|
|
|
|
return this.readReceiptManager.markAllAsRead();
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
async createConversation(userId: string): Promise<ConversationResponse | null> {
|
|
|
|
|
|
return this.conversationOps.createConversation(userId);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
updateConversation(conversationId: string, updates: Partial<ConversationResponse>): void {
|
|
|
|
|
|
return this.conversationOps.updateConversation(conversationId, updates);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
removeConversation(conversationId: string): void {
|
|
|
|
|
|
return this.conversationOps.removeConversation(conversationId);
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
clearConversations(): void {
|
|
|
|
|
|
return this.conversationOps.clearConversations();
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
updateUnreadCount(conversationId: string, count: number): void {
|
|
|
|
|
|
return this.conversationOps.updateUnreadCount(conversationId, count);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
setSystemUnreadCount(count: number): void {
|
|
|
|
|
|
return this.conversationOps.setSystemUnreadCount(count);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
incrementSystemUnreadCount(): void {
|
|
|
|
|
|
return this.conversationOps.incrementSystemUnreadCount();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
decrementSystemUnreadCount(count?: number): void {
|
|
|
|
|
|
return this.conversationOps.decrementSystemUnreadCount(count);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
// ==================== 状态查询方法(直接使用 store)====================
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
getConversations(): ConversationResponse[] {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().getConversations();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
getConversation(conversationId: string): ConversationResponse | null {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().getConversation(conversationId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getMessages(conversationId: string): MessageResponse[] {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().getMessages(conversationId);
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getUnreadCount(): { total: number; system: number } {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().getUnreadCount();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isConnected(): boolean {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().isConnected();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
isLoading(): boolean {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().isLoading();
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
isLoadingMessages(conversationId: string): boolean {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().isLoadingMessages(conversationId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getTypingUsers(groupId: string): string[] {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().getTypingUsers(groupId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isMuted(groupId: string): boolean {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().isMuted(groupId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setMutedStatus(groupId: string, isMuted: boolean): void {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().setMutedStatus(groupId, isMuted);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 活动会话管理 ====================
|
|
|
|
|
|
|
|
|
|
|
|
setActiveConversation(conversationId: string | null): void {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const normalizedId = conversationId ? normalizeConversationId(conversationId) : null;
|
|
|
|
|
|
useMessageStore.getState().setCurrentConversation(normalizedId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getActiveConversation(): string | null {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return useMessageStore.getState().currentConversationId;
|
2026-03-31 16:13:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async activateConversation(conversationId: string, options?: { forceSync?: boolean }): Promise<void> {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const normalizedId = normalizeConversationId(conversationId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
if (!normalizedId) return;
|
|
|
|
|
|
|
|
|
|
|
|
await this.initialize();
|
|
|
|
|
|
this.setActiveConversation(normalizedId);
|
|
|
|
|
|
|
|
|
|
|
|
// 先下发当前内存快照
|
|
|
|
|
|
const snapshot = this.getMessages(normalizedId);
|
2026-03-31 18:22:24 +08:00
|
|
|
|
subscriptionManager.notifySubscribers({
|
2026-03-31 16:13:24 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 订阅机制 ====================
|
|
|
|
|
|
|
|
|
|
|
|
subscribe(subscriber: import('./types').MessageSubscriber): () => void {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const unsubscribe = subscriptionManager.subscribe(subscriber);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 立即发送当前状态给新订阅者(与原始版本保持一致)
|
|
|
|
|
|
subscriber({
|
|
|
|
|
|
type: 'conversations_updated',
|
2026-03-31 18:22:24 +08:00
|
|
|
|
payload: { conversations: this.getConversations() },
|
2026-03-31 16:13:24 +08:00
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const unreadCount = this.getUnreadCount();
|
2026-03-31 16:13:24 +08:00
|
|
|
|
subscriber({
|
|
|
|
|
|
type: 'unread_count_updated',
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
totalUnreadCount: unreadCount.total,
|
|
|
|
|
|
systemUnreadCount: unreadCount.system,
|
|
|
|
|
|
},
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
subscriber({
|
|
|
|
|
|
type: 'connection_changed',
|
2026-03-31 18:22:24 +08:00
|
|
|
|
payload: { connected: this.isConnected() },
|
2026-03-31 16:13:24 +08:00
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 如果当前有活动会话,立即发送该会话的消息给新订阅者
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const activeConversationId = this.getActiveConversation();
|
2026-03-31 16:13:24 +08:00
|
|
|
|
if (activeConversationId) {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
const currentMessages = this.getMessages(activeConversationId);
|
2026-03-31 16:13:24 +08:00
|
|
|
|
if (currentMessages.length > 0) {
|
|
|
|
|
|
subscriber({
|
|
|
|
|
|
type: 'messages_updated',
|
|
|
|
|
|
payload: {
|
|
|
|
|
|
conversationId: activeConversationId,
|
|
|
|
|
|
messages: [...currentMessages],
|
|
|
|
|
|
source: 'initial_sync',
|
|
|
|
|
|
},
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
return unsubscribe;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
subscribeToMessages(conversationId: string, callback: (messages: MessageResponse[]) => void): () => void {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
return subscriptionManager.subscribe((event) => {
|
2026-03-31 16:13:24 +08:00
|
|
|
|
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;
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
await this.fetchConversations(forceRefresh, source);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private shouldDeferConversationRefresh(source: string, forceRefresh: boolean): boolean {
|
2026-03-31 18:22:24 +08:00
|
|
|
|
if (!this.getActiveConversation()) return false;
|
2026-03-31 16:13:24 +08:00
|
|
|
|
if (!forceRefresh) return false;
|
|
|
|
|
|
return source === 'sse-reconnect' || source === 'prefetch' || source === 'hooks-initial-refresh';
|
2026-03-18 12:11:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-31 16:13:24 +08:00
|
|
|
|
// ==================== 单例导出 ====================
|
|
|
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
|
export const messageManager = new MessageManager();
|
2026-03-31 16:13:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 导出类以支持类型检查和测试
|
|
|
|
|
|
export { MessageManager };
|
|
|
|
|
|
|
|
|
|
|
|
export default messageManager;
|