Files
PrivyDrop/frontend/hooks/useRoomManager.ts
T
2025-08-17 13:24:41 +08:00

399 lines
12 KiB
TypeScript

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());
}
interface UseRoomManagerProps {
messages: Messages | null;
putMessageInMs: (
message: string,
isShareEnd?: boolean,
displayTimeMs?: number
) => void;
sender: WebRTC_Initiator | null;
receiver: WebRTC_Recipient | null;
broadcastDataToPeers: () => Promise<boolean>;
resetSenderConnection: () => Promise<void>;
resetReceiverConnection: () => Promise<void>;
}
export function useRoomManager({
messages,
putMessageInMs,
sender,
receiver,
broadcastDataToPeers,
resetSenderConnection,
resetReceiverConnection,
}: UseRoomManagerProps) {
// 从 store 中获取状态
const {
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
activeTab,
sharePeerCount,
retrievePeerCount,
senderDisconnected,
isSenderInRoom,
isReceiverInRoom,
setShareRoomId,
setInitShareRoomId,
setShareLink,
setShareRoomStatusText,
setRetrieveRoomStatusText,
setSharePeerCount,
setRetrievePeerCount,
resetReceiverState,
resetSenderApp,
} = useFileTransferStore();
// 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);
putMessageInMs(messages.text.ClipboardApp.roomStatus.leftRoomMsg, false);
} catch (error) {
console.error("Error leaving room:", error);
putMessageInMs("Failed to leave the room.", false);
} finally {
// Reset application state (不清空房间ID)
resetReceiverState();
// 清理WebRTC连接状态
await resetReceiverConnection();
console.log(
`[RoomManager Debug] Receiver left room and WebRTC connection reset`
);
}
}, [
receiver,
putMessageInMs,
messages,
resetReceiverState,
resetReceiverConnection,
]);
// Reset sender app state
const resetSenderAppState = useCallback(async () => {
try {
// 1. Clean up WebRTC connections and reset peer count
await resetSenderConnection();
// 2. Clear share link and progress
resetSenderApp();
// 3. Get new room ID from backend
const newRoomId = await fetchRoom();
setShareRoomId(newRoomId || "");
setInitShareRoomId(newRoomId || "");
console.log(
"Sender application state reset successfully, new room ID:",
newRoomId
);
} catch (error) {
console.error("Error during sender state reset:", error);
putMessageInMs("Error resetting sender state.", true);
}
}, [
resetSenderConnection,
putMessageInMs,
resetSenderApp,
setShareRoomId,
setInitShareRoomId,
]);
// Sender leave room function
const handleLeaveSenderRoom = useCallback(async () => {
if (!sender || !sender.roomId || !sender.peerId || !messages) return;
try {
await leaveRoom(sender.roomId, sender.peerId);
putMessageInMs(messages.text.ClipboardApp.roomStatus.leftRoomMsg, true);
} catch (error) {
console.error("Error leaving room:", error);
putMessageInMs("Failed to leave the room.", true);
} finally {
// Reset sender state and get new room ID
await resetSenderAppState();
}
}, [sender, putMessageInMs, resetSenderAppState, messages]);
// Initialize shareRoomId on mount
useEffect(() => {
if (
messages &&
putMessageInMs &&
!initShareRoomId &&
activeTab === "send"
) {
// Ensure this only runs on the sender's side on initial load
const initNewRoom = async () => {
try {
const newRoomId = await fetchRoom();
setShareRoomId(newRoomId || "");
setInitShareRoomId(newRoomId || "");
} catch (err) {
console.error("Error fetching initial room:", err);
const errorMsg =
messages.text?.ClipboardApp?.fetchRoom_err ||
"Error fetching room ID.";
putMessageInMs(errorMsg, true);
}
};
initNewRoom();
}
}, [
messages,
initShareRoomId,
activeTab,
setShareRoomId,
setInitShareRoomId,
putMessageInMs,
]);
// Debounced function to actually check the room ID and update the state
const performDebouncedRoomCheck = useMemo(
() =>
debounce(async (roomIdToCheck: string) => {
if (!messages || !putMessageInMs) return;
if (!roomIdToCheck.trim()) {
return;
}
try {
const available = await checkRoom(roomIdToCheck);
if (available) {
setShareRoomId(roomIdToCheck);
putMessageInMs(
messages.text.ClipboardApp.roomCheck.available_msg,
true
);
} else {
putMessageInMs(
messages.text.ClipboardApp.roomCheck.notAvailable_msg,
true
);
}
} catch (error) {
console.error("Error checking room availability:", error);
putMessageInMs("Error checking room.", true);
}
}, 750),
[messages, putMessageInMs, setShareRoomId]
);
// UI calls this function to handle changes in the room ID input
const processRoomIdInput = useCallback(
(inputRoomId: string) => {
if (!inputRoomId.trim() && messages && putMessageInMs) {
putMessageInMs(messages.text.ClipboardApp.roomCheck.empty_msg, true);
performDebouncedRoomCheck.cancel();
return;
}
performDebouncedRoomCheck(inputRoomId);
},
[performDebouncedRoomCheck, messages, putMessageInMs]
);
const joinRoom = useCallback(
async (isSenderSide: boolean, currentRoomIdToJoin: string) => {
if (
!messages ||
!putMessageInMs ||
(isSenderSide && !sender) ||
(!isSenderSide && !receiver)
) {
console.warn("joinRoom prerequisites not met");
return;
}
const peer = isSenderSide ? sender : receiver;
if (!peer) return;
if (!currentRoomIdToJoin.trim()) {
putMessageInMs(
messages.text.ClipboardApp.joinRoom.EmptyMsg,
isSenderSide
);
return;
}
// Create room if sender and not the initial room ID
if (isSenderSide && activeTab === "send" && !peer.isInRoom) {
if (currentRoomIdToJoin !== initShareRoomId) {
try {
const success = await createRoom(currentRoomIdToJoin);
if (!success) {
putMessageInMs(
messages.text.ClipboardApp.joinRoom.DuplicateMsg,
isSenderSide
);
return;
}
setShareRoomId(currentRoomIdToJoin);
} catch (error) {
putMessageInMs(
messages.text.ClipboardApp.joinRoom.failMsg +
` (Create room error)`,
isSenderSide
);
return;
}
}
}
try {
const actualRoomIdForSenderJoin =
isSenderSide && currentRoomIdToJoin !== initShareRoomId
? currentRoomIdToJoin
: isSenderSide
? 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,
isSenderSide,
6000
);
// 更新 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) {
setShareRoomId(actualRoomIdForSenderJoin);
}
} else {
useFileTransferStore.getState().setIsReceiverInRoom(true);
console.log(
`[RoomManager Debug] Receiver joined room, setIsReceiverInRoom(true)`
);
}
} catch (error) {
let errorMsgToShow = messages.text.ClipboardApp.joinRoom.failMsg;
if (error instanceof Error) {
errorMsgToShow =
error.message === "Room does not exist"
? messages.text.ClipboardApp.joinRoom.notExist
: `${messages.text.ClipboardApp.joinRoom.failMsg} ${error.message}`;
}
putMessageInMs(errorMsgToShow, isSenderSide);
}
},
[
messages,
putMessageInMs,
sender,
receiver,
activeTab,
initShareRoomId,
shareRoomId,
setShareLink,
setShareRoomId,
]
);
const generateShareLinkAndBroadcast = useCallback(async () => {
if (!sender || !messages || !putMessageInMs || !shareRoomId) return;
if (sharePeerCount === 0) {
putMessageInMs(messages.text.ClipboardApp.waitting_tips, true);
} else {
await broadcastDataToPeers();
}
const link = `${window.location.origin}${window.location.pathname}?roomId=${shareRoomId}`;
setShareLink(link);
}, [
sender,
messages,
putMessageInMs,
shareRoomId,
sharePeerCount,
setShareLink,
broadcastDataToPeers,
]);
// useEffect for room status text
useEffect(() => {
if (!messages) {
if (activeTab === "send") setShareRoomStatusText("");
else setRetrieveRoomStatusText("");
return;
}
const isInRoom = activeTab === "send" ? isSenderInRoom : isReceiverInRoom;
const currentPeerCount =
activeTab === "send" ? sharePeerCount : retrievePeerCount;
let 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,
messages,
senderDisconnected,
isSenderInRoom,
isReceiverInRoom,
setShareRoomStatusText,
setRetrieveRoomStatusText,
]);
return {
shareRoomId,
initShareRoomId,
shareLink,
shareRoomStatusText,
retrieveRoomStatusText,
processRoomIdInput,
joinRoom,
generateShareLinkAndBroadcast,
handleLeaveReceiverRoom,
handleLeaveSenderRoom,
};
}