fix:The recipient needs to clean the connection when leaving the room
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}, [
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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: "",
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user