/** * 状态管理 * 使用简单的 Observable 模式,可替换为 Redux/Vuex/Pinia */ import { mockCallSession, mockMessages } from './models.js'; class CallStateManager { constructor() { // 核心状态 this.state = { session: { ...mockCallSession }, messages: [...mockMessages], isSidebarOpen: false, unreadCount: 0, localStream: null, // MediaStream 对象 remoteStream: null // MediaStream 对象 }; // 监听器数组 this.listeners = []; // 初始化 this.init(); } // 订阅状态变化 subscribe(callback) { this.listeners.push(callback); return () => { this.listeners = this.listeners.filter(cb => cb !== callback); }; } // 通知所有监听器 notify(changes) { this.listeners.forEach(cb => cb(this.state, changes)); } // 初始化 init() { // 启动通话时长计时器 this.durationInterval = setInterval(() => { this.state.session.duration++; this.notify({ type: 'DURATION_UPDATE', duration: this.state.session.duration }); }, 1000); // 模拟远端音频活动 (实际应由 WebRTC VAD 检测触发) this.simulateRemoteActivity(); // 模拟网络质量变化 this.simulateNetworkChange(); } // 更新本地媒体状态 updateLocalMedia(mediaType, value) { this.state.session.localUser.mediaState[mediaType] = value; this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value }); // 发送媒体状态到服务器 this.emitMediaStateChange(); } // 更新远端媒体状态 (由 WebSocket 消息触发) updateRemoteMedia(mediaState) { this.state.session.remoteUser.mediaState = { ...this.state.session.remoteUser.mediaState, ...mediaState }; this.notify({ type: 'REMOTE_MEDIA_CHANGE', mediaState }); } // 添加消息 addMessage(message) { this.state.messages.push(message); // 如果侧边栏关闭且不是自己发的,增加未读 if (!this.state.isSidebarOpen && !message.isSelf) { this.state.unreadCount++; } this.notify({ type: 'NEW_MESSAGE', message, unreadCount: this.state.unreadCount }); } // 切换侧边栏 toggleSidebar() { this.state.isSidebarOpen = !this.state.isSidebarOpen; if (this.state.isSidebarOpen) { this.state.unreadCount = 0; } this.notify({ type: 'SIDEBAR_TOGGLE', isOpen: this.state.isSidebarOpen }); } // 结束通话 endCall() { clearInterval(this.durationInterval); this.state.session.status = 'ended'; this.notify({ type: 'CALL_ENDED' }); // 发送结束通话请求到服务器 // [API_CALL: POST /api/call/:callId/leave] // [WEBSOCKET_EMIT: leave-call] } // 模拟远端活动 (开发测试用) simulateRemoteActivity() { setInterval(() => { if (Math.random() > 0.7) { const isSpeaking = Math.random() > 0.5; this.updateRemoteMedia({ isSpeaking }); } }, 800); } // 模拟网络质量变化 (开发测试用) simulateNetworkChange() { const qualities = ['excellent', 'good', 'fair', 'poor']; setInterval(() => { if (Math.random() > 0.8) { const quality = qualities[Math.floor(Math.random() * qualities.length)]; this.state.session.remoteUser.networkQuality = quality; this.notify({ type: 'NETWORK_CHANGE', quality }); } }, 5000); } // 发送媒体状态到服务器 emitMediaStateChange() { const payload = { userId: this.state.session.localUser.id, ...this.state.session.localUser.mediaState }; console.log('[WebSocket Emit] media-state-changed:', payload); // socket.emit('media-state-changed', payload); } // Getters getState() { return this.state; } getLocalUser() { return this.state.session.localUser; } getRemoteUser() { return this.state.session.remoteUser; } getMessages() { return this.state.messages; } } // 创建单例实例 const store = new CallStateManager(); export default store;