feat(weChat): 添加视频请求处理和WebSocket监听功能
- 在weChat store中新增pendingVideoRequests状态和相关操作方法 - 实现WebSocket监听处理视频下载响应 - 重构ChatWindow组件,将视频处理逻辑移至store - 优化消息分组和渲染逻辑
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { Layout, Button, Avatar, Space, Dropdown, Menu, Tooltip } from "antd";
|
||||
import {
|
||||
PhoneOutlined,
|
||||
@@ -18,10 +18,7 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
||||
import styles from "./ChatWindow.module.scss";
|
||||
import {
|
||||
useWebSocketStore,
|
||||
WebSocketMessage,
|
||||
} from "@/store/module/websocket/websocket";
|
||||
import { useWebSocketStore } from "@/store/module/websocket/websocket";
|
||||
import { formatWechatTime } from "@/utils/common";
|
||||
import Person from "./components/Person";
|
||||
import MessageEnter from "./components/MessageEnter";
|
||||
@@ -39,18 +36,13 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
showProfile = true,
|
||||
onToggleProfile,
|
||||
}) => {
|
||||
const [messages, setMessages] = useState<ChatRecord[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pendingVideoRequests, setPendingVideoRequests] = useState<
|
||||
Record<string, string>
|
||||
>({});
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const currentMessages = useWeChatStore(state => state.currentMessages);
|
||||
|
||||
useEffect(() => {
|
||||
// 只有在非视频加载操作时才自动滚动到底部
|
||||
// 检查是否有视频正在加载中
|
||||
const hasLoadingVideo = messages.some(msg => {
|
||||
const hasLoadingVideo = currentMessages.some(msg => {
|
||||
try {
|
||||
const content =
|
||||
typeof msg.content === "string"
|
||||
@@ -65,78 +57,13 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
if (!hasLoadingVideo) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [messages]);
|
||||
}, [currentMessages]);
|
||||
|
||||
// 添加 WebSocket 消息订阅 - 监听视频下载响应消息
|
||||
// 初始化WebSocket监听
|
||||
useEffect(() => {
|
||||
// 只有当有待处理的视频请求时才订阅WebSocket消息
|
||||
if (Object.keys(pendingVideoRequests).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("开始监听视频下载响应,当前待处理请求:", pendingVideoRequests);
|
||||
|
||||
// 订阅 WebSocket 消息变化
|
||||
const unsubscribe = useWebSocketStore.subscribe(state => {
|
||||
// 只处理新增的消息
|
||||
const messages = state.messages as WebSocketMessage[];
|
||||
|
||||
// 筛选出视频下载响应消息
|
||||
messages.forEach(message => {
|
||||
if (message?.content?.cmdType === "CmdDownloadVideoResult") {
|
||||
console.log("收到视频下载响应:", message.content);
|
||||
|
||||
// 检查是否是我们正在等待的视频响应
|
||||
const messageId = Object.keys(pendingVideoRequests).find(
|
||||
id => pendingVideoRequests[id] === message.content.friendMessageId,
|
||||
);
|
||||
|
||||
if (messageId) {
|
||||
console.log("找到对应的消息ID:", messageId);
|
||||
|
||||
// 从待处理队列中移除
|
||||
setPendingVideoRequests(prev => {
|
||||
const newRequests = { ...prev };
|
||||
delete newRequests[messageId];
|
||||
return newRequests;
|
||||
});
|
||||
|
||||
// 更新消息内容,将视频URL添加到对应的消息中
|
||||
setMessages(prevMessages => {
|
||||
return prevMessages.map(msg => {
|
||||
if (msg.id === Number(messageId)) {
|
||||
try {
|
||||
const msgContent =
|
||||
typeof msg.content === "string"
|
||||
? JSON.parse(msg.content)
|
||||
: msg.content;
|
||||
|
||||
// 更新消息内容,添加视频URL并移除加载状态
|
||||
return {
|
||||
...msg,
|
||||
content: JSON.stringify({
|
||||
...msgContent,
|
||||
videoUrl: message.content.url,
|
||||
isLoading: false,
|
||||
}),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("解析消息内容失败:", e);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 组件卸载时取消订阅
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [pendingVideoRequests]); // 依赖于pendingVideoRequests,当队列变化时重新设置订阅
|
||||
const unsubscribe = useWeChatStore.getState().initWebSocketListener();
|
||||
return unsubscribe;
|
||||
}, []);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
@@ -157,29 +84,13 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
});
|
||||
|
||||
// 将消息ID和请求序列号添加到待处理队列
|
||||
setPendingVideoRequests(prev => ({
|
||||
...prev,
|
||||
[messageId]: messageId,
|
||||
}));
|
||||
// 将消息ID添加到待处理队列
|
||||
useWeChatStore
|
||||
.getState()
|
||||
.addPendingVideoRequest(messageId.toString(), messageId.toString());
|
||||
|
||||
// 更新消息状态为加载中
|
||||
setMessages(prevMessages => {
|
||||
return prevMessages.map(msg => {
|
||||
if (msg.id === messageId) {
|
||||
// 保存原始内容,添加loading状态
|
||||
const originalContent = msg.content;
|
||||
return {
|
||||
...msg,
|
||||
content: JSON.stringify({
|
||||
...JSON.parse(originalContent),
|
||||
isLoading: true,
|
||||
}),
|
||||
};
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
});
|
||||
useWeChatStore.getState().setMessageLoading(messageId.toString(), true);
|
||||
};
|
||||
|
||||
// 解析消息内容,判断消息类型并返回对应的渲染内容
|
||||
@@ -613,14 +524,10 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
|
||||
// 用于分组消息并添加时间戳的辅助函数
|
||||
const groupMessagesByTime = (messages: ChatRecord[]) => {
|
||||
const groups: { time: string; messages: ChatRecord[] }[] = [];
|
||||
messages.forEach(msg => {
|
||||
// 使用 formatWechatTime 函数格式化时间戳
|
||||
const formattedTime = formatWechatTime(msg.wechatTime);
|
||||
groups.push({ time: formattedTime, messages: [msg] });
|
||||
});
|
||||
|
||||
return groups;
|
||||
return messages.map(msg => ({
|
||||
time: formatWechatTime(msg.wechatTime),
|
||||
messages: [msg],
|
||||
}));
|
||||
};
|
||||
|
||||
const renderMessage = (msg: ChatRecord) => {
|
||||
@@ -721,21 +628,13 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
{/* 聊天内容 */}
|
||||
<Content className={styles.chatContent}>
|
||||
<div className={styles.messagesContainer}>
|
||||
{loading ? (
|
||||
<div className={styles.loadingContainer}>
|
||||
<div>加载中...</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{groupMessagesByTime(messages).map((group, groupIndex) => (
|
||||
<React.Fragment key={`group-${groupIndex}`}>
|
||||
<div className={styles.messageTime}>{group.time}</div>
|
||||
{group.messages.map(renderMessage)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</>
|
||||
)}
|
||||
{groupMessagesByTime(currentMessages).map((group, groupIndex) => (
|
||||
<React.Fragment key={`group-${groupIndex}`}>
|
||||
<div className={styles.messageTime}>{group.time}</div>
|
||||
{group.messages.map(renderMessage)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
|
||||
@@ -7,8 +7,22 @@ export interface WeChatState {
|
||||
// 当前聊天用户的消息列表(只存储当前聊天用户的消息)
|
||||
currentMessages: ChatRecord[];
|
||||
|
||||
setCurrentContact: (contract: ContractData | weChatGroup) => void;
|
||||
// 消息加载状态
|
||||
messagesLoading: boolean;
|
||||
|
||||
// 待处理的视频请求队列
|
||||
pendingVideoRequests: Record<string, string>;
|
||||
|
||||
// Actions
|
||||
setCurrentContact: (contract: ContractData | weChatGroup) => void;
|
||||
loadChatMessages: (contact: ContractData | weChatGroup) => Promise<void>;
|
||||
|
||||
// 视频请求相关方法
|
||||
addPendingVideoRequest: (messageId: string, requestId: string) => void;
|
||||
removePendingVideoRequest: (messageId: string) => void;
|
||||
updateMessageWithVideo: (messageId: string, videoUrl: string) => void;
|
||||
setMessageLoading: (messageId: string, isLoading: boolean) => void;
|
||||
|
||||
// WebSocket监听初始化
|
||||
initWebSocketListener: () => void;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ import { WeChatState } from "./weChat.data";
|
||||
import { clearUnreadCount, updateConfig } from "@/pages/pc/ckbox/api";
|
||||
import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
|
||||
import { addChatSession } from "@/store/module/ckchat/ckchat";
|
||||
import {
|
||||
useWebSocketStore,
|
||||
WebSocketMessage,
|
||||
} from "@/store/module/websocket/websocket";
|
||||
export const useWeChatStore = create<WeChatState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
@@ -12,6 +16,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
currentContract: null,
|
||||
currentMessages: [],
|
||||
messagesLoading: false,
|
||||
pendingVideoRequests: {},
|
||||
|
||||
// Actions
|
||||
setCurrentContact: (contract: ContractData | weChatGroup) => {
|
||||
@@ -58,7 +63,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
},
|
||||
|
||||
setMessageLoading: loading => {
|
||||
set({ messagesLoading: loading });
|
||||
set({ messagesLoading: Boolean(loading) });
|
||||
},
|
||||
|
||||
addMessage: message => {
|
||||
@@ -80,11 +85,121 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
getCurrentMessages: () => get().currentMessages,
|
||||
getMessagesLoading: () => get().messagesLoading,
|
||||
|
||||
// 视频请求相关方法
|
||||
addPendingVideoRequest: (messageId: string, requestId: string) => {
|
||||
set(state => ({
|
||||
pendingVideoRequests: {
|
||||
...state.pendingVideoRequests,
|
||||
[messageId]: requestId,
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
removePendingVideoRequest: (messageId: string) => {
|
||||
set(state => {
|
||||
const newRequests = { ...state.pendingVideoRequests };
|
||||
delete newRequests[messageId];
|
||||
return { pendingVideoRequests: newRequests };
|
||||
});
|
||||
},
|
||||
|
||||
updateMessageWithVideo: (messageId: string, videoUrl: string) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg => {
|
||||
if (msg.id === Number(messageId)) {
|
||||
try {
|
||||
const msgContent =
|
||||
typeof msg.content === "string"
|
||||
? JSON.parse(msg.content)
|
||||
: msg.content;
|
||||
return {
|
||||
...msg,
|
||||
content: JSON.stringify({
|
||||
...msgContent,
|
||||
videoUrl: videoUrl,
|
||||
isLoading: false,
|
||||
}),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("解析消息内容失败:", e);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}),
|
||||
}));
|
||||
},
|
||||
|
||||
setMessageLoadingStatus: (messageId: string, isLoading: boolean) => {
|
||||
set(state => ({
|
||||
currentMessages: state.currentMessages.map(msg => {
|
||||
if (msg.id === Number(messageId)) {
|
||||
try {
|
||||
const originalContent = msg.content;
|
||||
return {
|
||||
...msg,
|
||||
content: JSON.stringify({
|
||||
...JSON.parse(originalContent),
|
||||
isLoading: isLoading,
|
||||
}),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("解析消息内容失败:", e);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}),
|
||||
}));
|
||||
},
|
||||
|
||||
// WebSocket监听初始化
|
||||
initWebSocketListener: () => {
|
||||
const unsubscribe = useWebSocketStore.subscribe(state => {
|
||||
const messages = state.messages as WebSocketMessage[];
|
||||
const currentState = get();
|
||||
|
||||
// 只有当有待处理的视频请求时才处理
|
||||
if (Object.keys(currentState.pendingVideoRequests).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
messages.forEach(message => {
|
||||
if (message?.content?.cmdType === "CmdDownloadVideoResult") {
|
||||
console.log("收到视频下载响应:", message.content);
|
||||
|
||||
// 检查是否是我们正在等待的视频响应
|
||||
const messageId = Object.keys(
|
||||
currentState.pendingVideoRequests,
|
||||
).find(
|
||||
id =>
|
||||
currentState.pendingVideoRequests[id] ===
|
||||
message.content.friendMessageId,
|
||||
);
|
||||
|
||||
if (messageId) {
|
||||
console.log("找到对应的消息ID:", messageId);
|
||||
|
||||
// 从待处理队列中移除
|
||||
currentState.removePendingVideoRequest(messageId);
|
||||
|
||||
// 更新消息内容,添加视频URL
|
||||
currentState.updateMessageWithVideo(
|
||||
messageId,
|
||||
message.content.url,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
},
|
||||
|
||||
clearAllData: () => {
|
||||
set({
|
||||
currentContract: null,
|
||||
currentMessages: [],
|
||||
messagesLoading: false,
|
||||
pendingVideoRequests: {},
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user