refactor:Refactored the useRoomManager,useWebRTCConnection hook

This commit is contained in:
david_bai
2025-08-17 07:57:29 +08:00
parent e15783aeff
commit 083206bed3
4 changed files with 168 additions and 165 deletions
+33 -50
View File
@@ -1,11 +1,5 @@
"use client";
import React, {
useState,
useRef,
useCallback,
useEffect,
useMemo,
} from "react";
import React, { useRef, useCallback, useEffect, useMemo } from "react";
import { Button } from "@/components/ui/button";
import useRichTextToPlainText from "../hooks/useRichTextToPlainText";
import QRCodeComponent from "./ClipboardApp/ShareCard";
@@ -19,31 +13,47 @@ import { SendTabPanel } from "./ClipboardApp/SendTabPanel";
import { RetrieveTabPanel } from "./ClipboardApp/RetrieveTabPanel";
import FullScreenDropZone from "./ClipboardApp/FullScreenDropZone";
import { traverseFileTree } from "@/lib/fileUtils";
import { useFileTransferStore } from "@/stores/fileTransferStore";
const ClipboardApp = () => {
const { shareMessage, retrieveMessage, putMessageInMs } =
useClipboardAppMessages();
const [retrieveRoomIdInput, setRetrieveRoomIdInput] = useState("");
const [activeTab, setActiveTab] = useState<"send" | "retrieve">("send");
const [isDragging, setIsDragging] = useState(false);
const dragCounter = useRef(0);
const retrieveJoinRoomBtnRef = useRef<HTMLButtonElement>(null); // Ref for the receiver's "Join Room" button
const retrieveJoinRoomBtnRef = useRef<HTMLButtonElement>(null);
const { messages, isLoadingMessages } = usePageSetup({
setRetrieveRoomId: setRetrieveRoomIdInput,
setActiveTab,
setRetrieveRoomId: useFileTransferStore.getState().setRetrieveRoomIdInput,
setActiveTab: useFileTransferStore.getState().setActiveTab,
retrieveJoinRoomBtnRef,
});
// 从 store 中获取状态
const {
activeTab,
retrieveRoomIdInput,
isDragging,
shareContent,
sendFiles,
retrievedContent,
retrievedFileMetas,
sendProgress,
receiveProgress,
isAnyFileTransferring,
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
setIsDragging,
setRetrieveRoomIdInput,
setActiveTab,
} = useFileTransferStore();
const richTextToPlainText = useRichTextToPlainText();
// Initialize File Transfer Handler Hook
const {
shareContent,
sendFiles,
retrievedContent,
retrievedFileMetas,
updateShareContent,
addFilesToSend,
removeFileToSend,
@@ -51,23 +61,21 @@ const ClipboardApp = () => {
onFileMetadataReceived,
onFileFullyReceived,
handleDownloadFile,
resetReceiverState,
} = 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,
receiver,
sharePeerCount,
retrievePeerCount,
sendProgress,
receiveProgress,
isAnyFileTransferring,
broadcastDataToAllPeers,
requestFile,
requestFolder,
@@ -88,20 +96,10 @@ const ClipboardApp = () => {
onFileReceived: onFileFullyReceived,
});
// Function to reset sender peer count
const resetSenderPeerCount = useCallback(() => {
// This will be used by useRoomManager to reset sharePeerCount
// We can access the sharePeerCount setter through a custom approach
// For now, we'll implement this by triggering a re-render
// The actual reset happens in useWebRTCConnection via sender.leaveRoomAndCleanup()
// But we need to ensure the UI updates, so we'll add this logic there
}, []);
const resetAppState = useCallback(async () => {
// Graceful state reset instead of page reload
try {
// Reset file transfer state
resetReceiverState();
useFileTransferStore.getState().resetReceiverState();
// Reset WebRTC connection state
await resetReceiverConnection();
@@ -112,18 +110,12 @@ const ClipboardApp = () => {
console.log("Application state reset successfully");
} catch (error) {
console.error("Error during state reset:", error);
// Fallback to page reload if graceful reset fails
window.location.reload();
}
}, [resetReceiverState, resetReceiverConnection]);
}, [resetReceiverConnection, setRetrieveRoomIdInput]);
// Initialize Room Manager Hook
const {
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
processRoomIdInput,
joinRoom,
generateShareLinkAndBroadcast,
@@ -134,13 +126,8 @@ const ClipboardApp = () => {
putMessageInMs,
sender,
receiver,
activeTab,
sharePeerCount,
retrievePeerCount,
senderDisconnected,
broadcastDataToPeers: () =>
broadcastDataToAllPeers(shareContent, sendFiles),
resetApp: resetAppState,
resetSenderConnection,
});
@@ -214,12 +201,9 @@ const ClipboardApp = () => {
window.removeEventListener("dragover", handleDragOver);
window.removeEventListener("drop", handleDrop);
};
}, [activeTab, handleFileDrop]);
}, [activeTab, handleFileDrop, setIsDragging]);
if (isLoadingMessages || !messages) {
// Use a skeleton screen placeholder to replace the simple text loading prompt.
// The height of this placeholder is similar to the height of the component that is finally loaded,
// This prevents layout displacement and ensures that the lazy loading component below will not be triggered prematurely.
return (
<div className="container mx-auto px-4 py-8 w-full md:max-w-4xl">
<div className="min-h-[1000px] w-full bg-gray-200/50 dark:bg-gray-800/50 rounded-lg animate-pulse">
@@ -287,7 +271,7 @@ const ClipboardApp = () => {
) : (
<RetrieveTabPanel
messages={messages}
putMessageInMs={putMessageInMs} // Needed for onLocationPick
putMessageInMs={putMessageInMs}
retrieveRoomStatusText={retrieveRoomStatusText}
retrieveRoomIdInput={retrieveRoomIdInput}
setRetrieveRoomIdInput={setRetrieveRoomIdInput}
@@ -300,7 +284,6 @@ const ClipboardApp = () => {
receiveProgress={receiveProgress}
isAnyFileTransferring={isAnyFileTransferring}
handleDownloadFile={handleDownloadFile}
// Pass WebRTC interaction methods
requestFile={requestFile}
requestFolder={requestFolder}
setReceiverDirectoryHandle={setReceiverDirectoryHandle}
+80 -61
View File
@@ -1,9 +1,10 @@
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { useEffect, useCallback, useMemo } from "react";
import { fetchRoom, createRoom, checkRoom, leaveRoom } from "@/app/config/api";
import { debounce } from "lodash";
import type { Messages } from "@/types/messages";
import type WebRTC_Initiator from "@/lib/webrtc_Initiator";
import type WebRTC_Recipient from "@/lib/webrtc_Recipient";
import { useFileTransferStore } from "@/stores/fileTransferStore";
function format_peopleMsg(template: string, peerCount: number) {
return template.replace("{peerCount}", peerCount.toString());
@@ -18,13 +19,8 @@ interface UseRoomManagerProps {
) => void;
sender: WebRTC_Initiator | null;
receiver: WebRTC_Recipient | null;
activeTab: "send" | "retrieve";
sharePeerCount: number;
retrievePeerCount: number;
senderDisconnected: boolean;
broadcastDataToPeers: () => Promise<boolean>;
resetApp: () => void; // Add a reset function prop
resetSenderConnection: () => Promise<void>; // Add function to reset sender connection
resetSenderConnection: () => Promise<void>;
}
export function useRoomManager({
@@ -32,21 +28,32 @@ export function useRoomManager({
putMessageInMs,
sender,
receiver,
activeTab,
sharePeerCount,
retrievePeerCount,
senderDisconnected,
broadcastDataToPeers,
resetApp,
resetSenderConnection,
}: UseRoomManagerProps) {
const [shareRoomId, setShareRoomId] = useState(""); // Represents the validated or initially fetched room ID
const [initShareRoomId, setInitShareRoomId] = useState(""); // Stores the initially fetched room ID for comparison
const [shareLink, setShareLink] = useState("");
const [shareRoomStatusText, setShareRoomStatusText] = useState("");
const [retrieveRoomStatusText, setRetrieveRoomStatusText] = useState("");
// 从 store 中获取状态
const {
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
activeTab,
sharePeerCount,
retrievePeerCount,
senderDisconnected,
setShareRoomId,
setInitShareRoomId,
setShareLink,
setShareRoomStatusText,
setRetrieveRoomStatusText,
setSharePeerCount,
setRetrievePeerCount,
resetReceiverState,
resetSenderApp,
} = useFileTransferStore();
// Receiver leave room function (renamed and simplified)
// Receiver leave room function
const handleLeaveReceiverRoom = useCallback(async () => {
if (!receiver || !receiver.roomId || !receiver.peerId || !messages) return;
try {
@@ -57,18 +64,26 @@ export function useRoomManager({
putMessageInMs("Failed to leave the room.", false);
} finally {
// Reset application state
resetApp();
resetReceiverState();
// Reset peer count
setRetrievePeerCount(0);
}
}, [receiver, putMessageInMs, resetApp, messages]);
}, [
receiver,
putMessageInMs,
messages,
resetReceiverState,
setRetrievePeerCount,
]);
// Reset sender app state (preserve send content, get new room ID)
const resetSenderApp = useCallback(async () => {
// Reset sender app state
const resetSenderAppState = useCallback(async () => {
try {
// 1. Clean up WebRTC connections and reset peer count
await resetSenderConnection();
// 2. Clear share link
setShareLink("");
// 2. Clear share link and progress
resetSenderApp();
// 3. Get new room ID from backend
const newRoomId = await fetchRoom();
@@ -83,9 +98,15 @@ export function useRoomManager({
console.error("Error during sender state reset:", error);
putMessageInMs("Error resetting sender state.", true);
}
}, [resetSenderConnection, putMessageInMs]);
}, [
resetSenderConnection,
putMessageInMs,
resetSenderApp,
setShareRoomId,
setInitShareRoomId,
]);
// Sender leave room function (new)
// Sender leave room function
const handleLeaveSenderRoom = useCallback(async () => {
if (!sender || !sender.roomId || !sender.peerId || !messages) return;
try {
@@ -96,9 +117,9 @@ export function useRoomManager({
putMessageInMs("Failed to leave the room.", true);
} finally {
// Reset sender state and get new room ID
await resetSenderApp();
await resetSenderAppState();
}
}, [sender, putMessageInMs, resetSenderApp, messages]);
}, [sender, putMessageInMs, resetSenderAppState, messages]);
// Initialize shareRoomId on mount
useEffect(() => {
@@ -124,7 +145,14 @@ export function useRoomManager({
};
initNewRoom();
}
}, [messages, initShareRoomId]);
}, [
messages,
initShareRoomId,
activeTab,
setShareRoomId,
setInitShareRoomId,
putMessageInMs,
]);
// Debounced function to actually check the room ID and update the state
const performDebouncedRoomCheck = useMemo(
@@ -133,24 +161,18 @@ export function useRoomManager({
if (!messages || !putMessageInMs) return;
if (!roomIdToCheck.trim()) {
// If the input is cleared, don't perform a check, but you can clear the message or handle it otherwise
// putMessageInMs(messages.text.ClipboardApp.roomCheck.empty_msg, true);
// Consider resetting shareRoomId to initShareRoomId if you want to restore the default when input is cleared
// setShareRoomId(initShareRoomId);
return;
}
try {
const available = await checkRoom(roomIdToCheck);
if (available) {
setShareRoomId(roomIdToCheck); // Update the validated shareRoomId
setShareRoomId(roomIdToCheck);
putMessageInMs(
messages.text.ClipboardApp.roomCheck.available_msg,
true
);
} else {
// Room is not available, do not update shareRoomId; it will retain the last valid or initial value
// The value in the user's input box is managed by SendTabPanel's local state and will not roll back because of this
putMessageInMs(
messages.text.ClipboardApp.roomCheck.notAvailable_msg,
true
@@ -158,13 +180,9 @@ export function useRoomManager({
}
} catch (error) {
console.error("Error checking room availability:", error);
putMessageInMs(
//messages.text.ClipboardApp.roomCheck.error_msg ||
"Error checking room.",
true
);
putMessageInMs("Error checking room.", true);
}
}, 750), // Increased debounce delay to 750ms
}, 750),
[messages, putMessageInMs, setShareRoomId]
);
@@ -172,12 +190,9 @@ export function useRoomManager({
const processRoomIdInput = useCallback(
(inputRoomId: string) => {
if (!inputRoomId.trim() && messages && putMessageInMs) {
// If the user clears the input box
putMessageInMs(messages.text.ClipboardApp.roomCheck.empty_msg, true);
performDebouncedRoomCheck.cancel();
// And if you want the validated shareRoomId to revert to the initial state upon clearing:
// setShareRoomId(initShareRoomId); // This would update the QR code, etc., to the initial ID
return; // Don't proceed with debounced check for an empty string
return;
}
performDebouncedRoomCheck(inputRoomId);
},
@@ -197,7 +212,7 @@ export function useRoomManager({
}
const peer = isSenderSide ? sender : receiver;
if (!peer) return; // Should be caught by above, but for type safety
if (!peer) return;
if (!currentRoomIdToJoin.trim()) {
putMessageInMs(
@@ -219,7 +234,6 @@ export function useRoomManager({
);
return;
}
// If creation is successful, WebRTC's joinRoom will use this ID, and we should update shareRoomId to this newly created ID
setShareRoomId(currentRoomIdToJoin);
} catch (error) {
putMessageInMs(
@@ -233,9 +247,6 @@ export function useRoomManager({
}
try {
// WebRTC joinRoom uses the ID provided by the user (for the receiver) or the validated/newly created ID (for the sender)
// For the sender, if createRoom above was successful and set shareRoomId, peer.joinRoom should use it
// But if currentRoomIdToJoin is initShareRoomId, use it directly
const actualRoomIdForSenderJoin =
isSenderSide && currentRoomIdToJoin !== initShareRoomId
? currentRoomIdToJoin
@@ -254,7 +265,6 @@ export function useRoomManager({
const link = `${window.location.origin}${window.location.pathname}?roomId=${actualRoomIdForSenderJoin}`;
setShareLink(link);
if (actualRoomIdForSenderJoin !== shareRoomId) {
// If joining was successful by entering a new ID, update shareRoomId
setShareRoomId(actualRoomIdForSenderJoin);
}
}
@@ -283,7 +293,7 @@ export function useRoomManager({
);
const generateShareLinkAndBroadcast = useCallback(async () => {
if (!sender || !messages || !putMessageInMs || !shareRoomId) return; // Ensure shareRoomId is valid
if (!sender || !messages || !putMessageInMs || !shareRoomId) return;
if (sender.peerConnections.size === 0) {
putMessageInMs(messages.text.ClipboardApp.waitting_tips, true);
@@ -292,7 +302,14 @@ export function useRoomManager({
}
const link = `${window.location.origin}${window.location.pathname}?roomId=${shareRoomId}`;
setShareLink(link);
}, [sender, messages, putMessageInMs, shareRoomId]);
}, [
sender,
messages,
putMessageInMs,
shareRoomId,
setShareLink,
broadcastDataToPeers,
]);
// useEffect for room status text
useEffect(() => {
@@ -339,20 +356,22 @@ export function useRoomManager({
receiver,
messages,
senderDisconnected,
sender?.isInRoom, // Add isInRoom state to dependencies
receiver?.isInRoom, // Add isInRoom state to dependencies
sender?.isInRoom,
receiver?.isInRoom,
setShareRoomStatusText,
setRetrieveRoomStatusText,
]);
return {
shareRoomId, // This is the validated or initial room ID
initShareRoomId, // Exposed for UI comparison or reset logic
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
processRoomIdInput, // New input processing function
processRoomIdInput,
joinRoom,
generateShareLinkAndBroadcast,
handleLeaveReceiverRoom, // Renamed function
handleLeaveSenderRoom, // New function
handleLeaveReceiverRoom,
handleLeaveSenderRoom,
};
}
+53 -52
View File
@@ -8,8 +8,9 @@ import {
getIceServers,
getSocketOptions,
} from "@/app/config/environment";
import type { CustomFile, fileMetadata, FileMeta } from "@/types/webrtc"; // Assuming FileMeta might be used by caller
import type { CustomFile, fileMetadata, FileMeta } from "@/types/webrtc";
import type { Messages } from "@/types/messages";
import { useFileTransferStore } from "@/stores/fileTransferStore";
const developmentEnv = process.env.NEXT_PUBLIC_development === "true";
@@ -21,7 +22,7 @@ export type ProgressState = { [fileId: string]: FileProgressPeers };
interface UseWebRTCConnectionProps {
shareContent: string;
sendFiles: CustomFile[];
isContentPresent: boolean; // To know if there is any content (text or files)
isContentPresent: boolean;
// Callbacks for data received from peers
onStringReceived: (data: string, peerId: string) => void;
onFileMetaReceived: (meta: fileMetadata, peerId: string) => void;
@@ -53,12 +54,20 @@ export function useWebRTCConnection({
const [receiverFileTransfer, setReceiverFileTransfer] =
useState<FileReceiver | null>(null);
const [sharePeerCount, setSharePeerCount] = useState(0);
const [retrievePeerCount, setRetrievePeerCount] = useState(0);
const [sendProgress, setSendProgress] = useState<ProgressState>({});
const [receiveProgress, setReceiveProgress] = useState<ProgressState>({});
const [senderDisconnected, setSenderDisconnected] = useState(false);
// 从 store 中获取状态
const {
sharePeerCount,
retrievePeerCount,
sendProgress,
receiveProgress,
senderDisconnected,
setSharePeerCount,
setRetrievePeerCount,
setSendProgress,
setReceiveProgress,
setSenderDisconnected,
setIsAnyFileTransferring,
} = useFileTransferStore();
// Calculate isAnyFileTransferring internally based on progress states
const isAnyFileTransferring = useMemo(() => {
@@ -66,13 +75,22 @@ export function useWebRTCConnection({
...Object.values(sendProgress),
...Object.values(receiveProgress),
];
return allProgress.some((fileProgress) =>
Object.values(fileProgress).some(
(progress) => progress.progress > 0 && progress.progress < 1
)
);
return allProgress.some((fileProgress: unknown) => {
const typedFileProgress = fileProgress as FileProgressPeers;
return Object.values(typedFileProgress).some(
(progress: unknown) => {
const typedProgress = progress as PeerProgressDetails;
return typedProgress.progress > 0 && typedProgress.progress < 1;
}
);
});
}, [sendProgress, receiveProgress]);
// 更新 store 中的 isAnyFileTransferring 状态
useEffect(() => {
setIsAnyFileTransferring(isAnyFileTransferring);
}, [isAnyFileTransferring, setIsAnyFileTransferring]);
// Initialize WebRTC objects and their cleanup
useEffect(() => {
const webRTCConfig = {
@@ -106,7 +124,6 @@ export function useWebRTCConnection({
console.error(
"SenderFileTransfer not initialized for sendStringAndMetasToPeer"
);
// TODO: Use putMessageInMs for critical errors visible to user?
return;
}
if (textContent) {
@@ -123,12 +140,11 @@ export function useWebRTCConnection({
const broadcastDataToAllPeers = useCallback(
async (textContent: string, filesToSend: CustomFile[]) => {
if (!sender || sender.peerConnections.size === 0) {
// The caller (useRoomManager) will handle user message like "waiting for peers"
if (developmentEnv)
console.warn(
"No sender peers to broadcast to, or sender not initialized."
);
return false; // Indicate failure or no action
return false;
}
if (!senderFileTransfer) {
console.error("senderFileTransfer is not initialized for broadcast.");
@@ -143,11 +159,10 @@ export function useWebRTCConnection({
sendStringAndMetasToPeer(peerId, textContent, filesToSend)
)
);
return true; // Indicate success
return true;
} catch (error) {
console.error("Error broadcasting data to peers:", error);
// Optionally use putMessageInMs here for a generic broadcast error
return false; // Indicate failure
return false;
}
},
[sender, senderFileTransfer, sendStringAndMetasToPeer]
@@ -161,22 +176,18 @@ export function useWebRTCConnection({
console.log(`Sender connection state with ${peerId}: ${state}`);
setSharePeerCount(sender.peerConnections.size);
if (state === "connected") {
senderFileTransfer.setProgressCallback((fileId, progress, speed) => {
setSendProgress((prev) => ({
senderFileTransfer.setProgressCallback((fileId, progress: number, speed: number) => {
setSendProgress((prev: ProgressState) => ({
...prev,
[fileId]: { ...prev[fileId], [peerId]: { progress, speed } },
}));
}, peerId);
// putMessageInMs(`Connected to a new peer (sender side). Total: ${sender.peerConnections.size}`, true);
}
// Add more detailed user messages based on state if needed via putMessageInMs
};
sender.onDataChannelOpen = () =>
broadcastDataToAllPeers(shareContent, sendFiles);
sender.onPeerDisconnected = (peerId) => {
// Use setTimeout to ensure cleanupExistingConnection has been called
// before we update the peer count
setTimeout(() => {
setSharePeerCount(sender.peerConnections.size);
}, 0);
@@ -184,7 +195,6 @@ export function useWebRTCConnection({
sender.onError = (error) => {
console.error("Sender Error:", error.message, error.context);
// Optionally, use putMessageInMs to show a user-friendly error
putMessageInMs(`Connection error: ${error.message}`, true);
};
}
@@ -196,14 +206,13 @@ export function useWebRTCConnection({
setRetrievePeerCount(receiver.peerConnections.size);
if (state === "connected") {
receiverFileTransfer.setProgressCallback(
(fileId, progress, speed) => {
setReceiveProgress((prev) => ({
(fileId, progress: number, speed: number) => {
setReceiveProgress((prev: ProgressState) => ({
...prev,
[fileId]: { ...prev[fileId], [peerId]: { progress, speed } },
}));
}
);
// Example: putMessageInMs(`Connected to a new peer (receiver side). Total: ${receiver.peerConnections.size}`, false);
} else if (state === "failed" || state === "disconnected") {
if (isAnyFileTransferring) {
receiverFileTransfer.gracefulShutdown();
@@ -234,18 +243,13 @@ export function useWebRTCConnection({
receiver.onPeerDisconnected = (peerId) => {
if (developmentEnv)
console.log(`Receiver peer ${peerId} disconnected.`);
// On the receiver side, any peer is a sender.
setSenderDisconnected(true);
// Set peer count to 0 since the peer has disconnected
// Note: receiver.peerConnections.size might still be > 0 at this point
// because cleanupExistingConnection hasn't been called yet
setRetrievePeerCount(0);
};
receiver.onConnectionEstablished = (peerId) => {
if (developmentEnv)
console.log(`Receiver connection established with ${peerId}.`);
// If a connection is re-established, assume sender is back.
setSenderDisconnected(false);
};
@@ -267,6 +271,11 @@ export function useWebRTCConnection({
shareContent,
sendFiles,
isAnyFileTransferring,
setSharePeerCount,
setRetrievePeerCount,
setSendProgress,
setReceiveProgress,
setSenderDisconnected,
]);
// Effect to handle graceful shutdown on page unload
@@ -276,7 +285,6 @@ export function useWebRTCConnection({
if (isAnyFileTransferring) {
receiverFileTransfer?.gracefulShutdown();
}
// Show the browser's confirmation dialog
e.preventDefault();
e.returnValue = "";
}
@@ -291,7 +299,6 @@ export function useWebRTCConnection({
const requestFile = useCallback(
(fileId: string, peerId?: string) => {
// Assuming FileReceiver methods can take optional peerId
if (!receiverFileTransfer) return;
if (developmentEnv)
console.log(
@@ -333,29 +340,24 @@ export function useWebRTCConnection({
// Reset function for receiver connection (for leave room functionality)
const resetReceiverConnection = useCallback(async () => {
if (receiver) {
// First reset all UI states to ensure consistent state
setSenderDisconnected(false);
setRetrievePeerCount(0);
// Then cleanup the WebRTC connection
await receiver.leaveRoomAndCleanup();
}
}, [receiver]);
}, [receiver, setSenderDisconnected, setRetrievePeerCount]);
// Reset function for sender connection (for leave room functionality)
const resetSenderConnection = useCallback(async () => {
if (sender) {
// First cleanup the WebRTC connection (this sets isInRoom = false)
await sender.leaveRoomAndCleanup();
// Then reset UI state to ensure consistent state
setSharePeerCount(0);
}
}, [sender]);
}, [sender, setSharePeerCount]);
// Manual safe save function (replaces the beforeunload graceful shutdown)
// Manual safe save function
const manualSafeSave = useCallback(() => {
if (receiverFileTransfer) {
receiverFileTransfer.gracefulShutdown();
// Provide user feedback
if (putMessageInMs && messages) {
putMessageInMs(
messages.text.FileListDisplay.safeSaveSuccessMsg,
@@ -367,22 +369,21 @@ export function useWebRTCConnection({
}, [receiverFileTransfer, putMessageInMs, messages]);
return {
sender, // Exposed for useRoomManager (e.g., sender.isInRoom, sender.joinRoom)
receiver, // Exposed for useRoomManager
// Not exposing senderFileTransfer/receiverFileTransfer directly to encourage using specific methods
sender,
receiver,
sharePeerCount,
retrievePeerCount,
sendProgress,
receiveProgress,
isAnyFileTransferring, // Export the calculated state
isAnyFileTransferring,
broadcastDataToAllPeers,
requestFile,
requestFolder,
setReceiverDirectoryHandle,
getReceiverSaveType,
senderDisconnected,
resetReceiverConnection, // Export the new reset function
resetSenderConnection, // Export the new sender reset function
manualSafeSave, // Export the manual safe save function
resetReceiverConnection,
resetSenderConnection,
manualSafeSave,
};
}
}
+2 -2
View File
@@ -27,7 +27,7 @@ interface FileTransferState {
isAnyFileTransferring: boolean;
// UI 状态
activeTab: 'send' | 'receive';
activeTab: 'send' | 'retrieve';
retrieveRoomIdInput: string;
isDragging: boolean;
@@ -63,7 +63,7 @@ interface FileTransferState {
setIsAnyFileTransferring: (transferring: boolean) => void;
// UI 状态相关 actions
setActiveTab: (tab: 'send' | 'receive') => void;
setActiveTab: (tab: 'send' | 'retrieve') => void;
setRetrieveRoomIdInput: (input: string) => void;
setIsDragging: (dragging: boolean) => void;