mirror of
https://github.com/MatrixSeven/file-transfer-go.git
synced 2026-06-11 04:53:11 +08:00
新增房间验证工具函数,包含房间代码格式验证和房间状态检查逻辑
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
// 连接相关的hooks
|
||||
export { useConnectionState } from './useConnectionState';
|
||||
export { useRoomConnection } from './useRoomConnection';
|
||||
export { useSharedWebRTCManager } from './useSharedWebRTCManager';
|
||||
export { useWebRTCSupport } from './useWebRTCSupport';
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useToast } from '@/components/ui/toast-simple';
|
||||
|
||||
interface UseConnectionStateProps {
|
||||
isWebSocketConnected: boolean;
|
||||
isConnected: boolean;
|
||||
isConnecting: boolean;
|
||||
error: string;
|
||||
pickupCode: string;
|
||||
fileListLength: number;
|
||||
currentTransferFile: any;
|
||||
setCurrentTransferFile: (file: any) => void;
|
||||
updateFileListStatus: (callback: (prev: any[]) => any[]) => void;
|
||||
}
|
||||
|
||||
export const useConnectionState = ({
|
||||
isWebSocketConnected,
|
||||
isConnected,
|
||||
isConnecting,
|
||||
error,
|
||||
pickupCode,
|
||||
fileListLength,
|
||||
currentTransferFile,
|
||||
setCurrentTransferFile,
|
||||
updateFileListStatus
|
||||
}: UseConnectionStateProps) => {
|
||||
const { showToast } = useToast();
|
||||
const [lastError, setLastError] = useState<string>('');
|
||||
|
||||
// 处理连接错误
|
||||
useEffect(() => {
|
||||
if (error && error !== lastError) {
|
||||
console.log('=== 连接错误处理 ===');
|
||||
console.log('错误信息:', error);
|
||||
|
||||
// 根据错误类型显示不同的提示
|
||||
let errorMessage = error;
|
||||
|
||||
if (error.includes('WebSocket')) {
|
||||
errorMessage = '服务器连接失败,请检查网络连接或稍后重试';
|
||||
} else if (error.includes('数据通道')) {
|
||||
errorMessage = '数据通道连接失败,请重新尝试连接';
|
||||
} else if (error.includes('连接超时')) {
|
||||
errorMessage = '连接超时,请检查网络状况或重新尝试';
|
||||
} else if (error.includes('连接失败')) {
|
||||
errorMessage = 'WebRTC连接失败,可能是网络环境限制,请尝试刷新页面';
|
||||
} else if (error.includes('信令错误')) {
|
||||
errorMessage = '信令服务器错误,请稍后重试';
|
||||
} else if (error.includes('创建连接失败')) {
|
||||
errorMessage = '无法建立P2P连接,请检查网络设置';
|
||||
}
|
||||
|
||||
// 显示错误提示
|
||||
showToast(errorMessage, "error");
|
||||
setLastError(error);
|
||||
|
||||
// 如果是严重连接错误,清理传输状态
|
||||
if (error.includes('连接失败') || error.includes('数据通道连接失败') || error.includes('WebSocket')) {
|
||||
console.log('严重连接错误,清理传输状态');
|
||||
setCurrentTransferFile(null);
|
||||
|
||||
// 重置所有正在传输的文件状态
|
||||
updateFileListStatus((prev: any[]) => prev.map(item =>
|
||||
item.status === 'downloading'
|
||||
? { ...item, status: 'ready' as const, progress: 0 }
|
||||
: item
|
||||
));
|
||||
}
|
||||
}
|
||||
}, [error, lastError, showToast, setCurrentTransferFile, updateFileListStatus]);
|
||||
|
||||
// 监听连接状态变化和清理传输状态
|
||||
useEffect(() => {
|
||||
console.log('=== 连接状态变化 ===');
|
||||
console.log('WebSocket连接状态:', isWebSocketConnected);
|
||||
console.log('WebRTC连接状态:', isConnected);
|
||||
console.log('连接中状态:', isConnecting);
|
||||
|
||||
// 当连接断开或有错误时,清理所有传输状态
|
||||
const shouldCleanup = (!isWebSocketConnected && !isConnected && !isConnecting && pickupCode) ||
|
||||
((!isConnected && !isConnecting) || error);
|
||||
|
||||
if (shouldCleanup) {
|
||||
const hasCurrentTransfer = !!currentTransferFile;
|
||||
const hasFileList = fileListLength > 0;
|
||||
|
||||
// 只有在之前有连接活动时才显示断开提示和清理状态
|
||||
if (hasFileList || hasCurrentTransfer) {
|
||||
if (!isWebSocketConnected && pickupCode) {
|
||||
showToast('与服务器的连接已断开,请重新连接', "error");
|
||||
}
|
||||
|
||||
console.log('连接断开,清理传输状态');
|
||||
|
||||
if (currentTransferFile) {
|
||||
setCurrentTransferFile(null);
|
||||
}
|
||||
|
||||
// 重置所有正在下载的文件状态
|
||||
updateFileListStatus((prev: any[]) => {
|
||||
const hasDownloadingFiles = prev.some(item => item.status === 'downloading');
|
||||
if (hasDownloadingFiles) {
|
||||
console.log('重置正在传输的文件状态');
|
||||
return prev.map(item =>
|
||||
item.status === 'downloading'
|
||||
? { ...item, status: 'ready' as const, progress: 0 }
|
||||
: item
|
||||
);
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket连接成功时的提示
|
||||
if (isWebSocketConnected && isConnecting && !isConnected) {
|
||||
console.log('WebSocket已连接,正在建立P2P连接...');
|
||||
}
|
||||
|
||||
}, [isWebSocketConnected, isConnected, isConnecting, pickupCode, error, showToast, currentTransferFile, fileListLength, setCurrentTransferFile, updateFileListStatus]);
|
||||
|
||||
// 监听连接状态变化并提供日志
|
||||
useEffect(() => {
|
||||
console.log('=== WebRTC连接状态变化 ===');
|
||||
console.log('连接状态:', {
|
||||
isConnected,
|
||||
isConnecting,
|
||||
isWebSocketConnected,
|
||||
pickupCode,
|
||||
fileListLength
|
||||
});
|
||||
}, [isConnected, isConnecting, isWebSocketConnected, pickupCode, fileListLength]);
|
||||
|
||||
return {
|
||||
lastError
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useToast } from '@/components/ui/toast-simple';
|
||||
import { validateRoomCode, checkRoomStatus } from '@/lib/room-utils';
|
||||
|
||||
interface UseRoomConnectionProps {
|
||||
connect: (code: string, role: 'sender' | 'receiver') => void;
|
||||
@@ -11,61 +12,6 @@ export const useRoomConnection = ({ connect, isConnecting, isConnected }: UseRoo
|
||||
const { showToast } = useToast();
|
||||
const [isJoiningRoom, setIsJoiningRoom] = useState(false);
|
||||
|
||||
const validateRoomCode = (code: string): string | null => {
|
||||
const trimmedCode = code.trim();
|
||||
if (!trimmedCode || trimmedCode.length !== 6) {
|
||||
return '请输入正确的6位取件码';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const checkRoomStatus = async (code: string) => {
|
||||
const response = await fetch(`/api/room-info?code=${code}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: 无法检查房间状态`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!result.success) {
|
||||
let errorMessage = result.message || '房间不存在或已过期';
|
||||
if (result.message?.includes('expired')) {
|
||||
errorMessage = '房间已过期,请联系发送方重新创建';
|
||||
} else if (result.message?.includes('not found')) {
|
||||
errorMessage = '房间不存在,请检查取件码是否正确';
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// 检查房间是否已满
|
||||
if (result.is_room_full) {
|
||||
throw new Error('当前房间人数已满,正在传输中无法加入');
|
||||
}
|
||||
|
||||
if (!result.sender_online) {
|
||||
throw new Error('发送方不在线,请确认取件码是否正确或联系发送方');
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const handleNetworkError = (error: Error): string => {
|
||||
if (error.message.includes('network') || error.message.includes('fetch')) {
|
||||
return '网络连接失败,请检查网络状况';
|
||||
} else if (error.message.includes('timeout')) {
|
||||
return '请求超时,请重试';
|
||||
} else if (error.message.includes('HTTP 404')) {
|
||||
return '房间不存在,请检查取件码';
|
||||
} else if (error.message.includes('HTTP 500')) {
|
||||
return '服务器错误,请稍后重试';
|
||||
} else if (error.message.includes('房间人数已满') || error.message.includes('正在传输中无法加入')) {
|
||||
return '当前房间人数已满,正在传输中无法加入,请稍后再试';
|
||||
} else {
|
||||
return error.message;
|
||||
}
|
||||
};
|
||||
|
||||
// 加入房间 (接收模式)
|
||||
const joinRoom = useCallback(async (code: string) => {
|
||||
console.log('=== 加入房间 ===');
|
||||
@@ -88,7 +34,12 @@ export const useRoomConnection = ({ connect, isConnecting, isConnected }: UseRoo
|
||||
|
||||
try {
|
||||
console.log('检查房间状态...');
|
||||
await checkRoomStatus(code.trim());
|
||||
const result = await checkRoomStatus(code.trim());
|
||||
|
||||
if (!result.success) {
|
||||
showToast(result.error || '检查房间状态失败', "error");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('房间状态检查通过,开始连接...');
|
||||
connect(code.trim(), 'receiver');
|
||||
@@ -96,7 +47,7 @@ export const useRoomConnection = ({ connect, isConnecting, isConnected }: UseRoo
|
||||
|
||||
} catch (error) {
|
||||
console.error('检查房间状态失败:', error);
|
||||
const errorMessage = error instanceof Error ? handleNetworkError(error) : '检查房间状态失败';
|
||||
const errorMessage = error instanceof Error ? error.message : '检查房间状态失败';
|
||||
showToast(errorMessage, "error");
|
||||
} finally {
|
||||
setIsJoiningRoom(false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useWebRTCStateManager } from './useWebRTCStateManager';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useWebRTCStore, type WebRTCStateManager } from '../ui/webRTCStore';
|
||||
import { useWebRTCDataChannelManager, WebRTCMessage } from './useWebRTCDataChannelManager';
|
||||
import { useWebRTCTrackManager } from './useWebRTCTrackManager';
|
||||
import { useWebRTCConnectionCore } from './useWebRTCConnectionCore';
|
||||
@@ -50,8 +50,27 @@ export interface WebRTCConnection {
|
||||
* 整合所有模块,提供统一的接口
|
||||
*/
|
||||
export function useSharedWebRTCManager(): WebRTCConnection {
|
||||
// 创建各个管理器实例
|
||||
const stateManager = useWebRTCStateManager();
|
||||
// 直接从 zustand store 创建状态管理器
|
||||
const store = useWebRTCStore();
|
||||
const stateManager: WebRTCStateManager = useMemo(() => ({
|
||||
getState: () => ({
|
||||
isConnected: store.isConnected,
|
||||
isConnecting: store.isConnecting,
|
||||
isWebSocketConnected: store.isWebSocketConnected,
|
||||
isPeerConnected: store.isPeerConnected,
|
||||
error: store.error,
|
||||
canRetry: store.canRetry,
|
||||
currentRoom: store.currentRoom,
|
||||
}),
|
||||
updateState: store.updateState,
|
||||
setCurrentRoom: store.setCurrentRoom,
|
||||
resetToInitial: store.resetToInitial,
|
||||
isConnectedToRoom: (roomCode: string, role: 'sender' | 'receiver') =>
|
||||
store.currentRoom?.code === roomCode &&
|
||||
store.currentRoom?.role === role &&
|
||||
store.isConnected,
|
||||
}), [store]);
|
||||
|
||||
const dataChannelManager = useWebRTCDataChannelManager(stateManager);
|
||||
const trackManager = useWebRTCTrackManager(stateManager);
|
||||
const connectionCore = useWebRTCConnectionCore(
|
||||
@@ -60,8 +79,15 @@ export function useSharedWebRTCManager(): WebRTCConnection {
|
||||
trackManager
|
||||
);
|
||||
|
||||
// 获取当前状态
|
||||
const state = stateManager.getState();
|
||||
// 从 store 获取当前状态
|
||||
const state = {
|
||||
isConnected: store.isConnected,
|
||||
isConnecting: store.isConnecting,
|
||||
isWebSocketConnected: store.isWebSocketConnected,
|
||||
isPeerConnected: store.isPeerConnected,
|
||||
error: store.error,
|
||||
canRetry: store.canRetry,
|
||||
};
|
||||
|
||||
// 创建 createOfferNow 方法
|
||||
const createOfferNow = useCallback(async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useRef, useCallback } from 'react';
|
||||
import { getWsUrl } from '@/lib/config';
|
||||
import { getIceServersConfig } from '../settings/useIceServersConfig';
|
||||
import { WebRTCStateManager } from './useWebRTCStateManager';
|
||||
import { WebRTCStateManager } from '../ui/webRTCStore';
|
||||
import { WebRTCDataChannelManager, WebRTCMessage } from './useWebRTCDataChannelManager';
|
||||
import { WebRTCTrackManager } from './useWebRTCTrackManager';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRef, useCallback } from 'react';
|
||||
import { WebRTCStateManager } from './useWebRTCStateManager';
|
||||
import { WebRTCStateManager } from '../ui/webRTCStore';
|
||||
|
||||
// 消息类型
|
||||
export interface WebRTCMessage {
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useWebRTCStore } from '../ui/webRTCStore';
|
||||
|
||||
// 基础连接状态
|
||||
export interface WebRTCState {
|
||||
isConnected: boolean;
|
||||
isConnecting: boolean;
|
||||
isWebSocketConnected: boolean;
|
||||
isPeerConnected: boolean;
|
||||
error: string | null;
|
||||
canRetry: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebRTC 状态管理器
|
||||
* 负责连接状态的统一管理
|
||||
*/
|
||||
export interface WebRTCStateManager {
|
||||
// 获取当前状态
|
||||
getState: () => WebRTCState;
|
||||
|
||||
// 更新状态
|
||||
updateState: (updates: Partial<WebRTCState>) => void;
|
||||
|
||||
// 设置当前房间
|
||||
setCurrentRoom: (room: { code: string; role: 'sender' | 'receiver' } | null) => void;
|
||||
|
||||
// 重置到初始状态
|
||||
resetToInitial: () => void;
|
||||
|
||||
// 检查是否已连接到指定房间
|
||||
isConnectedToRoom: (roomCode: string, role: 'sender' | 'receiver') => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebRTC 状态管理 Hook
|
||||
* 封装对 webRTCStore 的操作,提供状态更新和查询的统一接口
|
||||
*/
|
||||
export function useWebRTCStateManager(): WebRTCStateManager {
|
||||
const webrtcStore = useWebRTCStore();
|
||||
|
||||
const getState = useCallback((): WebRTCState => {
|
||||
return {
|
||||
isConnected: webrtcStore.isConnected,
|
||||
isConnecting: webrtcStore.isConnecting,
|
||||
isWebSocketConnected: webrtcStore.isWebSocketConnected,
|
||||
isPeerConnected: webrtcStore.isPeerConnected,
|
||||
error: webrtcStore.error,
|
||||
canRetry: webrtcStore.canRetry,
|
||||
};
|
||||
}, [webrtcStore]);
|
||||
|
||||
const updateState = useCallback((updates: Partial<WebRTCState>) => {
|
||||
webrtcStore.updateState(updates);
|
||||
}, [webrtcStore]);
|
||||
|
||||
const setCurrentRoom = useCallback((room: { code: string; role: 'sender' | 'receiver' } | null) => {
|
||||
webrtcStore.setCurrentRoom(room);
|
||||
}, [webrtcStore]);
|
||||
|
||||
const resetToInitial = useCallback(() => {
|
||||
webrtcStore.resetToInitial();
|
||||
}, [webrtcStore]);
|
||||
|
||||
const isConnectedToRoom = useCallback((roomCode: string, role: 'sender' | 'receiver') => {
|
||||
return webrtcStore.currentRoom?.code === roomCode &&
|
||||
webrtcStore.currentRoom?.role === role &&
|
||||
webrtcStore.isConnected;
|
||||
}, [webrtcStore]);
|
||||
|
||||
return {
|
||||
getState,
|
||||
updateState,
|
||||
setCurrentRoom,
|
||||
resetToInitial,
|
||||
isConnectedToRoom,
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { WebRTCStateManager } from './useWebRTCStateManager';
|
||||
import { WebRTCStateManager } from '../ui/webRTCStore';
|
||||
|
||||
/**
|
||||
* WebRTC 媒体轨道管理器
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { useRef, useCallback, useEffect } from 'react';
|
||||
|
||||
interface FileInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
size: number;
|
||||
type: string;
|
||||
status: 'ready' | 'downloading' | 'completed';
|
||||
progress: number;
|
||||
}
|
||||
import type { FileInfo } from '@/types';
|
||||
|
||||
interface UseFileListSyncProps {
|
||||
sendFileList: (fileInfos: FileInfo[]) => void;
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
|
||||
interface FileInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
size: number;
|
||||
type: string;
|
||||
status: 'ready' | 'downloading' | 'completed';
|
||||
progress: number;
|
||||
}
|
||||
import type { FileInfo } from '@/types';
|
||||
|
||||
interface UseFileStateManagerProps {
|
||||
mode: 'send' | 'receive';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import type { WebRTCConnection } from '../connection/useSharedWebRTCManager';
|
||||
import type { FileInfo } from '@/types';
|
||||
|
||||
// 文件传输状态
|
||||
interface FileTransferState {
|
||||
@@ -21,16 +22,6 @@ interface FileReceiveProgress {
|
||||
progress: number;
|
||||
}
|
||||
|
||||
// 文件信息
|
||||
interface FileInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
size: number;
|
||||
type: string;
|
||||
status: 'ready' | 'downloading' | 'completed';
|
||||
progress: number;
|
||||
}
|
||||
|
||||
// 文件元数据
|
||||
interface FileMetadata {
|
||||
id: string;
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface WebRTCState {
|
||||
export interface WebRTCState {
|
||||
isConnected: boolean;
|
||||
isConnecting: boolean;
|
||||
isWebSocketConnected: boolean;
|
||||
isPeerConnected: boolean;
|
||||
error: string | null;
|
||||
canRetry: boolean; // 新增:是否可以重试
|
||||
canRetry: boolean;
|
||||
currentRoom: { code: string; role: 'sender' | 'receiver' } | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebRTC 状态管理器接口
|
||||
* 供 ConnectionCore / DataChannelManager / TrackManager 作为参数使用
|
||||
*/
|
||||
export interface WebRTCStateManager {
|
||||
getState: () => WebRTCState;
|
||||
updateState: (updates: Partial<WebRTCState>) => void;
|
||||
setCurrentRoom: (room: { code: string; role: 'sender' | 'receiver' } | null) => void;
|
||||
resetToInitial: () => void;
|
||||
isConnectedToRoom: (roomCode: string, role: 'sender' | 'receiver') => boolean;
|
||||
}
|
||||
|
||||
interface WebRTCStore extends WebRTCState {
|
||||
updateState: (updates: Partial<WebRTCState>) => void;
|
||||
setCurrentRoom: (room: { code: string; role: 'sender' | 'receiver' } | null) => void;
|
||||
reset: () => void;
|
||||
resetToInitial: () => void; // 新增:完全重置到初始状态
|
||||
resetToInitial: () => void;
|
||||
}
|
||||
|
||||
const initialState: WebRTCState = {
|
||||
@@ -23,7 +35,7 @@ const initialState: WebRTCState = {
|
||||
isWebSocketConnected: false,
|
||||
isPeerConnected: false,
|
||||
error: null,
|
||||
canRetry: false, // 初始状态下不需要重试
|
||||
canRetry: false,
|
||||
currentRoom: null,
|
||||
};
|
||||
|
||||
@@ -42,5 +54,5 @@ export const useWebRTCStore = create<WebRTCStore>((set) => ({
|
||||
|
||||
reset: () => set(initialState),
|
||||
|
||||
resetToInitial: () => set(initialState), // 完全重置到初始状态
|
||||
resetToInitial: () => set(initialState),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user