fix:Now there is a progress bar for file transfer

This commit is contained in:
david_bai
2025-08-17 10:10:40 +08:00
parent 083206bed3
commit 75f9ff39ae
4 changed files with 182 additions and 133 deletions
-6
View File
@@ -57,9 +57,6 @@ const ClipboardApp = () => {
updateShareContent,
addFilesToSend,
removeFileToSend,
onStringDataReceived,
onFileMetadataReceived,
onFileFullyReceived,
handleDownloadFile,
} = useFileTransferHandler({ messages, putMessageInMs });
@@ -91,9 +88,6 @@ const ClipboardApp = () => {
isContentPresent,
messages,
putMessageInMs,
onStringReceived: onStringDataReceived,
onFileMetaReceived: onFileMetadataReceived,
onFileReceived: onFileFullyReceived,
});
const resetAppState = useCallback(async () => {
+28 -29
View File
@@ -42,6 +42,8 @@ export function useRoomManager({
sharePeerCount,
retrievePeerCount,
senderDisconnected,
isSenderInRoom,
isReceiverInRoom,
setShareRoomId,
setInitShareRoomId,
setShareLink,
@@ -261,12 +263,16 @@ export function useRoomManager({
6000
);
// 更新 Store 中的房间状态
if (isSenderSide) {
useFileTransferStore.getState().setIsSenderInRoom(true);
const link = `${window.location.origin}${window.location.pathname}?roomId=${actualRoomIdForSenderJoin}`;
setShareLink(link);
if (actualRoomIdForSenderJoin !== shareRoomId) {
setShareRoomId(actualRoomIdForSenderJoin);
}
} else {
useFileTransferStore.getState().setIsReceiverInRoom(true);
}
} catch (error) {
let errorMsgToShow = messages.text.ClipboardApp.joinRoom.failMsg;
@@ -313,51 +319,44 @@ export function useRoomManager({
// useEffect for room status text
useEffect(() => {
if (
!messages ||
(activeTab === "send" && !sender) ||
(activeTab === "retrieve" && !receiver)
) {
if (!messages) {
if (activeTab === "send") setShareRoomStatusText("");
else setRetrieveRoomStatusText("");
return;
}
const currentPeer = activeTab === "send" ? sender : receiver;
const isInRoom = activeTab === "send" ? isSenderInRoom : isReceiverInRoom;
const currentPeerCount =
activeTab === "send" ? sharePeerCount : retrievePeerCount;
let statusText = "";
if (currentPeer) {
if (!currentPeer.isInRoom) {
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;
}
if (activeTab === "send") setShareRoomStatusText(statusText);
else setRetrieveRoomStatusText(statusText);
if (!isInRoom) {
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;
}
if (activeTab === "send") setShareRoomStatusText(statusText);
else setRetrieveRoomStatusText(statusText);
}, [
activeTab,
sharePeerCount,
retrievePeerCount,
sender,
receiver,
messages,
senderDisconnected,
sender?.isInRoom,
receiver?.isInRoom,
isSenderInRoom,
isReceiverInRoom,
setShareRoomStatusText,
setRetrieveRoomStatusText,
]);
+41 -34
View File
@@ -23,10 +23,6 @@ interface UseWebRTCConnectionProps {
shareContent: string;
sendFiles: CustomFile[];
isContentPresent: boolean;
// Callbacks for data received from peers
onStringReceived: (data: string, peerId: string) => void;
onFileMetaReceived: (meta: fileMetadata, peerId: string) => void;
onFileReceived: (file: CustomFile, peerId: string) => void;
// For user feedback and messages from the hook, if any (mostly for console for now)
messages: Messages | null;
@@ -41,9 +37,6 @@ export function useWebRTCConnection({
shareContent,
sendFiles,
isContentPresent,
onStringReceived,
onFileMetaReceived,
onFileReceived,
messages,
putMessageInMs,
}: UseWebRTCConnectionProps) {
@@ -77,12 +70,10 @@ export function useWebRTCConnection({
];
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;
}
);
return Object.values(typedFileProgress).some((progress: unknown) => {
const typedProgress = progress as PeerProgressDetails;
return typedProgress.progress > 0 && typedProgress.progress < 1;
});
});
}, [sendProgress, receiveProgress]);
@@ -174,22 +165,31 @@ export function useWebRTCConnection({
sender.onConnectionStateChange = (state, peerId) => {
if (developmentEnv)
console.log(`Sender connection state with ${peerId}: ${state}`);
// 更新连接状态
useFileTransferStore.getState().setShareConnectionState(state as any);
setSharePeerCount(sender.peerConnections.size);
if (state === "connected") {
senderFileTransfer.setProgressCallback((fileId, progress: number, speed: number) => {
setSendProgress((prev: ProgressState) => ({
...prev,
[fileId]: { ...prev[fileId], [peerId]: { progress, speed } },
}));
}, peerId);
senderFileTransfer.setProgressCallback(
(fileId, progress: number, speed: number) => {
useFileTransferStore.getState().updateSendProgress(fileId, peerId, { progress, speed });
},
peerId
);
}
};
sender.onDataChannelOpen = () =>
sender.onDataChannelOpen = () => {
// 当数据通道打开时,标记发送方已加入房间
useFileTransferStore.getState().setIsSenderInRoom(true);
broadcastDataToAllPeers(shareContent, sendFiles);
}
sender.onPeerDisconnected = (peerId) => {
setTimeout(() => {
setSharePeerCount(sender.peerConnections.size);
// 检查是否所有 peer 都已断开连接
if (sender.peerConnections.size === 0) {
useFileTransferStore.getState().setIsSenderInRoom(false);
}
}, 0);
};
@@ -203,14 +203,13 @@ export function useWebRTCConnection({
receiver.onConnectionStateChange = (state, peerId) => {
if (developmentEnv)
console.log(`Receiver connection state with ${peerId}: ${state}`);
// 更新连接状态
useFileTransferStore.getState().setRetrieveConnectionState(state as any);
setRetrievePeerCount(receiver.peerConnections.size);
if (state === "connected") {
receiverFileTransfer.setProgressCallback(
(fileId, progress: number, speed: number) => {
setReceiveProgress((prev: ProgressState) => ({
...prev,
[fileId]: { ...prev[fileId], [peerId]: { progress, speed } },
}));
useFileTransferStore.getState().updateReceiveProgress(fileId, peerId, { progress, speed });
}
);
} else if (state === "failed" || state === "disconnected") {
@@ -223,21 +222,30 @@ export function useWebRTCConnection({
receiverFileTransfer.onStringReceived = (data) => {
const peerId = "testId";
if (developmentEnv) console.log(`String received from peer ${peerId}`);
onStringReceived(data, peerId || "unknown_peer");
useFileTransferStore.getState().setRetrievedContent(data);
};
receiverFileTransfer.onFileMetaReceived = (meta) => {
const peerId = "testId";
if (developmentEnv)
console.log(
`File meta received from peer ${peerId} for: ${meta.name}`
);
onFileMetaReceived(meta, peerId || "unknown_peer");
const { type, ...metaWithoutType } = meta;
const store = useFileTransferStore.getState();
// Filter out existing file with same ID and add the new one
const DPrev = store.retrievedFileMetas.filter(
(existingFile) => existingFile.fileId !== metaWithoutType.fileId
);
store.setRetrievedFileMetas([...DPrev, metaWithoutType]);
};
receiverFileTransfer.onFileReceived = async (file) => {
const peerId = "testId";
const peerId = "testId"; // This should be dynamic in a multi-peer scenario
if (developmentEnv)
console.log(`File received from peer ${peerId}: ${file.name}`);
onFileReceived(file, peerId || "unknown_peer");
// Directly call the store action
useFileTransferStore.getState().addRetrievedFile(file);
};
receiver.onPeerDisconnected = (peerId) => {
@@ -245,12 +253,14 @@ export function useWebRTCConnection({
console.log(`Receiver peer ${peerId} disconnected.`);
setSenderDisconnected(true);
setRetrievePeerCount(0);
useFileTransferStore.getState().setIsReceiverInRoom(false);
};
receiver.onConnectionEstablished = (peerId) => {
if (developmentEnv)
console.log(`Receiver connection established with ${peerId}.`);
setSenderDisconnected(false);
useFileTransferStore.getState().setIsReceiverInRoom(true);
};
receiver.onError = (error) => {
@@ -263,9 +273,6 @@ export function useWebRTCConnection({
senderFileTransfer,
receiver,
receiverFileTransfer,
onStringReceived,
onFileMetaReceived,
onFileReceived,
putMessageInMs,
broadcastDataToAllPeers,
shareContent,
@@ -273,8 +280,6 @@ export function useWebRTCConnection({
isAnyFileTransferring,
setSharePeerCount,
setRetrievePeerCount,
setSendProgress,
setReceiveProgress,
setSenderDisconnected,
]);
@@ -342,6 +347,7 @@ export function useWebRTCConnection({
if (receiver) {
setSenderDisconnected(false);
setRetrievePeerCount(0);
useFileTransferStore.getState().setIsReceiverInRoom(false);
await receiver.leaveRoomAndCleanup();
}
}, [receiver, setSenderDisconnected, setRetrievePeerCount]);
@@ -351,6 +357,7 @@ export function useWebRTCConnection({
if (sender) {
await sender.leaveRoomAndCleanup();
setSharePeerCount(0);
useFileTransferStore.getState().setIsSenderInRoom(false);
}
}, [sender, setSharePeerCount]);
@@ -386,4 +393,4 @@ export function useWebRTCConnection({
resetSenderConnection,
manualSafeSave,
};
}
}
+113 -64
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import { CustomFile, FileMeta } from '@/types/webrtc';
import { create } from "zustand";
import { CustomFile, FileMeta } from "@/types/webrtc";
interface FileTransferState {
// 房间相关状态
@@ -8,33 +8,39 @@ interface FileTransferState {
shareLink: string;
shareRoomStatusText: string;
retrieveRoomStatusText: string;
// WebRTC 连接状态
// WebRTC 连接状态 - 发送方
shareConnectionState: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed';
isSenderInRoom: boolean;
sharePeerCount: number;
// WebRTC 连接状态 - 接收方
retrieveConnectionState: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed';
isReceiverInRoom: boolean;
retrievePeerCount: number;
senderDisconnected: boolean;
// 文件传输状态
shareContent: string;
sendFiles: CustomFile[];
retrievedContent: string;
retrievedFiles: CustomFile[];
retrievedFileMetas: FileMeta[];
// 传输进度状态
sendProgress: Record<string, any>;
receiveProgress: Record<string, any>;
isAnyFileTransferring: boolean;
// UI 状态
activeTab: 'send' | 'retrieve';
activeTab: "send" | "retrieve";
retrieveRoomIdInput: string;
isDragging: boolean;
// 消息状态
shareMessage: string;
retrieveMessage: string;
// Actions
// 房间相关 actions
setShareRoomId: (id: string) => void;
@@ -42,12 +48,16 @@ interface FileTransferState {
setShareLink: (link: string) => void;
setShareRoomStatusText: (text: string) => void;
setRetrieveRoomStatusText: (text: string) => void;
// WebRTC 连接相关 actions
setShareConnectionState: (state: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed') => void;
setIsSenderInRoom: (isInRoom: boolean) => void;
setSharePeerCount: (count: number) => void;
setRetrieveConnectionState: (state: 'idle' | 'connecting' | 'connected' | 'disconnected' | 'failed') => void;
setIsReceiverInRoom: (isInRoom: boolean) => void;
setRetrievePeerCount: (count: number) => void;
setSenderDisconnected: (disconnected: boolean) => void;
// 文件传输相关 actions
setShareContent: (content: string) => void;
setSendFiles: (files: CustomFile[]) => void;
@@ -56,22 +66,25 @@ interface FileTransferState {
setRetrievedContent: (content: string) => void;
setRetrievedFiles: (files: CustomFile[]) => void;
setRetrievedFileMetas: (metas: FileMeta[]) => void;
addRetrievedFile: (file: CustomFile) => void;
// 传输进度相关 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;
setIsAnyFileTransferring: (transferring: boolean) => void;
// UI 状态相关 actions
setActiveTab: (tab: 'send' | 'retrieve') => void;
setActiveTab: (tab: "send" | "retrieve") => void;
setRetrieveRoomIdInput: (input: string) => void;
setIsDragging: (dragging: boolean) => void;
// 消息相关 actions
setShareMessage: (message: string) => void;
setRetrieveMessage: (message: string) => void;
setRetrieveRoomId: (input: string) => void;
// 重置相关 actions
resetReceiverState: () => void;
resetSenderApp: () => void;
@@ -79,81 +92,117 @@ interface FileTransferState {
export const useFileTransferStore = create<FileTransferState>()((set, get) => ({
// 初始状态
shareRoomId: '',
initShareRoomId: '',
shareLink: '',
shareRoomStatusText: '',
retrieveRoomStatusText: '',
shareRoomId: "",
initShareRoomId: "",
shareLink: "",
shareRoomStatusText: "",
retrieveRoomStatusText: "",
shareConnectionState: 'idle',
isSenderInRoom: false,
sharePeerCount: 0,
retrieveConnectionState: 'idle',
isReceiverInRoom: false,
retrievePeerCount: 0,
senderDisconnected: false,
shareContent: '',
shareContent: "",
sendFiles: [],
retrievedContent: '',
retrievedContent: "",
retrievedFiles: [],
retrievedFileMetas: [],
sendProgress: {},
receiveProgress: {},
isAnyFileTransferring: false,
activeTab: 'send',
retrieveRoomIdInput: '',
activeTab: "send",
retrieveRoomIdInput: "",
isDragging: false,
shareMessage: '',
retrieveMessage: '',
shareMessage: "",
retrieveMessage: "",
// Actions 实现
setShareRoomId: (id) => set({ shareRoomId: id }),
setInitShareRoomId: (id) => set({ initShareRoomId: id }),
setShareLink: (link) => set({ shareLink: link }),
setShareRoomStatusText: (text) => set({ shareRoomStatusText: text }),
setRetrieveRoomStatusText: (text) => set({ retrieveRoomStatusText: text }),
// WebRTC 连接相关 actions
setShareConnectionState: (state) => set({ shareConnectionState: state }),
setIsSenderInRoom: (isInRoom) => set({ isSenderInRoom: isInRoom }),
setSharePeerCount: (count) => set({ sharePeerCount: count }),
setRetrieveConnectionState: (state) => set({ retrieveConnectionState: state }),
setIsReceiverInRoom: (isInRoom) => set({ isReceiverInRoom: isInRoom }),
setRetrievePeerCount: (count) => set({ retrievePeerCount: count }),
setSenderDisconnected: (disconnected) => set({ senderDisconnected: disconnected }),
setSenderDisconnected: (disconnected) =>
set({ senderDisconnected: disconnected }),
setShareContent: (content) => set({ shareContent: content }),
setSendFiles: (files) => set({ sendFiles: files }),
addSendFiles: (files) => set((state) => ({ sendFiles: [...state.sendFiles, ...files] })),
removeSendFile: (meta) => set((state) => {
if (meta.folderName && meta.folderName !== '') {
return {
sendFiles: state.sendFiles.filter(
(file) => file.folderName !== meta.folderName
)
};
} else {
return {
sendFiles: state.sendFiles.filter((file) => file.name !== meta.name)
};
}
}),
addSendFiles: (files) =>
set((state) => ({ sendFiles: [...state.sendFiles, ...files] })),
removeSendFile: (meta) =>
set((state) => {
if (meta.folderName && meta.folderName !== "") {
return {
sendFiles: state.sendFiles.filter(
(file) => file.folderName !== meta.folderName
),
};
} else {
return {
sendFiles: state.sendFiles.filter((file) => file.name !== meta.name),
};
}
}),
setRetrievedContent: (content) => set({ retrievedContent: content }),
setRetrievedFiles: (files) => set({ retrievedFiles: files }),
setRetrievedFileMetas: (metas) => set({ retrievedFileMetas: metas }),
addRetrievedFile: (file) =>
set((state) => ({ retrievedFiles: [...state.retrievedFiles, file] })),
setSendProgress: (progress) => set({ sendProgress: progress }),
setReceiveProgress: (progress) => set({ receiveProgress: progress }),
setIsAnyFileTransferring: (transferring) => set({ isAnyFileTransferring: transferring }),
updateSendProgress: (fileId, peerId, progress) =>
set((state) => ({
sendProgress: {
...state.sendProgress,
[fileId]: { ...state.sendProgress[fileId], [peerId]: progress },
},
})),
updateReceiveProgress: (fileId, peerId, progress) =>
set((state) => ({
receiveProgress: {
...state.receiveProgress,
[fileId]: { ...state.receiveProgress[fileId], [peerId]: progress },
},
})),
setIsAnyFileTransferring: (transferring) =>
set({ isAnyFileTransferring: transferring }),
setActiveTab: (tab) => set({ activeTab: tab }),
setRetrieveRoomIdInput: (input) => set({ retrieveRoomIdInput: input }),
setRetrieveRoomId: (input) => set({ retrieveRoomIdInput: input }),
setIsDragging: (dragging) => set({ isDragging: dragging }),
setShareMessage: (message) => set({ shareMessage: message }),
setRetrieveMessage: (message) => set({ retrieveMessage: message }),
resetReceiverState: () => set({
retrievedContent: '',
retrievedFiles: [],
retrievedFileMetas: []
}),
resetSenderApp: () => set({
shareLink: '',
sendProgress: {},
receiveProgress: {},
isAnyFileTransferring: false
})
}));
resetReceiverState: () =>
set({
retrievedContent: "",
retrievedFiles: [],
retrievedFileMetas: [],
retrievePeerCount: 0,
senderDisconnected: false,
retrieveRoomIdInput: "",
receiveProgress: {},
retrieveRoomStatusText: "",
}),
resetSenderApp: () =>
set({
shareLink: "",
sendProgress: {},
receiveProgress: {},
isAnyFileTransferring: false,
}),
}));