fix:Fix the incomplete cleanup issue when the receiver exits the room
This commit is contained in:
@@ -115,7 +115,6 @@ const FileListDisplay: React.FC<FileListDisplayProps> = ({
|
||||
fileType: file.type,
|
||||
fileId: generateFileId(file),
|
||||
}));
|
||||
console.log("Processed files:", processedFiles);
|
||||
for (let file of processedFiles) {
|
||||
if (file.folderName !== "") {
|
||||
folders_[file.folderName] = folders_[file.folderName] || {
|
||||
@@ -144,18 +143,11 @@ const FileListDisplay: React.FC<FileListDisplayProps> = ({
|
||||
if (file.size >= largeFileThreshold) needPick = true;
|
||||
}
|
||||
}
|
||||
// console.log('Single files before setState:', tempSingleFiles);
|
||||
// console.log('Folders before setState:', Object.values(folders_));
|
||||
|
||||
// Use functional updates to ensure the state is updated correctly
|
||||
setSingleFiles((prev) => {
|
||||
// console.log('Previous single files:', prev);
|
||||
// console.log('New single files:', tempSingleFiles);
|
||||
return [...tempSingleFiles];
|
||||
});
|
||||
setFolders((prev) => {
|
||||
// console.log('Previous folders:', prev);
|
||||
// console.log('New folders:', Object.values(folders_));
|
||||
return [...Object.values(folders_)];
|
||||
});
|
||||
setNeedPickLocation(needPick); // Set whether a save directory needs to be selected
|
||||
@@ -188,21 +180,11 @@ const FileListDisplay: React.FC<FileListDisplayProps> = ({
|
||||
setShowFinished((prev) => ({ ...prev, [fileId]: true }));
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(
|
||||
`[FileListDisplay Debug] ⏰ Timeout executing for fileId: ${fileId}, peerId: ${activePeerId}`
|
||||
);
|
||||
setShowFinished((prev) => {
|
||||
const updated = { ...prev };
|
||||
delete updated[fileId];
|
||||
console.log(
|
||||
`[FileListDisplay Debug] ❌ Deleted showFinished for fileId: ${fileId}`
|
||||
);
|
||||
return updated;
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[FileListDisplay Debug] 🗑️ Clearing progress for fileId: ${fileId}, peerId: ${activePeerId}`
|
||||
);
|
||||
// 根据模式清理对应的progress数据
|
||||
if (mode === "sender") {
|
||||
clearSendProgress(fileId, activePeerId);
|
||||
@@ -252,32 +234,8 @@ const FileListDisplay: React.FC<FileListDisplayProps> = ({
|
||||
const currentProgress = activePeerId
|
||||
? fileProgress?.[activePeerId]?.progress
|
||||
: null;
|
||||
|
||||
console.log(`[FileListDisplay Debug] 🔍 File: ${item.fileId}`);
|
||||
console.log(
|
||||
`[FileListDisplay Debug] - prevShowFinished: ${prevShowFinished}`
|
||||
);
|
||||
console.log(
|
||||
`[FileListDisplay Debug] - currentShowFinished: ${currentShowFinished}`
|
||||
);
|
||||
console.log(`[FileListDisplay Debug] - activePeerId: ${activePeerId}`);
|
||||
console.log(
|
||||
`[FileListDisplay Debug] - currentProgress: ${currentProgress}`
|
||||
);
|
||||
console.log(
|
||||
`[FileListDisplay Debug] - downloadCount: ${
|
||||
downloadCounts[item.fileId] || 0
|
||||
}`
|
||||
);
|
||||
|
||||
// Detecting false -> true transitions
|
||||
if (!prevShowFinished && currentShowFinished) {
|
||||
console.log(
|
||||
`[FileListDisplay Debug] 🎯 COUNTING! File ${item.fileId} transition detected`
|
||||
);
|
||||
console.log(
|
||||
`[FileListDisplay Debug] - Transition: ${prevShowFinished} -> ${currentShowFinished}`
|
||||
);
|
||||
if (!isSaveToDisk && onDownload) {
|
||||
onDownload(item);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ export function RetrieveTabPanel({
|
||||
senderDisconnected,
|
||||
isReceiverInRoom,
|
||||
} = useFileTransferStore();
|
||||
|
||||
const onLocationPick = useCallback(async (): Promise<boolean> => {
|
||||
if (!messages) return false; // Should not happen if panel is rendered
|
||||
if (!window.showDirectoryPicker) {
|
||||
|
||||
@@ -12,10 +12,17 @@ function format_peopleMsg(template: string, peerCount: number) {
|
||||
// 移除所有 WebRTC 相关的 props 依赖
|
||||
interface UseRoomManagerProps {
|
||||
messages: Messages | null;
|
||||
putMessageInMs: (message: string, isShareEnd?: boolean, displayTimeMs?: number) => void;
|
||||
putMessageInMs: (
|
||||
message: string,
|
||||
isShareEnd?: boolean,
|
||||
displayTimeMs?: number
|
||||
) => void;
|
||||
}
|
||||
|
||||
export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps) {
|
||||
export function useRoomManager({
|
||||
messages,
|
||||
putMessageInMs,
|
||||
}: UseRoomManagerProps) {
|
||||
// 从 store 获取状态
|
||||
const {
|
||||
shareRoomId,
|
||||
@@ -39,65 +46,84 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
} = useFileTransferStore();
|
||||
|
||||
// 加入房间方法 - 直接使用 webrtcService
|
||||
const joinRoom = useCallback(async (isSenderSide: boolean, roomId: string) => {
|
||||
if (!messages) return;
|
||||
const joinRoom = useCallback(
|
||||
async (isSenderSide: boolean, roomId: string) => {
|
||||
if (!messages) return;
|
||||
|
||||
try {
|
||||
console.log(`[RoomManager] 正在加入房间: ${roomId} (${isSenderSide ? '发送方' : '接收方'})`);
|
||||
|
||||
// 如果是发送方且房间ID不是初始ID,需要先创建房间
|
||||
if (isSenderSide && activeTab === "send" && roomId !== initShareRoomId) {
|
||||
try {
|
||||
const success = await createRoom(roomId);
|
||||
if (!success) {
|
||||
putMessageInMs(messages.text.ClipboardApp.joinRoom.DuplicateMsg, isSenderSide);
|
||||
try {
|
||||
// 如果是发送方且房间ID不是初始ID,需要先创建房间
|
||||
if (
|
||||
isSenderSide &&
|
||||
activeTab === "send" &&
|
||||
roomId !== initShareRoomId
|
||||
) {
|
||||
try {
|
||||
const success = await createRoom(roomId);
|
||||
if (!success) {
|
||||
putMessageInMs(
|
||||
messages.text.ClipboardApp.joinRoom.DuplicateMsg,
|
||||
isSenderSide
|
||||
);
|
||||
return;
|
||||
}
|
||||
setShareRoomId(roomId);
|
||||
} catch (error) {
|
||||
putMessageInMs(
|
||||
messages.text.ClipboardApp.joinRoom.failMsg +
|
||||
` (Create room error)`,
|
||||
isSenderSide
|
||||
);
|
||||
return;
|
||||
}
|
||||
setShareRoomId(roomId);
|
||||
} catch (error) {
|
||||
putMessageInMs(
|
||||
messages.text.ClipboardApp.joinRoom.failMsg + ` (Create room error)`,
|
||||
isSenderSide
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 确定实际要加入的房间ID
|
||||
const actualRoomId = isSenderSide && roomId !== initShareRoomId
|
||||
? roomId
|
||||
: isSenderSide
|
||||
? shareRoomId
|
||||
: roomId;
|
||||
|
||||
// 直接调用 service 方法,无需依赖注入
|
||||
await webrtcService.joinRoom(actualRoomId, isSenderSide);
|
||||
|
||||
putMessageInMs(messages.text.ClipboardApp.joinRoom.successMsg, isSenderSide, 6000);
|
||||
|
||||
// 更新分享链接
|
||||
if (isSenderSide) {
|
||||
const link = `${window.location.origin}${window.location.pathname}?roomId=${actualRoomId}`;
|
||||
setShareLink(link);
|
||||
if (actualRoomId !== shareRoomId) {
|
||||
setShareRoomId(actualRoomId);
|
||||
// 确定实际要加入的房间ID
|
||||
const actualRoomId =
|
||||
isSenderSide && roomId !== initShareRoomId
|
||||
? roomId
|
||||
: isSenderSide
|
||||
? shareRoomId
|
||||
: roomId;
|
||||
|
||||
// 直接调用 service 方法,无需依赖注入
|
||||
await webrtcService.joinRoom(actualRoomId, isSenderSide);
|
||||
|
||||
putMessageInMs(
|
||||
messages.text.ClipboardApp.joinRoom.successMsg,
|
||||
isSenderSide,
|
||||
6000
|
||||
);
|
||||
|
||||
// 更新分享链接
|
||||
if (isSenderSide) {
|
||||
const link = `${window.location.origin}${window.location.pathname}?roomId=${actualRoomId}`;
|
||||
setShareLink(link);
|
||||
if (actualRoomId !== shareRoomId) {
|
||||
setShareRoomId(actualRoomId);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[RoomManager] 加入房间失败:", error);
|
||||
let errorMsg = messages.text.ClipboardApp.joinRoom.failMsg;
|
||||
if (error instanceof Error) {
|
||||
errorMsg =
|
||||
error.message === "Room does not exist"
|
||||
? messages.text.ClipboardApp.joinRoom.notExist
|
||||
: `${messages.text.ClipboardApp.joinRoom.failMsg} ${error.message}`;
|
||||
}
|
||||
putMessageInMs(errorMsg, isSenderSide);
|
||||
}
|
||||
|
||||
console.log(`[RoomManager] 成功加入房间: ${actualRoomId}`);
|
||||
|
||||
// 注意:广播逻辑已经在 webrtcService 的 onDataChannelOpen 事件中自动处理
|
||||
} catch (error) {
|
||||
console.error("[RoomManager] 加入房间失败:", error);
|
||||
let errorMsg = messages.text.ClipboardApp.joinRoom.failMsg;
|
||||
if (error instanceof Error) {
|
||||
errorMsg = error.message === "Room does not exist"
|
||||
? messages.text.ClipboardApp.joinRoom.notExist
|
||||
: `${messages.text.ClipboardApp.joinRoom.failMsg} ${error.message}`;
|
||||
}
|
||||
putMessageInMs(errorMsg, isSenderSide);
|
||||
}
|
||||
}, [messages, putMessageInMs, activeTab, initShareRoomId, shareRoomId, setShareRoomId, setShareLink]);
|
||||
},
|
||||
[
|
||||
messages,
|
||||
putMessageInMs,
|
||||
activeTab,
|
||||
initShareRoomId,
|
||||
shareRoomId,
|
||||
setShareRoomId,
|
||||
setShareLink,
|
||||
]
|
||||
);
|
||||
|
||||
// 生成分享链接并广播
|
||||
const generateShareLinkAndBroadcast = useCallback(async () => {
|
||||
@@ -114,8 +140,6 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
// 更新分享链接
|
||||
const link = `${window.location.origin}${window.location.pathname}?roomId=${shareRoomId}`;
|
||||
setShareLink(link);
|
||||
|
||||
console.log("[RoomManager] 分享链接已生成并广播");
|
||||
} catch (error) {
|
||||
console.error("[RoomManager] 生成分享链接失败:", error);
|
||||
putMessageInMs("生成分享链接失败", true);
|
||||
@@ -127,22 +151,21 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
if (!messages) return;
|
||||
|
||||
try {
|
||||
console.log("[RoomManager] 接收方离开房间");
|
||||
|
||||
// 调用后端 API 离开房间
|
||||
if (webrtcService.receiver.roomId && webrtcService.receiver.peerId) {
|
||||
await leaveRoom(webrtcService.receiver.roomId, webrtcService.receiver.peerId);
|
||||
await leaveRoom(
|
||||
webrtcService.receiver.roomId,
|
||||
webrtcService.receiver.peerId
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
putMessageInMs(messages.text.ClipboardApp.roomStatus.leftRoomMsg, false);
|
||||
|
||||
|
||||
// 重置接收方状态
|
||||
resetReceiverState();
|
||||
|
||||
|
||||
// 清理 WebRTC 连接
|
||||
await webrtcService.leaveRoom(false);
|
||||
|
||||
console.log("[RoomManager] 接收方已离开房间并重置状态");
|
||||
} catch (error) {
|
||||
console.error("[RoomManager] 接收方离开房间失败:", error);
|
||||
putMessageInMs("离开房间失败", true);
|
||||
@@ -152,8 +175,6 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
// 发送方重置应用状态
|
||||
const resetSenderAppState = useCallback(async () => {
|
||||
try {
|
||||
console.log("[RoomManager] 重置发送方应用状态");
|
||||
|
||||
// 1. 清理 WebRTC 连接
|
||||
await webrtcService.leaveRoom(true);
|
||||
|
||||
@@ -164,8 +185,6 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
const newRoomId = await fetchRoom();
|
||||
setShareRoomId(newRoomId || "");
|
||||
setInitShareRoomId(newRoomId || "");
|
||||
|
||||
console.log("[RoomManager] 发送方状态重置完成,新房间ID:", newRoomId);
|
||||
} catch (error) {
|
||||
console.error("[RoomManager] 重置发送方状态失败:", error);
|
||||
putMessageInMs("重置发送方状态失败", true);
|
||||
@@ -177,15 +196,16 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
if (!messages) return;
|
||||
|
||||
try {
|
||||
console.log("[RoomManager] 发送方离开房间");
|
||||
|
||||
// 调用后端 API 离开房间
|
||||
if (webrtcService.sender.roomId && webrtcService.sender.peerId) {
|
||||
await leaveRoom(webrtcService.sender.roomId, webrtcService.sender.peerId);
|
||||
await leaveRoom(
|
||||
webrtcService.sender.roomId,
|
||||
webrtcService.sender.peerId
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
putMessageInMs(messages.text.ClipboardApp.roomStatus.leftRoomMsg, true);
|
||||
|
||||
|
||||
// 重置发送方状态并获取新房间ID
|
||||
await resetSenderAppState();
|
||||
} catch (error) {
|
||||
@@ -198,15 +218,20 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
const processRoomIdInput = useCallback(
|
||||
debounce(async (input: string) => {
|
||||
if (!input.trim() || !messages) return;
|
||||
|
||||
|
||||
try {
|
||||
const isValid = await checkRoom(input);
|
||||
if (isValid) {
|
||||
console.log(`[RoomManager] 房间 ${input} 验证成功`);
|
||||
setShareRoomId(input);
|
||||
putMessageInMs(messages.text.ClipboardApp.roomCheck.available_msg, true);
|
||||
putMessageInMs(
|
||||
messages.text.ClipboardApp.roomCheck.available_msg,
|
||||
true
|
||||
);
|
||||
} else {
|
||||
putMessageInMs(messages.text.ClipboardApp.roomCheck.notAvailable_msg, true);
|
||||
putMessageInMs(
|
||||
messages.text.ClipboardApp.roomCheck.notAvailable_msg,
|
||||
true
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[RoomManager] 验证房间失败:", error);
|
||||
@@ -218,7 +243,12 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
|
||||
// 初始化发送方房间ID
|
||||
useEffect(() => {
|
||||
if (messages && putMessageInMs && !initShareRoomId && activeTab === "send") {
|
||||
if (
|
||||
messages &&
|
||||
putMessageInMs &&
|
||||
!initShareRoomId &&
|
||||
activeTab === "send"
|
||||
) {
|
||||
const initNewRoom = async () => {
|
||||
try {
|
||||
const newRoomId = await fetchRoom();
|
||||
@@ -226,13 +256,21 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
setInitShareRoomId(newRoomId || "");
|
||||
} catch (err) {
|
||||
console.error("[RoomManager] 获取初始房间失败:", err);
|
||||
const errorMsg = messages.text?.ClipboardApp?.fetchRoom_err || "获取房间ID失败";
|
||||
const errorMsg =
|
||||
messages.text?.ClipboardApp?.fetchRoom_err || "获取房间ID失败";
|
||||
putMessageInMs(errorMsg, true);
|
||||
}
|
||||
};
|
||||
initNewRoom();
|
||||
}
|
||||
}, [messages, putMessageInMs, initShareRoomId, activeTab, setShareRoomId, setInitShareRoomId]);
|
||||
}, [
|
||||
messages,
|
||||
putMessageInMs,
|
||||
initShareRoomId,
|
||||
activeTab,
|
||||
setShareRoomId,
|
||||
setInitShareRoomId,
|
||||
]);
|
||||
|
||||
// 房间状态文本更新
|
||||
useEffect(() => {
|
||||
@@ -243,22 +281,25 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
}
|
||||
|
||||
const isInRoom = activeTab === "send" ? isSenderInRoom : isReceiverInRoom;
|
||||
const currentPeerCount = activeTab === "send" ? sharePeerCount : retrievePeerCount;
|
||||
const currentPeerCount =
|
||||
activeTab === "send" ? sharePeerCount : retrievePeerCount;
|
||||
let statusText = "";
|
||||
|
||||
if (!isInRoom) {
|
||||
statusText = activeTab === "retrieve"
|
||||
? messages.text.ClipboardApp.roomStatus.receiverEmptyMsg
|
||||
: messages.text.ClipboardApp.roomStatus.senderEmptyMsg;
|
||||
statusText =
|
||||
activeTab === "retrieve"
|
||||
? messages.text.ClipboardApp.roomStatus.receiverEmptyMsg
|
||||
: messages.text.ClipboardApp.roomStatus.senderEmptyMsg;
|
||||
} else if (currentPeerCount === 0) {
|
||||
statusText = messages.text.ClipboardApp.roomStatus.onlyOneMsg;
|
||||
} else {
|
||||
statusText = activeTab === "send"
|
||||
? format_peopleMsg(
|
||||
messages.text.ClipboardApp.roomStatus.peopleMsg_template,
|
||||
currentPeerCount + 1
|
||||
)
|
||||
: messages.text.ClipboardApp.roomStatus.connected_dis;
|
||||
statusText =
|
||||
activeTab === "send"
|
||||
? format_peopleMsg(
|
||||
messages.text.ClipboardApp.roomStatus.peopleMsg_template,
|
||||
currentPeerCount + 1
|
||||
)
|
||||
: messages.text.ClipboardApp.roomStatus.connected_dis;
|
||||
}
|
||||
|
||||
if (activeTab === "send") setShareRoomStatusText(statusText);
|
||||
@@ -287,7 +328,7 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
senderDisconnected,
|
||||
isSenderInRoom,
|
||||
isReceiverInRoom,
|
||||
|
||||
|
||||
// 方法
|
||||
processRoomIdInput,
|
||||
joinRoom,
|
||||
@@ -295,4 +336,4 @@ export function useRoomManager({ messages, putMessageInMs }: UseRoomManagerProps
|
||||
handleLeaveReceiverRoom,
|
||||
handleLeaveSenderRoom,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { webrtcService } from '@/lib/webrtcService';
|
||||
import { useFileTransferStore } from '@/stores/fileTransferStore';
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { webrtcService } from "@/lib/webrtcService";
|
||||
import { useFileTransferStore } from "@/stores/fileTransferStore";
|
||||
import type { Messages } from "@/types/messages";
|
||||
|
||||
// 保留类型定义以保持兼容性
|
||||
@@ -10,10 +10,17 @@ export type ProgressState = { [fileId: string]: FileProgressPeers };
|
||||
|
||||
interface UseWebRTCConnectionProps {
|
||||
messages: Messages | null;
|
||||
putMessageInMs: (message: string, isShareEnd?: boolean, displayTimeMs?: number) => void;
|
||||
putMessageInMs: (
|
||||
message: string,
|
||||
isShareEnd?: boolean,
|
||||
displayTimeMs?: number
|
||||
) => void;
|
||||
}
|
||||
|
||||
export function useWebRTCConnection({ messages, putMessageInMs }: UseWebRTCConnectionProps) {
|
||||
export function useWebRTCConnection({
|
||||
messages,
|
||||
putMessageInMs,
|
||||
}: UseWebRTCConnectionProps) {
|
||||
// 从 store 获取状态
|
||||
const {
|
||||
sharePeerCount,
|
||||
@@ -41,15 +48,6 @@ export function useWebRTCConnection({ messages, putMessageInMs }: UseWebRTCConne
|
||||
setIsAnyFileTransferring(isAnyFileTransferring);
|
||||
}, [isAnyFileTransferring, setIsAnyFileTransferring]);
|
||||
|
||||
// 确保服务在 React 生命周期内被初始化
|
||||
useEffect(() => {
|
||||
console.log('[useWebRTCConnection] WebRTC 服务已初始化');
|
||||
|
||||
return () => {
|
||||
console.log('[useWebRTCConnection] Hook 清理');
|
||||
};
|
||||
}, []);
|
||||
|
||||
return {
|
||||
// 状态从 store 获取
|
||||
sharePeerCount,
|
||||
@@ -57,21 +55,23 @@ export function useWebRTCConnection({ messages, putMessageInMs }: UseWebRTCConne
|
||||
senderDisconnected,
|
||||
sendProgress,
|
||||
receiveProgress,
|
||||
|
||||
|
||||
// 方法直接从 service 暴露
|
||||
broadcastDataToAllPeers: webrtcService.broadcastDataToAllPeers.bind(webrtcService),
|
||||
broadcastDataToAllPeers:
|
||||
webrtcService.broadcastDataToAllPeers.bind(webrtcService),
|
||||
requestFile: webrtcService.requestFile.bind(webrtcService),
|
||||
requestFolder: webrtcService.requestFolder.bind(webrtcService),
|
||||
setReceiverDirectoryHandle: webrtcService.setReceiverDirectoryHandle.bind(webrtcService),
|
||||
setReceiverDirectoryHandle:
|
||||
webrtcService.setReceiverDirectoryHandle.bind(webrtcService),
|
||||
getReceiverSaveType: webrtcService.getReceiverSaveType.bind(webrtcService),
|
||||
manualSafeSave: webrtcService.manualSafeSave.bind(webrtcService),
|
||||
|
||||
|
||||
// 重置连接方法
|
||||
resetSenderConnection: () => webrtcService.leaveRoom(true),
|
||||
resetReceiverConnection: () => webrtcService.leaveRoom(false),
|
||||
|
||||
|
||||
// 为了兼容性,保留这些属性(但实际上不再需要)
|
||||
sender: webrtcService.sender,
|
||||
receiver: webrtcService.receiver,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,13 +283,19 @@ class FileReceiver {
|
||||
peerId: string
|
||||
): Promise<void> {
|
||||
this.peerId = peerId;
|
||||
|
||||
if (typeof data === "string") {
|
||||
try {
|
||||
const parsedData = JSON.parse(data) as WebRTCMessage;
|
||||
|
||||
const handler =
|
||||
this.fileHandlers[parsedData.type as keyof FileHandlers];
|
||||
if (handler) {
|
||||
await handler(parsedData, peerId);
|
||||
} else {
|
||||
console.warn(
|
||||
`[DEBUG] ⚠️ FileReceiver 未找到处理器: ${parsedData.type}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.fireError("Error parsing received JSON data", { error });
|
||||
@@ -308,11 +314,20 @@ class FileReceiver {
|
||||
}
|
||||
|
||||
private handleFileMetadata(metadata: fileMetadata): void {
|
||||
if (this.pendingFilesMeta.has(metadata.fileId)) return; // Ignore if already received.
|
||||
if (this.pendingFilesMeta.has(metadata.fileId)) {
|
||||
console.log(
|
||||
`[DEBUG] 📥 FileReceiver 文件元数据已存在,忽略: ${metadata.fileId}`
|
||||
);
|
||||
return; // Ignore if already received.
|
||||
}
|
||||
|
||||
this.log("log", "Received file metadata", { metadata });
|
||||
this.pendingFilesMeta.set(metadata.fileId, metadata);
|
||||
this.onFileMetaReceived?.(metadata);
|
||||
|
||||
if (this.onFileMetaReceived) {
|
||||
this.onFileMetaReceived(metadata);
|
||||
} else {
|
||||
console.error(`[DEBUG] ❌ FileReceiver onFileMetaReceived 回调不存在!`);
|
||||
}
|
||||
// Record the file size for folder progress calculation.
|
||||
if (metadata.folderName) {
|
||||
const folderId = metadata.folderName;
|
||||
@@ -553,8 +568,15 @@ class FileReceiver {
|
||||
err,
|
||||
});
|
||||
});
|
||||
this.activeFileReception = null;
|
||||
}
|
||||
|
||||
// 🔧 清理所有内部状态,确保重新连接时能正确接收文件元数据
|
||||
this.pendingFilesMeta.clear();
|
||||
this.folderProgresses = {};
|
||||
this.saveType = {};
|
||||
this.activeFileReception = null;
|
||||
this.activeStringReception = null;
|
||||
this.currentFolderName = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,8 +203,10 @@ class FileSender {
|
||||
const fileId = generateFileId(file);
|
||||
this.pendingFiles.set(fileId, file);
|
||||
const fileMeta = this.getFileMeta(file);
|
||||
this.log("log", "Sending file metadata", { fileMeta, peerId: pId });
|
||||
if (!this.webrtcConnection.sendData(JSON.stringify(fileMeta), pId)) {
|
||||
const metaDataString = JSON.stringify(fileMeta);
|
||||
|
||||
const sendResult = this.webrtcConnection.sendData(metaDataString, pId);
|
||||
if (!sendResult) {
|
||||
this.fireError("Failed to send file metadata", {
|
||||
fileMeta,
|
||||
peerId: pId,
|
||||
|
||||
@@ -2,7 +2,11 @@ import WebRTC_Initiator from "@/lib/webrtc_Initiator";
|
||||
import WebRTC_Recipient from "@/lib/webrtc_Recipient";
|
||||
import FileSender from "@/lib/fileSender";
|
||||
import FileReceiver from "@/lib/fileReceiver";
|
||||
import { getIceServers, getSocketOptions, config } from "@/app/config/environment";
|
||||
import {
|
||||
getIceServers,
|
||||
getSocketOptions,
|
||||
config,
|
||||
} from "@/app/config/environment";
|
||||
import { useFileTransferStore } from "@/stores/fileTransferStore";
|
||||
import type { CustomFile } from "@/types/webrtc";
|
||||
|
||||
@@ -27,7 +31,6 @@ class WebRTCService {
|
||||
this.fileReceiver = new FileReceiver(this.receiver);
|
||||
|
||||
this.initializeEventHandlers();
|
||||
console.log("WebRTC Service 初始化完成 (单例模式)");
|
||||
}
|
||||
|
||||
public static getInstance(): WebRTCService {
|
||||
@@ -40,28 +43,31 @@ class WebRTCService {
|
||||
private initializeEventHandlers(): void {
|
||||
// 发送方事件处理
|
||||
this.sender.onConnectionStateChange = (state, peerId) => {
|
||||
console.log(`[WebRTC Service] 发送方连接状态: ${state} (对等端: ${peerId})`);
|
||||
useFileTransferStore.getState().setShareConnectionState(state as any);
|
||||
useFileTransferStore.getState().setSharePeerCount(this.sender.peerConnections.size);
|
||||
|
||||
if (state === 'connected') {
|
||||
useFileTransferStore
|
||||
.getState()
|
||||
.setSharePeerCount(this.sender.peerConnections.size);
|
||||
|
||||
if (state === "connected") {
|
||||
this.fileSender.setProgressCallback((fileId, progress, speed) => {
|
||||
useFileTransferStore.getState().updateSendProgress(fileId, peerId, { progress, speed });
|
||||
useFileTransferStore
|
||||
.getState()
|
||||
.updateSendProgress(fileId, peerId, { progress, speed });
|
||||
}, peerId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.sender.onDataChannelOpen = (peerId) => {
|
||||
console.log(`[WebRTC Service] 发送方数据通道打开: ${peerId}`);
|
||||
useFileTransferStore.getState().setIsSenderInRoom(true);
|
||||
// 自动广播当前内容
|
||||
this.broadcastDataToAllPeers();
|
||||
};
|
||||
|
||||
this.sender.onPeerDisconnected = (peerId) => {
|
||||
console.log(`[WebRTC Service] 发送方对等端断开: ${peerId}`);
|
||||
setTimeout(() => {
|
||||
useFileTransferStore.getState().setSharePeerCount(this.sender.peerConnections.size);
|
||||
useFileTransferStore
|
||||
.getState()
|
||||
.setSharePeerCount(this.sender.peerConnections.size);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
@@ -71,15 +77,18 @@ class WebRTCService {
|
||||
|
||||
// 接收方事件处理
|
||||
this.receiver.onConnectionStateChange = (state, peerId) => {
|
||||
console.log(`[WebRTC Service] 接收方连接状态: ${state} (对等端: ${peerId})`);
|
||||
useFileTransferStore.getState().setRetrieveConnectionState(state as any);
|
||||
useFileTransferStore.getState().setRetrievePeerCount(this.receiver.peerConnections.size);
|
||||
|
||||
if (state === 'connected') {
|
||||
useFileTransferStore
|
||||
.getState()
|
||||
.setRetrievePeerCount(this.receiver.peerConnections.size);
|
||||
|
||||
if (state === "connected") {
|
||||
this.fileReceiver.setProgressCallback((fileId, progress, speed) => {
|
||||
useFileTransferStore.getState().updateReceiveProgress(fileId, peerId, { progress, speed });
|
||||
useFileTransferStore
|
||||
.getState()
|
||||
.updateReceiveProgress(fileId, peerId, { progress, speed });
|
||||
});
|
||||
} else if (state === 'failed' || state === 'disconnected') {
|
||||
} else if (state === "failed" || state === "disconnected") {
|
||||
const { isAnyFileTransferring } = useFileTransferStore.getState();
|
||||
if (isAnyFileTransferring) {
|
||||
this.fileReceiver.gracefulShutdown();
|
||||
@@ -88,13 +97,14 @@ class WebRTCService {
|
||||
};
|
||||
|
||||
this.receiver.onConnectionEstablished = (peerId) => {
|
||||
console.log(`[WebRTC Service] 接收方连接建立: ${peerId}`);
|
||||
const store = useFileTransferStore.getState();
|
||||
useFileTransferStore.getState().setSenderDisconnected(false);
|
||||
useFileTransferStore.getState().setIsReceiverInRoom(true);
|
||||
};
|
||||
|
||||
this.receiver.onPeerDisconnected = (peerId) => {
|
||||
console.log(`[WebRTC Service] 接收方对等端断开: ${peerId}`);
|
||||
const store = useFileTransferStore.getState();
|
||||
|
||||
useFileTransferStore.getState().setSenderDisconnected(true);
|
||||
useFileTransferStore.getState().setRetrievePeerCount(0);
|
||||
};
|
||||
@@ -119,20 +129,16 @@ class WebRTCService {
|
||||
|
||||
// 业务方法
|
||||
public async joinRoom(roomId: string, isSender: boolean): Promise<void> {
|
||||
console.log(`[WebRTC Service] 加入房间: ${roomId} (${isSender ? '发送方' : '接收方'})`);
|
||||
|
||||
const peer = isSender ? this.sender : this.receiver;
|
||||
await peer.joinRoom(roomId, isSender);
|
||||
|
||||
const setInRoom = isSender
|
||||
|
||||
const setInRoom = isSender
|
||||
? useFileTransferStore.getState().setIsSenderInRoom
|
||||
: useFileTransferStore.getState().setIsReceiverInRoom;
|
||||
setInRoom(true);
|
||||
}
|
||||
|
||||
public async leaveRoom(isSender: boolean): Promise<void> {
|
||||
console.log(`[WebRTC Service] 离开房间 (${isSender ? '发送方' : '接收方'})`);
|
||||
|
||||
if (isSender) {
|
||||
await this.sender.leaveRoomAndCleanup();
|
||||
useFileTransferStore.getState().setIsSenderInRoom(false);
|
||||
@@ -147,7 +153,6 @@ class WebRTCService {
|
||||
public async broadcastDataToAllPeers(): Promise<boolean> {
|
||||
const { shareContent, sendFiles } = useFileTransferStore.getState();
|
||||
const peerIds = Array.from(this.sender.peerConnections.keys());
|
||||
|
||||
if (peerIds.length === 0) {
|
||||
console.warn("[WebRTC Service] 没有连接的对等端进行广播");
|
||||
return false;
|
||||
@@ -164,7 +169,6 @@ class WebRTCService {
|
||||
}
|
||||
})
|
||||
);
|
||||
console.log(`[WebRTC Service] 成功广播到 ${peerIds.length} 个对等端`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("[WebRTC Service] 广播失败:", error);
|
||||
@@ -180,7 +184,9 @@ class WebRTCService {
|
||||
this.fileReceiver.requestFolder(folderName);
|
||||
}
|
||||
|
||||
public async setReceiverDirectoryHandle(directoryHandle: FileSystemDirectoryHandle): Promise<void> {
|
||||
public async setReceiverDirectoryHandle(
|
||||
directoryHandle: FileSystemDirectoryHandle
|
||||
): Promise<void> {
|
||||
return this.fileReceiver.setSaveDirectory(directoryHandle);
|
||||
}
|
||||
|
||||
@@ -197,7 +203,7 @@ class WebRTCService {
|
||||
try {
|
||||
await Promise.all([
|
||||
this.sender.cleanUpBeforeExit(),
|
||||
this.receiver.cleanUpBeforeExit()
|
||||
this.receiver.cleanUpBeforeExit(),
|
||||
]);
|
||||
console.log("[WebRTC Service] 清理完成");
|
||||
} catch (error) {
|
||||
|
||||
@@ -237,16 +237,25 @@ export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
|
||||
setShareMessage: (message) => set({ shareMessage: message }),
|
||||
setRetrieveMessage: (message) => set({ retrieveMessage: message }),
|
||||
|
||||
resetReceiverState: () =>
|
||||
resetReceiverState: () => {
|
||||
// 🔧 清理 FileReceiver 的内部状态(通过 Service 层)
|
||||
try {
|
||||
const { webrtcService } = require("@/lib/webrtcService");
|
||||
webrtcService.fileReceiver.gracefulShutdown();
|
||||
} catch (error) {
|
||||
console.warn(`[DEBUG] ⚠️ 清理 FileReceiver 状态失败:`, error);
|
||||
}
|
||||
|
||||
set({
|
||||
retrievedContent: "",
|
||||
retrievedFiles: [],
|
||||
retrievedFileMetas: [],
|
||||
retrievedFileMetas: [], // 清空 Store 中的文件元数据
|
||||
retrievePeerCount: 0,
|
||||
senderDisconnected: false,
|
||||
receiveProgress: {},
|
||||
retrieveRoomStatusText: "",
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
resetSenderApp: () =>
|
||||
set({
|
||||
|
||||
Reference in New Issue
Block a user