【m】聊天相关逻辑迁移
This commit is contained in:
251
WebApp/client/public/onebyone/chatmessage.js
Normal file
251
WebApp/client/public/onebyone/chatmessage.js
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
* 消息模块
|
||||||
|
* 处理聊天消息的发送、接收和显示
|
||||||
|
*/
|
||||||
|
import { showNotification, generateId } from './utils.js';
|
||||||
|
import store from './store.js';
|
||||||
|
import { mockMessages } from './models.js';
|
||||||
|
// 消息相关的状态管理方法
|
||||||
|
let messageState = {
|
||||||
|
messages: [...mockMessages],
|
||||||
|
unreadCount: 0,
|
||||||
|
isSidebarOpen: false
|
||||||
|
};
|
||||||
|
|
||||||
|
let listeners = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅状态变化
|
||||||
|
* @param {Function} callback - 回调函数
|
||||||
|
* @returns {Function} 取消订阅的函数
|
||||||
|
*/
|
||||||
|
export function subscribe(callback) {
|
||||||
|
listeners.push(callback);
|
||||||
|
return () => {
|
||||||
|
listeners = listeners.filter(cb => cb !== callback);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知所有监听器
|
||||||
|
* @param {Object} changes - 变化对象
|
||||||
|
*/
|
||||||
|
function notify(changes) {
|
||||||
|
listeners.forEach(cb => cb(messageState, changes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加消息
|
||||||
|
* @param {Object} message - 消息对象
|
||||||
|
*/
|
||||||
|
export function addMessage (message) {
|
||||||
|
messageState.messages.push(message);
|
||||||
|
|
||||||
|
// 如果侧边栏关闭且不是自己发的,增加未读
|
||||||
|
if (!messageState.isSidebarOpen && !message.isSelf) {
|
||||||
|
messageState.unreadCount++;
|
||||||
|
notify({ type: 'SIDEBAR_TOGGLE', unreadCount: messageState.unreadCount });
|
||||||
|
}
|
||||||
|
|
||||||
|
notify({ type: 'NEW_MESSAGE', message, unreadCount: messageState.unreadCount });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送聊天消息
|
||||||
|
* @param {Object} message - 消息对象
|
||||||
|
* @param {Object} renderstreaming - WebRTC连接管理实例
|
||||||
|
*/
|
||||||
|
export function sendChatMessage(message) {
|
||||||
|
if (store.getRenderStreaming()) {
|
||||||
|
store.getRenderStreaming().sendMessage({
|
||||||
|
type: 'chat-message',
|
||||||
|
message: message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理接收到的聊天消息
|
||||||
|
* @param {Object} data - 消息数据
|
||||||
|
*/
|
||||||
|
export function handleChatMessage(data) {
|
||||||
|
console.log('处理聊天:', data);
|
||||||
|
addMessage(data);
|
||||||
|
|
||||||
|
const isImage = data.content && data.content.startsWith('data:image/');
|
||||||
|
const messageType = isImage ? 'file' : 'text';
|
||||||
|
|
||||||
|
// 显示通知
|
||||||
|
if (!message.isSelf) {
|
||||||
|
const content = isImage ? '[图片]' : message.content;
|
||||||
|
showNotification(`${message.senderName}: ${content.substring(0, 20)}${content.length > 20 ? '...' : ''}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换侧边栏
|
||||||
|
* @returns {boolean} 切换后的状态
|
||||||
|
*/
|
||||||
|
export function toggleSidebar() {
|
||||||
|
messageState.isSidebarOpen = !messageState.isSidebarOpen;
|
||||||
|
if (messageState.isSidebarOpen) {
|
||||||
|
messageState.unreadCount = 0;
|
||||||
|
}
|
||||||
|
notify({ type: 'SIDEBAR_TOGGLE', isOpen: messageState.isSidebarOpen, unreadCount: messageState.unreadCount });
|
||||||
|
return messageState.isSidebarOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取消息状态
|
||||||
|
* @returns {Object} 消息状态
|
||||||
|
*/
|
||||||
|
export function getMessageState() {
|
||||||
|
return messageState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
*/
|
||||||
|
export function sendMessage() {
|
||||||
|
const chatInput = document.getElementById('chatInput');
|
||||||
|
const content = chatInput.value.trim();
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
const state = store.getState();
|
||||||
|
const message = {
|
||||||
|
id: generateId(),
|
||||||
|
senderId: state.session.localUser.id,
|
||||||
|
senderName: state.session.localUser.name,
|
||||||
|
senderAvatar: state.session.localUser.avatar,
|
||||||
|
content: content,
|
||||||
|
type: 'text',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
isSelf: true
|
||||||
|
};
|
||||||
|
|
||||||
|
addMessage(message);
|
||||||
|
|
||||||
|
const newMessage = {
|
||||||
|
id: generateId(),
|
||||||
|
senderId: state.session.remoteUser.id,
|
||||||
|
senderName: state.session.remoteUser.name,
|
||||||
|
senderAvatar: state.session.remoteUser.avatar,
|
||||||
|
content: content,
|
||||||
|
type: 'text',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
isSelf: false
|
||||||
|
};
|
||||||
|
chatInput.value = '';
|
||||||
|
// 发送消息到服务器
|
||||||
|
sendChatMessage(newMessage);
|
||||||
|
|
||||||
|
//wsManager.send('chat-message', message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理聊天输入回车
|
||||||
|
* @param {KeyboardEvent} event - 键盘事件
|
||||||
|
*/
|
||||||
|
export function handleChatSubmit(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
sendMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开图片选择器
|
||||||
|
*/
|
||||||
|
export function openImagePicker() {
|
||||||
|
document.getElementById('imageInput').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理图片上传
|
||||||
|
* @param {Event} event - 事件对象
|
||||||
|
*/
|
||||||
|
export function handleImageUpload(event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
// 检查文件类型
|
||||||
|
if (!file.type.startsWith('image/')) {
|
||||||
|
showNotification('请选择图片文件', 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件大小(限制为5MB)
|
||||||
|
if (file.size > 5 * 1024 * 1024) {
|
||||||
|
showNotification('图片文件不能超过5MB', 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取图片文件
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function (e) {
|
||||||
|
const imageUrl = e.target.result;
|
||||||
|
sendImageMessage(imageUrl, file.name);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
|
||||||
|
// 重置文件输入
|
||||||
|
event.target.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送图片消息
|
||||||
|
* @param {string} imageUrl - 图片URL
|
||||||
|
* @param {string} fileName - 文件名
|
||||||
|
*/
|
||||||
|
export function sendImageMessage(imageUrl, fileName) {
|
||||||
|
const state = store.getState();
|
||||||
|
const newMessage = {
|
||||||
|
id: generateId(),
|
||||||
|
senderId: state.session.localUser.id,
|
||||||
|
senderName: state.session.localUser.name,
|
||||||
|
senderAvatar: state.session.localUser.avatar,
|
||||||
|
content: imageUrl,
|
||||||
|
fileName: fileName,
|
||||||
|
type: 'file',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
isSelf: true
|
||||||
|
};
|
||||||
|
|
||||||
|
(newMessage);
|
||||||
|
|
||||||
|
// 发送消息到服务器
|
||||||
|
// wsManager.send('send-message', newMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定消息相关的DOM事件
|
||||||
|
*/
|
||||||
|
export function bindMessageEvents() {
|
||||||
|
// 发送消息
|
||||||
|
window.sendMessage = sendMessage;
|
||||||
|
|
||||||
|
// 处理聊天输入回车
|
||||||
|
window.handleChatSubmit = handleChatSubmit;
|
||||||
|
|
||||||
|
// 打开图片选择器
|
||||||
|
window.openImagePicker = openImagePicker;
|
||||||
|
|
||||||
|
// 处理图片上传
|
||||||
|
window.handleImageUpload = handleImageUpload;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出所有函数
|
||||||
|
export default {
|
||||||
|
sendMessage,
|
||||||
|
handleChatSubmit,
|
||||||
|
openImagePicker,
|
||||||
|
handleImageUpload,
|
||||||
|
sendImageMessage,
|
||||||
|
bindMessageEvents,
|
||||||
|
addMessage,
|
||||||
|
sendChatMessage,
|
||||||
|
handleChatMessage,
|
||||||
|
toggleSidebar,
|
||||||
|
getMessageState,
|
||||||
|
subscribe
|
||||||
|
};
|
||||||
@@ -5,118 +5,18 @@
|
|||||||
import store from './store.js';
|
import store from './store.js';
|
||||||
import UIRenderer from './renderer.js';
|
import UIRenderer from './renderer.js';
|
||||||
import apiClient from './api.js';
|
import apiClient from './api.js';
|
||||||
// import wsManager from './websocket.js';
|
import { showNotification } from './utils.js';
|
||||||
import { mockCallSession } from './models.js';
|
import chatMessage from './chatmessage.js';
|
||||||
import { showNotification, generateId } from './utils.js';
|
|
||||||
|
|
||||||
// 全局变量
|
// 全局变量
|
||||||
let renderer = null;
|
|
||||||
let connectionId = "";
|
let connectionId = "";
|
||||||
/**
|
|
||||||
* 初始化应用
|
|
||||||
*/
|
|
||||||
// function initApp() {
|
|
||||||
// // 初始化渲染器
|
|
||||||
// renderer = new UIRenderer(store);
|
|
||||||
|
|
||||||
// // 初始化WebSocket连接
|
|
||||||
// wsManager.connect();
|
|
||||||
|
|
||||||
// // 绑定WebSocket事件
|
|
||||||
// bindWebSocketEvents();
|
|
||||||
|
|
||||||
// // 绑定DOM事件
|
|
||||||
// bindDomEvents();
|
|
||||||
|
|
||||||
// // 初始化WebRTC (如果需要)
|
|
||||||
// // initWebRTC();
|
|
||||||
|
|
||||||
|
|
||||||
// console.log('App initialized');
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 绑定WebSocket事件
|
|
||||||
*/
|
|
||||||
function bindWebSocketEvents() {
|
|
||||||
// wsManager.on('connect', () => {
|
|
||||||
// console.log('WebSocket connected');
|
|
||||||
// showNotification('已连接到服务器');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('disconnect', () => {
|
|
||||||
// console.log('WebSocket disconnected');
|
|
||||||
// showNotification('与服务器的连接已断开', 5000);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('message-received', (data) => {
|
|
||||||
// console.log('Message received:', data);
|
|
||||||
// store.addMessage(data.message);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('user-joined', (data) => {
|
|
||||||
// console.log('User joined:', data);
|
|
||||||
// showNotification(`${data.userId} 加入了通话`);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('user-left', (data) => {
|
|
||||||
// console.log('User left:', data);
|
|
||||||
// showNotification(`${data.userId} 离开了通话`);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('media-state-changed', (data) => {
|
|
||||||
// console.log('Media state changed:', data);
|
|
||||||
// // 更新远端媒体状态
|
|
||||||
// if (data.userId !== store.getLocalUser().id) {
|
|
||||||
// store.updateRemoteMedia(data);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('network-quality', (data) => {
|
|
||||||
// console.log('Network quality changed:', data);
|
|
||||||
// // 更新网络质量
|
|
||||||
// const state = store.getState();
|
|
||||||
// if (data.userId === state.session.remoteUser.id) {
|
|
||||||
// state.session.remoteUser.networkQuality = data.quality;
|
|
||||||
// store.notify({ type: 'NETWORK_CHANGE', quality: data.quality });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('call-ended', (data) => {
|
|
||||||
// console.log('Call ended:', data);
|
|
||||||
// store.endCall();
|
|
||||||
// showNotification('通话已结束', 3000);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// wsManager.on('call-request', (data) => {
|
|
||||||
// console.log('Call request received:', data);
|
|
||||||
// // 显示通话请求弹窗
|
|
||||||
// if (window.showCallRequest) {
|
|
||||||
// const caller = {
|
|
||||||
// name: mockCallSession.remoteUser.name,
|
|
||||||
// avatar: mockCallSession.remoteUser.avatar
|
|
||||||
// };
|
|
||||||
// window.showCallRequest(caller);
|
|
||||||
// connectionId = data.connectionId;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// //处理发送消息响应
|
|
||||||
// wsManager.on('chat-message', (data) => {
|
|
||||||
// console.log('chat-message:', data);
|
|
||||||
// // 显示消息
|
|
||||||
// store.addMessage({
|
|
||||||
// data: data.message,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定DOM事件
|
* 绑定DOM事件
|
||||||
*/
|
*/
|
||||||
function bindDomEvents() {
|
function bindDomEvents() {
|
||||||
// 切换侧边栏
|
// 切换侧边栏
|
||||||
window.toggleSidebar = function () {
|
window.toggleSidebar = function () {
|
||||||
store.toggleSidebar();
|
chatMessage.toggleSidebar();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 切换麦克风
|
// 切换麦克风
|
||||||
@@ -209,107 +109,8 @@ function bindDomEvents() {
|
|||||||
store.setUp(connectionId);
|
store.setUp(connectionId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送消息
|
// 绑定消息相关事件
|
||||||
window.sendMessage = function () {
|
chatMessage.bindMessageEvents();
|
||||||
const chatInput = document.getElementById('chatInput');
|
|
||||||
const content = chatInput.value.trim();
|
|
||||||
|
|
||||||
if (content) {
|
|
||||||
const state = store.getState();
|
|
||||||
const message = {
|
|
||||||
id: state.id,
|
|
||||||
senderId: state.session.localUser.id,
|
|
||||||
senderName: state.session.localUser.name,
|
|
||||||
senderAvatar: state.session.localUser.avatar,
|
|
||||||
content: content,
|
|
||||||
type: 'text',
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
isSelf: true
|
|
||||||
};
|
|
||||||
|
|
||||||
store.addMessage(message);
|
|
||||||
|
|
||||||
|
|
||||||
const newMessage = {
|
|
||||||
id: state.id,
|
|
||||||
senderId: state.session.remoteUser.id,
|
|
||||||
senderName: state.session.remoteUser.name,
|
|
||||||
senderAvatar: state.session.remoteUser.avatar,
|
|
||||||
content: content,
|
|
||||||
type: 'text',
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
isSelf: false
|
|
||||||
|
|
||||||
};
|
|
||||||
chatInput.value = '';
|
|
||||||
// 发送消息到服务器
|
|
||||||
store.sendChatMessage(newMessage);
|
|
||||||
|
|
||||||
//wsManager.send('chat-message', message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理聊天输入回车
|
|
||||||
window.handleChatSubmit = function (event) {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
window.sendMessage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 打开图片选择器
|
|
||||||
window.openImagePicker = function () {
|
|
||||||
document.getElementById('imageInput').click();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理图片上传
|
|
||||||
window.handleImageUpload = function (event) {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (file) {
|
|
||||||
// 检查文件类型
|
|
||||||
if (!file.type.startsWith('image/')) {
|
|
||||||
showNotification('请选择图片文件', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查文件大小(限制为5MB)
|
|
||||||
if (file.size > 5 * 1024 * 1024) {
|
|
||||||
showNotification('图片文件不能超过5MB', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取图片文件
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = function (e) {
|
|
||||||
const imageUrl = e.target.result;
|
|
||||||
sendImageMessage(imageUrl, file.name);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
|
|
||||||
// 重置文件输入
|
|
||||||
event.target.value = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发送图片消息
|
|
||||||
function sendImageMessage(imageUrl, fileName) {
|
|
||||||
const state = store.getState();
|
|
||||||
const newMessage = {
|
|
||||||
id: generateId(),
|
|
||||||
senderId: state.session.localUser.id,
|
|
||||||
senderName: state.session.localUser.name,
|
|
||||||
senderAvatar: state.session.localUser.avatar,
|
|
||||||
content: imageUrl,
|
|
||||||
fileName: fileName,
|
|
||||||
type: 'file',
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
isSelf: true
|
|
||||||
};
|
|
||||||
|
|
||||||
store.addMessage(newMessage);
|
|
||||||
|
|
||||||
// 发送消息到服务器
|
|
||||||
// wsManager.send('send-message', newMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 键盘快捷键
|
// 键盘快捷键
|
||||||
document.addEventListener('keydown', (event) => {
|
document.addEventListener('keydown', (event) => {
|
||||||
@@ -367,9 +168,6 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
// 绑定DOM事件
|
// 绑定DOM事件
|
||||||
bindDomEvents();
|
bindDomEvents();
|
||||||
|
|
||||||
// 绑定WebSocket事件
|
|
||||||
bindWebSocketEvents();
|
|
||||||
|
|
||||||
console.log('Video call app initialized successfully');
|
console.log('Video call app initialized successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing app:', error);
|
console.error('Error initializing app:', error);
|
||||||
@@ -378,4 +176,4 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 导出全局变量
|
// 导出全局变量
|
||||||
export { store, renderer, apiClient };
|
export { store, apiClient };
|
||||||
|
|||||||
@@ -106,26 +106,6 @@ const mockMessages = [
|
|||||||
type: "system",
|
type: "system",
|
||||||
timestamp: "2024-01-15T14:30:00.000Z",
|
timestamp: "2024-01-15T14:30:00.000Z",
|
||||||
isSelf: false
|
isSelf: false
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "msg-002",
|
|
||||||
senderId: "user-remote-002",
|
|
||||||
senderName: mockCallSession.remoteUser.name,
|
|
||||||
senderAvatar: mockCallSession.remoteUser.avatar,
|
|
||||||
content: "嗨,能听到我说话吗?",
|
|
||||||
type: "text",
|
|
||||||
timestamp: "2024-01-15T14:32:15.000Z",
|
|
||||||
isSelf: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "msg-003",
|
|
||||||
senderId: "user-local-001",
|
|
||||||
senderName: mockCallSession.localUser.name,
|
|
||||||
senderAvatar: mockCallSession.localUser.avatar,
|
|
||||||
content: "很清楚!你的画面也很清晰 👍",
|
|
||||||
type: "text",
|
|
||||||
timestamp: "2024-01-15T14:32:45.000Z",
|
|
||||||
isSelf: true
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { formatTime, formatTimestamp, toggleElement, toggleButtonState } from './utils.js';
|
import { formatTime, formatTimestamp, toggleElement, toggleButtonState } from './utils.js';
|
||||||
import { mockCallSession } from './models.js';
|
import { mockCallSession } from './models.js';
|
||||||
|
import chatMessage from './chatmessage.js';
|
||||||
class UIRenderer {
|
class UIRenderer {
|
||||||
constructor(stateManager) {
|
constructor(stateManager) {
|
||||||
this.stateManager = stateManager;
|
this.stateManager = stateManager;
|
||||||
@@ -56,6 +57,8 @@ class UIRenderer {
|
|||||||
|
|
||||||
// 订阅状态变化
|
// 订阅状态变化
|
||||||
this.unsubscribe = stateManager.subscribe(this.render.bind(this));
|
this.unsubscribe = stateManager.subscribe(this.render.bind(this));
|
||||||
|
// 订阅消息状态变化
|
||||||
|
this.messageUnsubscribe = chatMessage.subscribe(this.renderMessageState.bind(this));
|
||||||
// 初始化渲染
|
// 初始化渲染
|
||||||
this.render(this.stateManager.getState(), { type: 'INIT' });
|
this.render(this.stateManager.getState(), { type: 'INIT' });
|
||||||
|
|
||||||
@@ -71,6 +74,25 @@ class UIRenderer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 渲染消息状态变化
|
||||||
|
renderMessageState(messageState, changes) {
|
||||||
|
switch (changes.type) {
|
||||||
|
case 'NEW_MESSAGE':
|
||||||
|
this.renderChatMessages(messageState.messages);
|
||||||
|
this.renderUnreadCount(changes.unreadCount);
|
||||||
|
break;
|
||||||
|
case 'SIDEBAR_TOGGLE':
|
||||||
|
this.renderSidebar(changes.isOpen);
|
||||||
|
// 当侧边栏打开时,重置未读消息计数
|
||||||
|
if (changes.isOpen) {
|
||||||
|
this.renderUnreadCount(0);
|
||||||
|
} else {
|
||||||
|
this.renderUnreadCount(changes.unreadCount);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 绑定事件监听器
|
// 绑定事件监听器
|
||||||
bindEventListeners() {
|
bindEventListeners() {
|
||||||
// 事件监听器
|
// 事件监听器
|
||||||
@@ -85,7 +107,7 @@ class UIRenderer {
|
|||||||
this.renderRemoteVideo(state.session.remoteUser);
|
this.renderRemoteVideo(state.session.remoteUser);
|
||||||
this.renderLocalVideo(state.session.localUser, state.localStream);
|
this.renderLocalVideo(state.session.localUser, state.localStream);
|
||||||
this.renderControlButtons(state.session.localUser.mediaState);
|
this.renderControlButtons(state.session.localUser.mediaState);
|
||||||
this.renderChatMessages(state.messages);
|
this.renderChatMessages(chatMessage.getMessageState().messages);
|
||||||
this.renderUserList(state.session.localUser, state.session.remoteUser);
|
this.renderUserList(state.session.localUser, state.session.remoteUser);
|
||||||
|
|
||||||
// 初始化时检查远程流状态,显示或隐藏占位背景
|
// 初始化时检查远程流状态,显示或隐藏占位背景
|
||||||
@@ -121,19 +143,6 @@ class UIRenderer {
|
|||||||
this.renderRemoteVideo(state.session.remoteUser);
|
this.renderRemoteVideo(state.session.remoteUser);
|
||||||
this.renderUserList(state.session.localUser, state.session.remoteUser);
|
this.renderUserList(state.session.localUser, state.session.remoteUser);
|
||||||
break;
|
break;
|
||||||
case 'NEW_MESSAGE':
|
|
||||||
this.renderChatMessages(state.messages);
|
|
||||||
this.renderUnreadCount(changes.unreadCount);
|
|
||||||
break;
|
|
||||||
case 'SIDEBAR_TOGGLE':
|
|
||||||
this.renderSidebar(changes.isOpen);
|
|
||||||
// 当侧边栏打开时,重置未读消息计数
|
|
||||||
if (changes.isOpen) {
|
|
||||||
this.renderUnreadCount(0);
|
|
||||||
} else{
|
|
||||||
this.renderUnreadCount(changes.unreadCount);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'NETWORK_CHANGE':
|
case 'NETWORK_CHANGE':
|
||||||
this.renderNetworkStatus(changes.quality);
|
this.renderNetworkStatus(changes.quality);
|
||||||
break;
|
break;
|
||||||
@@ -645,6 +654,9 @@ class UIRenderer {
|
|||||||
if (this.unsubscribe) {
|
if (this.unsubscribe) {
|
||||||
this.unsubscribe();
|
this.unsubscribe();
|
||||||
}
|
}
|
||||||
|
if (this.messageUnsubscribe) {
|
||||||
|
this.messageUnsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,16 @@
|
|||||||
* 状态管理
|
* 状态管理
|
||||||
* 使用简单的 Observable 模式,可替换为 Redux/Vuex/Pinia
|
* 使用简单的 Observable 模式,可替换为 Redux/Vuex/Pinia
|
||||||
*/
|
*/
|
||||||
import { mockCallSession, mockMessages } from './models.js';
|
import { mockCallSession } from './models.js';
|
||||||
import { Signaling, WebSocketSignaling } from "../../module/signaling.js";// 信令管理
|
import { Signaling, WebSocketSignaling } from "../../module/signaling.js";// 信令管理
|
||||||
import { RenderStreaming } from "../../module/renderstreaming.js"; // WebRTC连接管理
|
import { RenderStreaming } from "../../module/renderstreaming.js"; // WebRTC连接管理
|
||||||
import { getServerConfig, getRTCConfiguration } from "../js/config.js";//服务器配置和RTC配置
|
import { getServerConfig, getRTCConfiguration } from "../js/config.js";//服务器配置和RTC配置
|
||||||
import { showNotification, generateId } from './utils.js'; // 导入通知函数
|
import { showNotification, generateId } from './utils.js'; // 导入通知函数
|
||||||
|
import chatMessage from './chatmessage.js';
|
||||||
// 默认视频流尺寸
|
// 默认视频流尺寸
|
||||||
const defaultStreamWidth = 1280;
|
const defaultStreamWidth = 1280;
|
||||||
const defaultStreamHeight = 720;
|
const defaultStreamHeight = 720;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CallStateManager {
|
class CallStateManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
const renderstreaming = null; // WebRTC连接管理实例
|
const renderstreaming = null; // WebRTC连接管理实例
|
||||||
@@ -26,9 +24,6 @@ class CallStateManager {
|
|||||||
...mockCallSession,
|
...mockCallSession,
|
||||||
status: 'idle' // 初始状态为空闲
|
status: 'idle' // 初始状态为空闲
|
||||||
},
|
},
|
||||||
messages: [...mockMessages],
|
|
||||||
isSidebarOpen: false,
|
|
||||||
unreadCount: 0,
|
|
||||||
localStream: null, // MediaStream 对象
|
localStream: null, // MediaStream 对象
|
||||||
remoteStream: null // MediaStream 对象
|
remoteStream: null // MediaStream 对象
|
||||||
};
|
};
|
||||||
@@ -372,7 +367,7 @@ class CallStateManager {
|
|||||||
// 处理聊天
|
// 处理聊天
|
||||||
// 添加到列表并更新UI
|
// 添加到列表并更新UI
|
||||||
|
|
||||||
this.handleChatMessage(data.message);
|
chatMessage.handleChatMessage(data.message);
|
||||||
} else if (data.type === 'on-message') {
|
} else if (data.type === 'on-message') {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,34 +452,19 @@ class CallStateManager {
|
|||||||
this.notify({ type: 'REMOTE_MEDIA_CHANGE', mediaState });
|
this.notify({ type: 'REMOTE_MEDIA_CHANGE', mediaState });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加
|
// 添加消息
|
||||||
addMessage(message) {
|
addMessage(message) {
|
||||||
this.state.messages.push(message);
|
chatMessage.addMessage(message);
|
||||||
|
|
||||||
// 如果侧边栏关闭且不是自己发的,增加未读
|
|
||||||
if (!this.state.isSidebarOpen && !message.isSelf) {
|
|
||||||
this.state.unreadCount++;
|
|
||||||
this.notify({ type: 'SIDEBAR_TOGGLE', unreadCount: this.state.unreadCount });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notify({ type: 'NEW_MESSAGE', message, unreadCount: this.state.unreadCount });
|
// 发送聊天消息
|
||||||
}
|
|
||||||
sendChatMessage(message) {
|
sendChatMessage(message) {
|
||||||
if (this.renderstreaming) {
|
chatMessage.sendChatMessage(message, this.renderstreaming);
|
||||||
|
}
|
||||||
|
|
||||||
this.renderstreaming.sendMessage({
|
|
||||||
type: 'chat-message',
|
|
||||||
message: message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 切换侧边栏
|
// 切换侧边栏
|
||||||
toggleSidebar() {
|
toggleSidebar() {
|
||||||
this.state.isSidebarOpen = !this.state.isSidebarOpen;
|
chatMessage.toggleSidebar();
|
||||||
if (this.state.isSidebarOpen) {
|
|
||||||
this.state.unreadCount = 0;
|
|
||||||
}
|
|
||||||
this.notify({ type: 'SIDEBAR_TOGGLE', isOpen: this.state.isSidebarOpen });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 结束通话
|
// 结束通话
|
||||||
@@ -812,18 +792,8 @@ class CallStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理接收到的聊天
|
|
||||||
handleChatMessage(data) {
|
|
||||||
console.log('处理聊天:', data);
|
|
||||||
store.addMessage(data);
|
|
||||||
const isImage = data.message && data.message.startsWith('data:image/');
|
|
||||||
const messageType = isImage ? 'file' : 'text';
|
|
||||||
|
|
||||||
|
|
||||||
// 显示通知
|
|
||||||
showNotification(`${senderName}: ${data.message.substring(0, 20)}${data.message.length > 20 ? '...' : ''}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送媒体状态到服务器
|
// 发送媒体状态到服务器
|
||||||
emitMediaStateChange() {
|
emitMediaStateChange() {
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -939,8 +909,10 @@ class CallStateManager {
|
|||||||
getState() { return this.state; }
|
getState() { return this.state; }
|
||||||
getLocalUser() { return this.state.session.localUser; }
|
getLocalUser() { return this.state.session.localUser; }
|
||||||
getRemoteUser() { return this.state.session.remoteUser; }
|
getRemoteUser() { return this.state.session.remoteUser; }
|
||||||
getMessages() { return this.state.messages; }
|
|
||||||
getConnectionId() { return this.connectionId; }
|
getConnectionId() { return this.connectionId; }
|
||||||
|
|
||||||
|
getRenderStreaming() { return this.renderstreaming; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建单例实例
|
// 创建单例实例
|
||||||
|
|||||||
Reference in New Issue
Block a user