fix:The recipient needs to clean the connection when leaving the room

This commit is contained in:
david_bai
2025-08-17 12:14:26 +08:00
parent 75f9ff39ae
commit 20607be9aa
7 changed files with 182 additions and 174 deletions
+2 -35
View File
@@ -31,20 +31,9 @@ const ClipboardApp = () => {
// 从 store 中获取状态
const {
activeTab,
retrieveRoomIdInput,
isDragging,
shareContent,
sendFiles,
retrievedContent,
retrievedFileMetas,
sendProgress,
receiveProgress,
isAnyFileTransferring,
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
setIsDragging,
setRetrieveRoomIdInput,
setActiveTab,
@@ -60,13 +49,6 @@ const ClipboardApp = () => {
handleDownloadFile,
} = useFileTransferHandler({ messages, putMessageInMs });
// Calculate the derived states for unload protection
const isContentPresent = useMemo(() => {
return (
shareContent !== "" || retrievedContent !== "" || sendFiles.length > 0
);
}, [shareContent, retrievedContent, sendFiles]);
// Initialize WebRTC Connection Hook
const {
sender,
@@ -83,9 +65,6 @@ const ClipboardApp = () => {
resetSenderConnection,
manualSafeSave,
} = useWebRTCConnection({
shareContent,
sendFiles,
isContentPresent,
messages,
putMessageInMs,
});
@@ -120,9 +99,9 @@ const ClipboardApp = () => {
putMessageInMs,
sender,
receiver,
broadcastDataToPeers: () =>
broadcastDataToAllPeers(shareContent, sendFiles),
broadcastDataToPeers: broadcastDataToAllPeers,
resetSenderConnection,
resetReceiverConnection,
});
const handleFileDrop = useCallback(
@@ -245,15 +224,10 @@ const ClipboardApp = () => {
{activeTab === "send" ? (
<SendTabPanel
messages={messages}
shareRoomStatusText={shareRoomStatusText}
shareContent={shareContent}
sendFiles={sendFiles}
updateShareContent={updateShareContent}
addFilesToSend={addFilesToSend}
removeFileToSend={removeFileToSend}
richTextToPlainText={richTextToPlainText}
sendProgress={sendProgress}
isAnyFileTransferring={isAnyFileTransferring}
processRoomIdInput={processRoomIdInput}
joinRoom={joinRoom}
generateShareLinkAndBroadcast={generateShareLinkAndBroadcast}
@@ -266,24 +240,17 @@ const ClipboardApp = () => {
<RetrieveTabPanel
messages={messages}
putMessageInMs={putMessageInMs}
retrieveRoomStatusText={retrieveRoomStatusText}
retrieveRoomIdInput={retrieveRoomIdInput}
setRetrieveRoomIdInput={setRetrieveRoomIdInput}
joinRoom={joinRoom}
retrieveJoinRoomBtnRef={retrieveJoinRoomBtnRef}
receiver={receiver}
retrievedContent={retrievedContent}
richTextToPlainText={richTextToPlainText}
retrievedFileMetas={retrievedFileMetas}
receiveProgress={receiveProgress}
isAnyFileTransferring={isAnyFileTransferring}
handleDownloadFile={handleDownloadFile}
requestFile={requestFile}
requestFolder={requestFolder}
setReceiverDirectoryHandle={setReceiverDirectoryHandle}
getReceiverSaveType={getReceiverSaveType}
retrieveMessage={retrieveMessage}
senderDisconnected={senderDisconnected}
handleLeaveRoom={handleLeaveReceiverRoom}
manualSafeSave={manualSafeSave}
/>
@@ -11,6 +11,7 @@ import type { Messages } from "@/types/messages";
import type { FileMeta } from "@/types/webrtc";
import type { ProgressState } from "@/hooks/useWebRTCConnection"; // Assuming this type is exported
import type WebRTC_Recipient from "@/lib/webrtc_Recipient";
import { useFileTransferStore } from "@/stores/fileTransferStore";
interface RetrieveTabPanelProps {
messages: Messages;
@@ -19,18 +20,11 @@ interface RetrieveTabPanelProps {
isShareEnd?: boolean,
displayTimeMs?: number
) => void; // For onLocationPick
retrieveRoomStatusText: string;
retrieveRoomIdInput: string;
setRetrieveRoomIdInput: (value: string) => void;
joinRoom: (isSender: boolean, roomId: string) => void;
retrieveJoinRoomBtnRef: React.RefObject<HTMLButtonElement>;
receiver: WebRTC_Recipient | null;
retrievedContent: string;
// setRetrievedContent: (content: string) => void; // If editor becomes editable
richTextToPlainText: (html: string) => string;
retrievedFileMetas: FileMeta[];
receiveProgress: ProgressState;
isAnyFileTransferring: boolean;
handleDownloadFile: (meta: FileMeta) => void;
// Functions for WebRTC interaction, passed from parent via useWebRTCConnection
requestFile: (fileId: string, peerId?: string) => void;
@@ -41,24 +35,17 @@ interface RetrieveTabPanelProps {
getReceiverSaveType: () => { [fileId: string]: boolean } | undefined;
manualSafeSave: () => void; // Add manual safe save function
retrieveMessage: string;
senderDisconnected: boolean;
handleLeaveRoom: () => void;
}
export function RetrieveTabPanel({
messages,
putMessageInMs,
retrieveRoomStatusText,
retrieveRoomIdInput,
setRetrieveRoomIdInput,
joinRoom,
retrieveJoinRoomBtnRef,
receiver,
retrievedContent,
richTextToPlainText,
retrievedFileMetas,
receiveProgress,
isAnyFileTransferring,
handleDownloadFile,
requestFile,
requestFolder,
@@ -66,9 +53,19 @@ export function RetrieveTabPanel({
getReceiverSaveType,
manualSafeSave,
retrieveMessage,
senderDisconnected,
handleLeaveRoom,
}: RetrieveTabPanelProps) {
// 从 store 中获取状态
const {
retrieveRoomStatusText,
retrieveRoomIdInput,
retrievedContent,
retrievedFileMetas,
receiveProgress,
isAnyFileTransferring,
senderDisconnected,
isReceiverInRoom,
} = useFileTransferStore();
const onLocationPick = useCallback(async (): Promise<boolean> => {
if (!messages) return false; // Should not happen if panel is rendered
if (!window.showDirectoryPicker) {
@@ -145,7 +142,7 @@ export function RetrieveTabPanel({
onClick={() => joinRoom(false, retrieveRoomIdInput)}
ref={retrieveJoinRoomBtnRef}
disabled={
!receiver || receiver.isInRoom || !retrieveRoomIdInput.trim()
!receiver || isReceiverInRoom || !retrieveRoomIdInput.trim()
}
>
{messages.text.ClipboardApp.html.joinRoom_dis}
@@ -153,7 +150,7 @@ export function RetrieveTabPanel({
<Button
variant="outline"
onClick={handleLeaveRoom}
disabled={!receiver || !receiver.isInRoom || isAnyFileTransferring}
disabled={!receiver || !isReceiverInRoom || isAnyFileTransferring}
>
{messages.text.ClipboardApp.roomStatus.leaveRoomBtn}
</Button>
@@ -13,6 +13,7 @@ import type { Messages } from "@/types/messages";
import type { CustomFile, FileMeta } from "@/types/webrtc";
import type { ProgressState } from "@/hooks/useWebRTCConnection";
import type WebRTC_Initiator from "@/lib/webrtc_Initiator";
import { useFileTransferStore } from "@/stores/fileTransferStore";
// Dynamically import the RichTextEditor
const RichTextEditor = dynamic(
@@ -29,39 +30,25 @@ const RichTextEditor = dynamic(
interface SendTabPanelProps {
messages: Messages;
shareRoomStatusText: string;
shareContent: string;
sendFiles: CustomFile[];
updateShareContent: (content: string) => void;
addFilesToSend: (files: CustomFile[]) => void;
removeFileToSend: (meta: FileMeta) => void;
richTextToPlainText: (html: string) => string;
sendProgress: ProgressState;
isAnyFileTransferring: boolean;
// shareRoomId: string; // This comes from useRoomManager and represents the validated ID
processRoomIdInput: (roomId: string) => void; // Passed from useRoomManager
joinRoom: (isSender: boolean, roomId: string) => void;
generateShareLinkAndBroadcast: () => void;
sender: WebRTC_Initiator | null;
shareMessage: string;
// Pass the validated/initial shareRoomId from useRoomManager for display/initialization
// Also, initShareRoomId can be useful if we want to reset the input to it.
currentValidatedShareRoomId: string;
handleLeaveSenderRoom: () => void; // New prop for leaving room
// initShareRoomId: string; // If needed for reset logic
}
export function SendTabPanel({
messages,
shareRoomStatusText,
shareContent,
sendFiles,
updateShareContent,
addFilesToSend,
removeFileToSend,
richTextToPlainText,
sendProgress,
isAnyFileTransferring,
processRoomIdInput,
joinRoom,
generateShareLinkAndBroadcast,
@@ -69,8 +56,16 @@ export function SendTabPanel({
shareMessage,
currentValidatedShareRoomId,
handleLeaveSenderRoom,
}: // initShareRoomId,
SendTabPanelProps) {
}: SendTabPanelProps) {
// 从 store 中获取状态
const {
shareContent,
sendFiles,
shareRoomStatusText,
sendProgress,
isAnyFileTransferring,
isSenderInRoom,
} = useFileTransferStore();
// Local state for immediate response in the input field
const [inputFieldValue, setInputFieldValue] = useState<string>(
currentValidatedShareRoomId
@@ -103,7 +98,7 @@ SendTabPanelProps) {
<div id="send-panel" role="tabpanel" aria-labelledby="send-tab">
<div className="mb-3 text-sm text-gray-600">
{shareRoomStatusText ||
(sender?.isInRoom
(isSenderInRoom
? messages.text.ClipboardApp.roomStatus.onlyOneMsg
: messages.text.ClipboardApp.roomStatus.senderEmptyMsg)}
</div>
@@ -142,7 +137,7 @@ SendTabPanelProps) {
<Button
className="w-full sm:w-auto"
onClick={() => joinRoom(true, inputFieldValue.trim())} // Attempt to join using the current input field value
disabled={!sender || sender.isInRoom || !inputFieldValue.trim()}
disabled={!sender || isSenderInRoom || !inputFieldValue.trim()}
>
{messages.text.ClipboardApp.html.joinRoom_dis}
</Button>
@@ -154,7 +149,7 @@ SendTabPanelProps) {
loadingText={messages.text.ClipboardApp.html.SyncSending_loadingText}
disabled={
!sender ||
!sender.isInRoom ||
!isSenderInRoom ||
(sendFiles.length === 0 && shareContent.trim() === "") ||
!currentValidatedShareRoomId.trim() ||
isAnyFileTransferring
@@ -165,7 +160,7 @@ SendTabPanelProps) {
<Button
variant="outline"
onClick={handleLeaveSenderRoom}
disabled={!sender || !sender.isInRoom || isAnyFileTransferring}
disabled={!sender || !isSenderInRoom || isAnyFileTransferring}
>
{messages.text.ClipboardApp.roomStatus.leaveRoomBtn}
</Button>
+1 -31
View File
@@ -60,34 +60,7 @@ export function useFileTransferHandler({
removeSendFile(metaToRemove);
}, [removeSendFile]);
// Callbacks for useWebRTCConnection
const onStringDataReceived = useCallback((data: string, peerId: string) => {
setRetrievedContent(data);
}, [setRetrievedContent]);
const onFileMetadataReceived = useCallback(
(meta: fileMetadata, peerId: string) => {
const { type, ...metaWithoutType } = meta;
// Filter out existing file with same ID and add the new one
const DPrev = retrievedFileMetas.filter(
(existingFile: FileMeta) => existingFile.fileId !== metaWithoutType.fileId
);
setRetrievedFileMetas([...DPrev, metaWithoutType]);
},
[retrievedFileMetas, setRetrievedFileMetas]
);
const onFileFullyReceived = useCallback((file: CustomFile, peerId: string) => {
// Check if file already exists
const isDuplicate = retrievedFiles.some(
(existingFile: CustomFile) =>
existingFile.fullName === file.fullName &&
existingFile.size === file.size
);
if (!isDuplicate) {
setRetrievedFiles([...retrievedFiles, file]);
}
}, [retrievedFiles, setRetrievedFiles]);
// 这些回调函数已经不再需要,因为WebRTC Hook现在直接使用Store
const handleDownloadFile = useCallback(
async (meta: FileMeta) => {
@@ -159,9 +132,6 @@ export function useFileTransferHandler({
addFilesToSend,
removeFileToSend,
resetReceiverState, // Export the reset function
onStringDataReceived,
onFileMetadataReceived,
onFileFullyReceived,
handleDownloadFile,
};
}
+49 -6
View File
@@ -21,6 +21,7 @@ interface UseRoomManagerProps {
receiver: WebRTC_Recipient | null;
broadcastDataToPeers: () => Promise<boolean>;
resetSenderConnection: () => Promise<void>;
resetReceiverConnection: () => Promise<void>;
}
export function useRoomManager({
@@ -30,6 +31,7 @@ export function useRoomManager({
receiver,
broadcastDataToPeers,
resetSenderConnection,
resetReceiverConnection,
}: UseRoomManagerProps) {
// 从 store 中获取状态
const {
@@ -57,6 +59,7 @@ export function useRoomManager({
// Receiver leave room function
const handleLeaveReceiverRoom = useCallback(async () => {
console.log(`[RoomManager Debug] Receiver leaving room manually`);
if (!receiver || !receiver.roomId || !receiver.peerId || !messages) return;
try {
await leaveRoom(receiver.roomId, receiver.peerId);
@@ -65,17 +68,20 @@ export function useRoomManager({
console.error("Error leaving room:", error);
putMessageInMs("Failed to leave the room.", false);
} finally {
// Reset application state
// Reset application state (不清空房间ID)
resetReceiverState();
// Reset peer count
setRetrievePeerCount(0);
// 清理WebRTC连接状态
await resetReceiverConnection();
console.log(
`[RoomManager Debug] Receiver left room and WebRTC connection reset`
);
}
}, [
receiver,
putMessageInMs,
messages,
resetReceiverState,
setRetrievePeerCount,
resetReceiverConnection,
]);
// Reset sender app state
@@ -256,6 +262,15 @@ export function useRoomManager({
? shareRoomId
: currentRoomIdToJoin;
console.log(
`[RoomManager Debug] ${
isSenderSide ? "Sender" : "Receiver"
} joining room: ${actualRoomIdForSenderJoin}`
);
console.log(
`[RoomManager Debug] Peer current state - isInRoom: ${peer.isInRoom}, roomId: ${peer.roomId}`
);
await peer.joinRoom(actualRoomIdForSenderJoin, isSenderSide);
putMessageInMs(
messages.text.ClipboardApp.joinRoom.successMsg,
@@ -266,6 +281,9 @@ export function useRoomManager({
// 更新 Store 中的房间状态
if (isSenderSide) {
useFileTransferStore.getState().setIsSenderInRoom(true);
console.log(
`[RoomManager Debug] Sender joined room, setIsSenderInRoom(true)`
);
const link = `${window.location.origin}${window.location.pathname}?roomId=${actualRoomIdForSenderJoin}`;
setShareLink(link);
if (actualRoomIdForSenderJoin !== shareRoomId) {
@@ -273,6 +291,9 @@ export function useRoomManager({
}
} else {
useFileTransferStore.getState().setIsReceiverInRoom(true);
console.log(
`[RoomManager Debug] Receiver joined room, setIsReceiverInRoom(true)`
);
}
} catch (error) {
let errorMsgToShow = messages.text.ClipboardApp.joinRoom.failMsg;
@@ -301,7 +322,7 @@ export function useRoomManager({
const generateShareLinkAndBroadcast = useCallback(async () => {
if (!sender || !messages || !putMessageInMs || !shareRoomId) return;
if (sender.peerConnections.size === 0) {
if (sharePeerCount === 0) {
putMessageInMs(messages.text.ClipboardApp.waitting_tips, true);
} else {
await broadcastDataToPeers();
@@ -313,6 +334,7 @@ export function useRoomManager({
messages,
putMessageInMs,
shareRoomId,
sharePeerCount,
setShareLink,
broadcastDataToPeers,
]);
@@ -330,13 +352,31 @@ export function useRoomManager({
activeTab === "send" ? sharePeerCount : retrievePeerCount;
let statusText = "";
// 调试日志
console.log(
`[RoomStatus Debug] activeTab: ${activeTab}, isInRoom: ${isInRoom}, peerCount: ${currentPeerCount}`
);
if (activeTab === "send") {
console.log(
`[RoomStatus Debug] Sender - isSenderInRoom: ${isSenderInRoom}, sharePeerCount: ${sharePeerCount}`
);
} else {
console.log(
`[RoomStatus Debug] Receiver - isReceiverInRoom: ${isReceiverInRoom}, retrievePeerCount: ${retrievePeerCount}`
);
}
if (!isInRoom) {
statusText =
activeTab === "retrieve"
? messages.text.ClipboardApp.roomStatus.receiverEmptyMsg
: messages.text.ClipboardApp.roomStatus.senderEmptyMsg;
console.log(`[RoomStatus Debug] Not in room, status: ${statusText}`);
} else if (currentPeerCount === 0) {
statusText = messages.text.ClipboardApp.roomStatus.onlyOneMsg;
console.log(
`[RoomStatus Debug] In room, no peers, status: ${statusText}`
);
} else {
statusText =
activeTab === "send"
@@ -345,8 +385,11 @@ export function useRoomManager({
currentPeerCount + 1
)
: messages.text.ClipboardApp.roomStatus.connected_dis;
console.log(
`[RoomStatus Debug] In room, with peers, status: ${statusText}`
);
}
if (activeTab === "send") setShareRoomStatusText(statusText);
else setRetrieveRoomStatusText(statusText);
}, [
+69 -55
View File
@@ -20,10 +20,6 @@ export type FileProgressPeers = { [peerId: string]: PeerProgressDetails };
export type ProgressState = { [fileId: string]: FileProgressPeers };
interface UseWebRTCConnectionProps {
shareContent: string;
sendFiles: CustomFile[];
isContentPresent: boolean;
// For user feedback and messages from the hook, if any (mostly for console for now)
messages: Messages | null;
putMessageInMs: (
@@ -34,9 +30,6 @@ interface UseWebRTCConnectionProps {
}
export function useWebRTCConnection({
shareContent,
sendFiles,
isContentPresent,
messages,
putMessageInMs,
}: UseWebRTCConnectionProps) {
@@ -47,8 +40,10 @@ export function useWebRTCConnection({
const [receiverFileTransfer, setReceiverFileTransfer] =
useState<FileReceiver | null>(null);
// 从 store 中获取状态
// 从 store 中获取状态和数据
const {
shareContent,
sendFiles,
sharePeerCount,
retrievePeerCount,
sendProgress,
@@ -128,36 +123,39 @@ export function useWebRTCConnection({
);
// Exposed function to broadcast data to all connected sender peers
const broadcastDataToAllPeers = useCallback(
async (textContent: string, filesToSend: CustomFile[]) => {
if (!sender || sender.peerConnections.size === 0) {
if (developmentEnv)
console.warn(
"No sender peers to broadcast to, or sender not initialized."
);
return false;
}
if (!senderFileTransfer) {
console.error("senderFileTransfer is not initialized for broadcast.");
return false;
}
const peerIds = Array.from(sender.peerConnections.keys());
const broadcastDataToAllPeers = useCallback(async () => {
if (!sender || sender.peerConnections.size === 0) {
if (developmentEnv)
console.log(`Broadcasting to peers: ${peerIds.join(", ")}`);
try {
await Promise.all(
peerIds.map((peerId) =>
sendStringAndMetasToPeer(peerId, textContent, filesToSend)
)
console.warn(
"No sender peers to broadcast to, or sender not initialized."
);
return true;
} catch (error) {
console.error("Error broadcasting data to peers:", error);
return false;
}
},
[sender, senderFileTransfer, sendStringAndMetasToPeer]
);
return false;
}
if (!senderFileTransfer) {
console.error("senderFileTransfer is not initialized for broadcast.");
return false;
}
const peerIds = Array.from(sender.peerConnections.keys());
if (developmentEnv)
console.log(`Broadcasting to peers: ${peerIds.join(", ")}`);
try {
await Promise.all(
peerIds.map((peerId) =>
sendStringAndMetasToPeer(peerId, shareContent, sendFiles)
)
);
return true;
} catch (error) {
console.error("Error broadcasting data to peers:", error);
return false;
}
}, [
sender,
senderFileTransfer,
sendStringAndMetasToPeer,
shareContent,
sendFiles,
]);
// Setup sender and receiver event handlers
useEffect(() => {
@@ -171,7 +169,9 @@ export function useWebRTCConnection({
if (state === "connected") {
senderFileTransfer.setProgressCallback(
(fileId, progress: number, speed: number) => {
useFileTransferStore.getState().updateSendProgress(fileId, peerId, { progress, speed });
useFileTransferStore
.getState()
.updateSendProgress(fileId, peerId, { progress, speed });
},
peerId
);
@@ -180,16 +180,17 @@ export function useWebRTCConnection({
sender.onDataChannelOpen = () => {
// 当数据通道打开时,标记发送方已加入房间
useFileTransferStore.getState().setIsSenderInRoom(true);
broadcastDataToAllPeers(shareContent, sendFiles);
}
broadcastDataToAllPeers();
};
sender.onPeerDisconnected = (peerId) => {
console.log(`[WebRTC Debug] Sender peer ${peerId} disconnected`);
setTimeout(() => {
setSharePeerCount(sender.peerConnections.size);
// 检查是否所有 peer 都已断开连接
if (sender.peerConnections.size === 0) {
useFileTransferStore.getState().setIsSenderInRoom(false);
}
const newPeerCount = sender.peerConnections.size;
console.log(
`[WebRTC Debug] Sender peer count after disconnect: ${newPeerCount}`
);
setSharePeerCount(newPeerCount);
}, 0);
};
@@ -204,12 +205,16 @@ export function useWebRTCConnection({
if (developmentEnv)
console.log(`Receiver connection state with ${peerId}: ${state}`);
// 更新连接状态
useFileTransferStore.getState().setRetrieveConnectionState(state as any);
useFileTransferStore
.getState()
.setRetrieveConnectionState(state as any);
setRetrievePeerCount(receiver.peerConnections.size);
if (state === "connected") {
receiverFileTransfer.setProgressCallback(
(fileId, progress: number, speed: number) => {
useFileTransferStore.getState().updateReceiveProgress(fileId, peerId, { progress, speed });
useFileTransferStore
.getState()
.updateReceiveProgress(fileId, peerId, { progress, speed });
}
);
} else if (state === "failed" || state === "disconnected") {
@@ -224,7 +229,7 @@ export function useWebRTCConnection({
if (developmentEnv) console.log(`String received from peer ${peerId}`);
useFileTransferStore.getState().setRetrievedContent(data);
};
receiverFileTransfer.onFileMetaReceived = (meta) => {
const peerId = "testId";
if (developmentEnv)
@@ -239,7 +244,7 @@ export function useWebRTCConnection({
);
store.setRetrievedFileMetas([...DPrev, metaWithoutType]);
};
receiverFileTransfer.onFileReceived = async (file) => {
const peerId = "testId"; // This should be dynamic in a multi-peer scenario
if (developmentEnv)
@@ -249,18 +254,24 @@ export function useWebRTCConnection({
};
receiver.onPeerDisconnected = (peerId) => {
if (developmentEnv)
console.log(`Receiver peer ${peerId} disconnected.`);
console.log(`[WebRTC Debug] Receiver peer ${peerId} disconnected`);
setSenderDisconnected(true);
setRetrievePeerCount(0);
useFileTransferStore.getState().setIsReceiverInRoom(false);
// 注意:接收端断开连接时应该保持在房间状态,除非主动离开
console.log(
`[WebRTC Debug] Receiver peer disconnected, but staying in room`
);
};
receiver.onConnectionEstablished = (peerId) => {
if (developmentEnv)
console.log(`Receiver connection established with ${peerId}.`);
console.log(
`[WebRTC Debug] Receiver connection established with ${peerId}`
);
setSenderDisconnected(false);
useFileTransferStore.getState().setIsReceiverInRoom(true);
console.log(
`[WebRTC Debug] Receiver setIsReceiverInRoom(true) after connection established`
);
};
receiver.onError = (error) => {
@@ -275,14 +286,17 @@ export function useWebRTCConnection({
receiverFileTransfer,
putMessageInMs,
broadcastDataToAllPeers,
shareContent,
sendFiles,
isAnyFileTransferring,
setSharePeerCount,
setRetrievePeerCount,
setSenderDisconnected,
]);
// Calculate isContentPresent from store data
const isContentPresent = useMemo(() => {
return shareContent !== "" || sendFiles.length > 0;
}, [shareContent, sendFiles]);
// Effect to handle graceful shutdown on page unload
useEffect(() => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
+32 -10
View File
@@ -10,12 +10,22 @@ interface FileTransferState {
retrieveRoomStatusText: string;
// WebRTC 连接状态 - 发送方
shareConnectionState: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed';
shareConnectionState:
| "idle"
| "connecting"
| "connected"
| "disconnected"
| "failed";
isSenderInRoom: boolean;
sharePeerCount: number;
// WebRTC 连接状态 - 接收方
retrieveConnectionState: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed';
retrieveConnectionState:
| "idle"
| "connecting"
| "connected"
| "disconnected"
| "failed";
isReceiverInRoom: boolean;
retrievePeerCount: number;
senderDisconnected: boolean;
@@ -50,10 +60,14 @@ interface FileTransferState {
setRetrieveRoomStatusText: (text: string) => void;
// WebRTC 连接相关 actions
setShareConnectionState: (state: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed') => void;
setShareConnectionState: (
state: "idle" | "connecting" | "connected" | "disconnected" | "failed"
) => void;
setIsSenderInRoom: (isInRoom: boolean) => void;
setSharePeerCount: (count: number) => void;
setRetrieveConnectionState: (state: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed') => void;
setRetrieveConnectionState: (
state: "idle" | "connecting" | "connected" | "disconnected" | "failed"
) => void;
setIsReceiverInRoom: (isInRoom: boolean) => void;
setRetrievePeerCount: (count: number) => void;
setSenderDisconnected: (disconnected: boolean) => void;
@@ -71,8 +85,16 @@ interface FileTransferState {
// 传输进度相关 actions
setSendProgress: (progress: Record<string, any>) => void;
setReceiveProgress: (progress: Record<string, any>) => void;
updateSendProgress: (fileId: string, peerId: string, progress: { progress: number; speed: number }) => void;
updateReceiveProgress: (fileId: string, peerId: string, progress: { progress: number; speed: number }) => void;
updateSendProgress: (
fileId: string,
peerId: string,
progress: { progress: number; speed: number }
) => void;
updateReceiveProgress: (
fileId: string,
peerId: string,
progress: { progress: number; speed: number }
) => void;
setIsAnyFileTransferring: (transferring: boolean) => void;
// UI 状态相关 actions
@@ -97,10 +119,10 @@ export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
shareLink: "",
shareRoomStatusText: "",
retrieveRoomStatusText: "",
shareConnectionState: 'idle',
shareConnectionState: "idle",
isSenderInRoom: false,
sharePeerCount: 0,
retrieveConnectionState: 'idle',
retrieveConnectionState: "idle",
isReceiverInRoom: false,
retrievePeerCount: 0,
senderDisconnected: false,
@@ -129,7 +151,8 @@ export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
setShareConnectionState: (state) => set({ shareConnectionState: state }),
setIsSenderInRoom: (isInRoom) => set({ isSenderInRoom: isInRoom }),
setSharePeerCount: (count) => set({ sharePeerCount: count }),
setRetrieveConnectionState: (state) => set({ retrieveConnectionState: state }),
setRetrieveConnectionState: (state) =>
set({ retrieveConnectionState: state }),
setIsReceiverInRoom: (isInRoom) => set({ isReceiverInRoom: isInRoom }),
setRetrievePeerCount: (count) => set({ retrievePeerCount: count }),
setSenderDisconnected: (disconnected) =>
@@ -193,7 +216,6 @@ export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
retrievedFileMetas: [],
retrievePeerCount: 0,
senderDisconnected: false,
retrieveRoomIdInput: "",
receiveProgress: {},
retrieveRoomStatusText: "",
}),