refactor(message): replace SubscriptionManager with Zustand selectors
All checks were successful
Frontend CI / ota-android (push) Successful in 11m17s
Frontend CI / build-and-push-web (push) Successful in 25m8s
Frontend CI / build-android-apk (push) Successful in 59m7s

Remove custom SubscriptionManager class and adopt Zustand's built-in
selector mechanism for reactive state updates. This simplifies the
architecture by eliminating manual subscription management and relying
on Zustand's automatic dependency tracking for component re-renders.

Key changes:
- Remove SubscriptionManager class from store.ts
- Replace all notifySubscribers calls with direct store updates
- Update hooks to use Zustand selectors with useShallow for complex data
- Remove subscribe/unsubscribe patterns from MessageManager
- Simplify EmbeddedChat to use selector instead of local state
- Rename requestConversationListRefresh to refreshConversations
This commit is contained in:
lafay
2026-03-31 19:15:13 +08:00
parent 94c11062f0
commit 259de04f3e
14 changed files with 198 additions and 993 deletions

View File

@@ -8,6 +8,7 @@
* - 移除了 MessageStateManager 包装层
* - 直接使用 zustand store
* - 服务层移至 services/ 目录
* - 移除了 SubscriptionManager改用 Zustand selector 进行响应式订阅
*/
import type { ConversationResponse, MessageResponse, MessageSegment, UserDTO } from '../../types/dto';
@@ -24,7 +25,7 @@ import { MessageSyncService } from './services/MessageSyncService';
import { WSMessageHandler } from './services/WSMessageHandler';
// 导入 store
import { useMessageStore, subscriptionManager, normalizeConversationId } from './store';
import { useMessageStore, normalizeConversationId } from './store';
// 重新导出类型,保持兼容性
export type {
@@ -164,23 +165,12 @@ class MessageManager {
this.wsHandler.setBootstrapping(false);
subscriptionManager.notifySubscribers({
type: 'conversations_updated',
payload: { conversations: this.getConversations() },
timestamp: Date.now(),
});
if (useMessageStore.getState().currentConversationId) {
await this.fetchMessages(useMessageStore.getState().currentConversationId!);
}
} catch (error) {
console.error('[MessageManager] 初始化失败:', error);
this.wsHandler.setBootstrapping(false);
subscriptionManager.notifySubscribers({
type: 'error',
payload: { error, context: 'initialize' },
timestamp: Date.now(),
});
}
})();
@@ -194,7 +184,6 @@ class MessageManager {
destroy(): void {
this.wsHandler.disconnect();
useMessageStore.getState().reset();
subscriptionManager.clear();
this.syncService.restartSources();
this.readReceiptManager.reset();
this.deduplication.reset();
@@ -339,18 +328,6 @@ class MessageManager {
await this.initialize();
this.setActiveConversation(normalizedId);
// 先下发当前内存快照
const snapshot = this.getMessages(normalizedId);
subscriptionManager.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;
@@ -371,81 +348,14 @@ class MessageManager {
}
}
// ==================== 订阅机制 ====================
subscribe(subscriber: import('./types').MessageSubscriber): () => void {
const unsubscribe = subscriptionManager.subscribe(subscriber);
// 立即发送当前状态给新订阅者(与原始版本保持一致)
subscriber({
type: 'conversations_updated',
payload: { conversations: this.getConversations() },
timestamp: Date.now(),
});
const unreadCount = this.getUnreadCount();
subscriber({
type: 'unread_count_updated',
payload: {
totalUnreadCount: unreadCount.total,
systemUnreadCount: unreadCount.system,
},
timestamp: Date.now(),
});
subscriber({
type: 'connection_changed',
payload: { connected: this.isConnected() },
timestamp: Date.now(),
});
// 如果当前有活动会话,立即发送该会话的消息给新订阅者
const activeConversationId = this.getActiveConversation();
if (activeConversationId) {
const currentMessages = this.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 subscriptionManager.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 中处理
async refreshConversations(forceRefresh: boolean, source: string): Promise<void> {
if (this.shouldDeferConversationRefresh(source, forceRefresh)) {
return;
}