diff --git a/chuan-next/src/app/HomePage-new.tsx b/chuan-next/src/app/HomePage-new.tsx
index e85a448..770941d 100644
--- a/chuan-next/src/app/HomePage-new.tsx
+++ b/chuan-next/src/app/HomePage-new.tsx
@@ -1,233 +1,63 @@
"use client";
-import React, { useEffect } from 'react';
+import React from 'react';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
-import { Upload, MessageSquare, Monitor } from 'lucide-react';
+import { Upload, MessageSquare, Monitor, Github, ExternalLink } from 'lucide-react';
import Hero from '@/components/Hero';
-import FileTransfer from '@/components/FileTransfer';
import TextTransfer from '@/components/TextTransfer';
import DesktopShare from '@/components/DesktopShare';
-import { RoomStatusDisplay } from '@/components/RoomStatusDisplay';
-import { TabSwitchDialog } from '@/components/TabSwitchDialog';
-
-// Hooks
-import { useFileTransfer } from '@/hooks/useFileTransfer';
-import { useRoomManager } from '@/hooks/useRoomManager';
-import { useFileSender } from '@/hooks/useFileSender';
-import { useFileReceiver } from '@/hooks/useFileReceiver';
-import { useWebSocketHandler } from '@/hooks/useWebSocketHandler';
-import { useTabManager } from '@/hooks/useTabManager';
-import { useUtilities } from '@/hooks/useUtilities';
-import { useUrlHandler } from '@/hooks/useUrlHandler';
+import { WebRTCFileTransfer } from '@/components/WebRTCFileTransfer';
+import { Button } from '@/components/ui/button';
export default function HomePage() {
- // 文件传输相关
- const {
- fileTransfers,
- transferProgresses,
- initFileTransfer,
- receiveFileChunk,
- completeFileDownload,
- clearTransfers,
- setTransferProgresses
- } = useFileTransfer();
-
- // 房间管理相关
- const {
- selectedFiles,
- pickupCode,
- pickupLink,
- currentRole,
- receiverFiles,
- isConnecting,
- roomStatus,
- isConnected,
- websocket,
- setSelectedFiles,
- setReceiverFiles,
- setRoomStatus,
- setIsConnecting,
- setCurrentRole,
- resetConnectingState,
- generateCode,
- joinRoom,
- updateFileList,
- handleRemoveFile,
- clearFiles,
- resetRoom,
- sendMessage,
- disconnect,
- connect
- } = useRoomManager();
-
- // Tab管理相关
- const {
- activeTab,
- showConfirmDialog,
- setShowConfirmDialog,
- handleTabChange,
- confirmTabSwitch,
- cancelTabSwitch,
- getModeDescription,
- updateUrlParams
- } = useTabManager(isConnected, pickupCode, isConnecting);
-
- // 工具函数
- const { copyToClipboard, showNotification } = useUtilities();
-
- // 文件发送处理
- const { handleFileRequest } = useFileSender(selectedFiles, sendMessage);
-
- // 文件接收处理
- const { downloadFile } = useFileReceiver(
- receiverFiles,
- transferProgresses,
- setTransferProgresses,
- websocket,
- sendMessage
- );
-
- // WebSocket连接状态变化处理
- useEffect(() => {
- resetConnectingState();
- }, [resetConnectingState]);
-
- // 额外的连接状态重置逻辑
- useEffect(() => {
- if (isConnected) {
- setIsConnecting(false);
- console.log('WebSocket已连接,重置连接中状态');
- }
- }, [isConnected, setIsConnecting]);
-
- // 监听WebSocket错误事件
- useEffect(() => {
- const handleWebSocketError = (event: CustomEvent) => {
- console.error('WebSocket连接错误:', event.detail);
- setIsConnecting(false);
- showNotification('连接失败,请检查网络或重试', 'error');
- };
-
- const handleWebSocketConnected = (event: CustomEvent) => {
- console.log('WebSocket连接成功:', event.detail);
- setIsConnecting(false);
- showNotification('连接成功!', 'success');
- };
-
- window.addEventListener('websocket-error', handleWebSocketError as EventListener);
- window.addEventListener('websocket-connected', handleWebSocketConnected as EventListener);
-
- return () => {
- window.removeEventListener('websocket-error', handleWebSocketError as EventListener);
- window.removeEventListener('websocket-connected', handleWebSocketConnected as EventListener);
- };
- }, [setIsConnecting, showNotification]);
-
- // WebSocket消息处理
- useWebSocketHandler({
- currentRole,
- setReceiverFiles,
- setRoomStatus,
- setIsConnecting,
- initFileTransfer,
- receiveFileChunk,
- completeFileDownload,
- handleFileRequest
- });
-
- // URL参数处理
- useUrlHandler({
- isConnected,
- pickupCode,
- setCurrentRole,
- joinRoom
- });
-
- // 处理添加更多文件
- const handleAddMoreFiles = () => {
- const input = document.createElement('input');
- input.type = 'file';
- input.multiple = true;
- input.onchange = async (e) => {
- const files = Array.from((e.target as HTMLInputElement).files || []);
- const newFiles = [...selectedFiles, ...files];
- setSelectedFiles(newFiles);
-
- if (pickupCode && files.length > 0) {
- updateFileList(newFiles);
- }
- };
- input.click();
- };
-
return (
-
- {/* Hero Section */}
-
- {/* Background decorations */}
-
-
-
+
+
+ {/* Hero Section */}
+
+
-
-
-
+ {/* Main Content */}
+
+
+ {/* Tabs Navigation - 横向布局 */}
+
+
-
- 传文件
+
+ 文件传输
文件
-
- 传文字
- 文字
+
+ 文本传输
+ 文本
-
+
共享桌面
桌面
+
-
- copyToClipboard(pickupCode, '取件码已复制到剪贴板!')}
- onCopyLink={() => copyToClipboard(pickupLink, '取件链接已复制到剪贴板!')}
- onAddMoreFiles={handleAddMoreFiles}
- onRemoveFile={handleRemoveFile}
- onClearFiles={clearFiles}
- onReset={resetRoom}
- onJoinRoom={joinRoom}
- receiverFiles={receiverFiles}
- onDownloadFile={downloadFile}
- transferProgresses={transferProgresses}
- isConnected={isConnected}
- isConnecting={isConnecting}
- disabled={isConnecting}
- />
-
-
+ {/* Tab Content */}
+
+
+
-
+
{
try {
@@ -242,63 +72,40 @@ export default function HomePage() {
const data = await response.json();
if (!response.ok) {
- const errorMessage = data.error || '创建文字传输房间失败';
- showNotification(errorMessage, 'error');
- return '';
+ throw new Error(data.error || '创建文本房间失败');
}
return data.code;
} catch (error) {
- console.error('创建文字传输房间失败:', error);
- showNotification('网络错误,请重试', 'error');
- return '';
+ console.error('创建文本房间失败:', error);
+ throw error;
}
}}
onReceiveText={async (code: string) => {
- console.log('onReceiveText被调用,但文字内容将通过WebSocket获取:', code);
- return '';
- }}
- websocket={websocket}
- isConnected={isConnected}
- currentRole={currentRole}
- onCreateWebSocket={(code: string, role: 'sender' | 'receiver') => {
- if (websocket) {
- disconnect();
+ try {
+ const response = await fetch(`/api/get-text-content?code=${code}`);
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.error || '获取文本内容失败');
+ }
+
+ return data.text;
+ } catch (error) {
+ console.error('获取文本内容失败:', error);
+ throw error;
}
- connect(code, role);
}}
/>
-
- {
- showNotification('桌面共享功能开发中', 'info');
- return 'DEF456';
- }}
- onStopSharing={async () => {
- showNotification('桌面共享已停止', 'info');
- }}
- onJoinSharing={async (code: string) => {
- showNotification('桌面共享功能开发中', 'info');
- }}
- />
+
+
-
-
-
-
+
+
-
- {/* 确认对话框 */}
-
);
}
diff --git a/chuan-next/src/app/api/room-info/route.ts b/chuan-next/src/app/api/room-info/route.ts
deleted file mode 100644
index c27fdee..0000000
--- a/chuan-next/src/app/api/room-info/route.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-
-const GO_BACKEND_URL = process.env.GO_BACKEND_URL || 'http://localhost:8080';
-
-export async function GET(request: NextRequest) {
- try {
- const { searchParams } = new URL(request.url);
- const code = searchParams.get('code');
-
- if (!code) {
- return NextResponse.json(
- { error: 'Missing code parameter' },
- { status: 400 }
- );
- }
-
- console.log('API Route: Getting room info, proxying to:', `${GO_BACKEND_URL}/api/room-info?code=${code}`);
-
- const response = await fetch(`${GO_BACKEND_URL}/api/room-info?code=${code}`, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-
- const data = await response.json();
-
- console.log('Backend response:', response.status, data);
-
- return NextResponse.json(data, { status: response.status });
- } catch (error) {
- console.error('API Route Error:', error);
- return NextResponse.json(
- { error: 'Failed to get room info', details: error instanceof Error ? error.message : 'Unknown error' },
- { status: 500 }
- );
- }
-}
diff --git a/chuan-next/src/app/api/room-status/route.ts b/chuan-next/src/app/api/room-status/route.ts
deleted file mode 100644
index 62b6cab..0000000
--- a/chuan-next/src/app/api/room-status/route.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-
-const GO_BACKEND_URL = process.env.GO_BACKEND_URL || 'http://localhost:8080';
-
-export async function GET(request: NextRequest) {
- try {
- const { searchParams } = new URL(request.url);
- const code = searchParams.get('code');
-
- if (!code) {
- return NextResponse.json(
- { error: 'Missing code parameter' },
- { status: 400 }
- );
- }
-
- console.log('API Route: Getting room status, proxying to:', `${GO_BACKEND_URL}/api/room-status?code=${code}`);
-
- const response = await fetch(`${GO_BACKEND_URL}/api/room-status?code=${code}`, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-
- const data = await response.json();
-
- console.log('Backend response:', response.status, data);
-
- return NextResponse.json(data, { status: response.status });
- } catch (error) {
- console.error('API Route Error:', error);
- return NextResponse.json(
- { error: 'Failed to get room status', details: error instanceof Error ? error.message : 'Unknown error' },
- { status: 500 }
- );
- }
-}
diff --git a/chuan-next/src/app/api/update-files/route.ts b/chuan-next/src/app/api/update-files/route.ts
deleted file mode 100644
index 2c869bd..0000000
--- a/chuan-next/src/app/api/update-files/route.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-
-const GO_BACKEND_URL = process.env.GO_BACKEND_URL || 'http://localhost:8080';
-
-export async function POST(request: NextRequest) {
- try {
- console.log('API Route: Updating files, proxying to:', `${GO_BACKEND_URL}/api/update-files`);
-
- const body = await request.json();
-
- const response = await fetch(`${GO_BACKEND_URL}/api/update-files`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- console.log('Backend response:', response.status, data);
-
- return NextResponse.json(data, { status: response.status });
- } catch (error) {
- console.error('API Route Error:', error);
- return NextResponse.json(
- { error: 'Failed to update files', details: error instanceof Error ? error.message : 'Unknown error' },
- { status: 500 }
- );
- }
-}
diff --git a/chuan-next/src/components/DesktopShare.tsx b/chuan-next/src/components/DesktopShare.tsx
index cf15a65..f7b022e 100644
--- a/chuan-next/src/components/DesktopShare.tsx
+++ b/chuan-next/src/components/DesktopShare.tsx
@@ -133,15 +133,58 @@ export default function DesktopShare({ onStartSharing, onStopSharing, onJoinShar
{mode === 'share' ? (
-
-
-
-
+
+ {/* 功能标题和状态 */}
+
+
+
+
+
+
+
共享桌面
+
+ {isSharing ? '桌面共享进行中' : '开始共享您的桌面屏幕'}
+
+
+
+
+ {/* 竖线分割 */}
+
+
+ {/* 状态显示 */}
+
+
连接状态
+
+ {/* WebSocket状态 */}
+
+
+ {/* 分隔符 */}
+
|
+
+ {/* WebRTC状态 */}
+
+ {isSharing ? (
+ <>
+
+
RTC
+ >
+ ) : (
+ <>
+
+
RTC
+ >
+ )}
+
+
+ {isSharing && connectionCode && (
+
+ {connectionCode}
+
+ )}
-
共享桌面
-
- {isSharing ? '桌面共享进行中' : '开始共享您的桌面屏幕'}
-
@@ -202,15 +245,63 @@ export default function DesktopShare({ onStartSharing, onStopSharing, onJoinShar
) : (
-
-
-
-
+
+ {/* 功能标题和状态 */}
+
+
+
+
+
+
+
观看桌面
+
+ {isViewing ? '正在观看桌面共享' : '输入连接码观看他人的桌面'}
+
+
+
+
+ {/* 竖线分割 */}
+
+
+ {/* 状态显示 */}
+
+
连接状态
+
+ {/* WebSocket状态 */}
+
+
+ {/* 分隔符 */}
+
|
+
+ {/* WebRTC状态 */}
+
+ {isViewing ? (
+ <>
+
+
RTC
+ >
+ ) : isLoading ? (
+ <>
+
+
RTC
+ >
+ ) : (
+ <>
+
+
RTC
+ >
+ )}
+
+
+ {isViewing && (
+
+ 观看中
+
+ )}
-
观看桌面
-
- {isViewing ? '正在观看桌面共享' : '输入连接码观看他人的桌面'}
-
diff --git a/chuan-next/src/components/FileReceive.tsx b/chuan-next/src/components/FileReceive.tsx
deleted file mode 100644
index edce1a4..0000000
--- a/chuan-next/src/components/FileReceive.tsx
+++ /dev/null
@@ -1,266 +0,0 @@
-"use client";
-
-import { useState, useCallback } from 'react';
-import { Button } from '@/components/ui/button';
-import { Input } from '@/components/ui/input';
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
-import { Progress } from '@/components/ui/progress';
-import { Download, FileText, Image, Video, Music, Archive } from 'lucide-react';
-import { FileInfo, TransferProgress } from '@/types';
-
-interface FileReceiveProps {
- onJoinRoom: (code: string) => void;
- files: FileInfo[];
- onDownloadFile: (fileId: string) => void;
- transferProgresses: TransferProgress[];
- isConnected: boolean;
- isConnecting: boolean;
-}
-
-const getFileIcon = (mimeType: string) => {
- if (mimeType.startsWith('image/')) return
;
- if (mimeType.startsWith('video/')) return
;
- if (mimeType.startsWith('audio/')) return
;
- if (mimeType.includes('zip') || mimeType.includes('rar')) return
;
- return
;
-};
-
-const formatFileSize = (bytes: number): string => {
- if (bytes === 0) return '0 Bytes';
- const k = 1024;
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
-};
-
-export function FileReceive({
- onJoinRoom,
- files,
- onDownloadFile,
- transferProgresses,
- isConnected,
- isConnecting
-}: FileReceiveProps) {
- const [pickupCode, setPickupCode] = useState('');
-
- const handleSubmit = useCallback((e: React.FormEvent) => {
- e.preventDefault();
- if (pickupCode.length === 6) {
- onJoinRoom(pickupCode.toUpperCase());
- }
- }, [pickupCode, onJoinRoom]);
-
- const handleInputChange = useCallback((e: React.ChangeEvent
) => {
- const value = e.target.value.replace(/[^A-Z0-9]/g, '').toUpperCase();
- if (value.length <= 6) {
- setPickupCode(value);
- }
- }, []);
-
- // 如果已经连接但没有文件,显示等待界面
- if ((isConnected || isConnecting) && files.length === 0) {
- return (
-
-
-
-
-
-
等待文件
-
- {isConnected ? '已连接到房间,等待发送方选择文件...' : '正在连接到房间...'}
-
-
- {/* 连接状态指示器 */}
-
-
-
-
- {isConnected ? '连接已建立' : '连接中...'}
-
-
-
-
- {/* 等待动画 */}
-
- {[...Array(3)].map((_, i) => (
-
- ))}
-
-
-
-
- 💡 提示:房间已连接,发送方清空文件列表后您会看到此界面,等待对方重新选择文件
-
-
-
-
- );
- }
-
- // 如果已经连接并且有文件列表,显示文件列表
- if (files.length > 0) {
- return (
-
-
-
-
-
-
-
-
-
可下载文件
-
- {isConnected ? (
- ✅ 已连接,可以下载文件
- ) : (
- ⏳ 正在建立连接...
- )}
-
-
-
-
- {files.length} 个文件
-
-
-
-
- {files.map((file) => {
- const progress = transferProgresses.find(p => p.originalFileId === file.id);
- const isDownloading = progress && progress.status === 'downloading';
- const isCompleted = progress && progress.status === 'completed';
-
- return (
-
-
-
-
- {getFileIcon(file.type)}
-
-
-
{file.name}
-
{formatFileSize(file.size)}
- {isCompleted && (
-
✅ 下载完成
- )}
-
-
-
-
-
- {progress && (progress.status === 'downloading' || progress.status === 'completed') && (
-
-
- {progress.status === 'completed' ? '下载完成' : '正在下载...'}
- {progress.progress.toFixed(1)}%
-
-
-
- {formatFileSize(progress.receivedSize)} / {formatFileSize(progress.totalSize)}
- {progress.status === 'downloading' && (
- 预计还需 {Math.ceil((progress.totalSize - progress.receivedSize) / 1024 / 1024)} MB
- )}
-
-
- )}
-
- );
- })}
-
-
-
- );
- }
-
- // 显示取件码输入界面
- return (
-
-
-
-
-
-
输入取件码
-
请输入6位取件码来获取文件
-
-
-
-
- {/* 使用提示 */}
-
-
- 💡 提示:取件码由发送方提供,有效期为24小时
-
-
-
- );
-}
diff --git a/chuan-next/src/components/FileTransfer.tsx b/chuan-next/src/components/FileTransfer.tsx
deleted file mode 100644
index 9796573..0000000
--- a/chuan-next/src/components/FileTransfer.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-"use client";
-
-import React, { useState, useCallback, useEffect } from 'react';
-import { useSearchParams, useRouter } from 'next/navigation';
-import { Button } from '@/components/ui/button';
-import { Upload, Download } from 'lucide-react';
-import FileUpload from '@/components/FileUpload';
-import { FileReceive } from '@/components/FileReceive';
-import { FileInfo, TransferProgress } from '@/types';
-
-interface FileTransferProps {
- // 发送方相关
- selectedFiles: File[];
- onFilesChange: (files: File[]) => void;
- onGenerateCode: () => void;
- pickupCode: string;
- pickupLink: string;
- onCopyCode: () => void;
- onCopyLink: () => void;
- onAddMoreFiles: () => void;
- onRemoveFile: (updatedFiles: File[]) => void;
- onClearFiles?: () => void;
- onReset: () => void;
-
- // 接收方相关
- onJoinRoom: (code: string) => void;
- receiverFiles: FileInfo[];
- onDownloadFile: (fileId: string) => void;
- transferProgresses: TransferProgress[];
-
- // 通用状态
- isConnected: boolean;
- isConnecting: boolean;
- disabled?: boolean;
-}
-
-export default function FileTransfer({
- selectedFiles,
- onFilesChange,
- onGenerateCode,
- pickupCode,
- pickupLink,
- onCopyCode,
- onCopyLink,
- onAddMoreFiles,
- onRemoveFile,
- onClearFiles,
- onReset,
- onJoinRoom,
- receiverFiles,
- onDownloadFile,
- transferProgresses,
- isConnected,
- isConnecting,
- disabled = false
-}: FileTransferProps) {
- const searchParams = useSearchParams();
- const router = useRouter();
- const [mode, setMode] = useState<'send' | 'receive'>('send');
-
- // 从URL参数中获取初始模式
- useEffect(() => {
- const urlMode = searchParams.get('mode') as 'send' | 'receive';
- const type = searchParams.get('type');
-
- if (type === 'file' && urlMode && ['send', 'receive'].includes(urlMode)) {
- setMode(urlMode);
- }
- }, [searchParams]);
-
- // 更新URL参数
- const updateMode = useCallback((newMode: 'send' | 'receive') => {
- setMode(newMode);
- const params = new URLSearchParams(searchParams.toString());
- params.set('type', 'file');
- params.set('mode', newMode);
- router.push(`?${params.toString()}`, { scroll: false });
- }, [searchParams, router]);
-
- return (
-
- {/* 模式切换 */}
-
-
-
-
-
-
-
- {mode === 'send' ? (
-
-
-
- ) : (
-
-
-
- )}
-
- );
-}
diff --git a/chuan-next/src/components/Hero.tsx b/chuan-next/src/components/Hero.tsx
index 85533d4..159aa6b 100644
--- a/chuan-next/src/components/Hero.tsx
+++ b/chuan-next/src/components/Hero.tsx
@@ -1,40 +1,21 @@
"use client";
import React from 'react';
-import { Github } from 'lucide-react';
export default function Hero() {
return (
-
-
+
+
文件快传
-
+
安全、快速、简单的传输服务
- 支持文件、文字、桌面共享 - 无需注册,即传即用
+ 支持文件、文字、桌面共享 - 无需注册,即传即用
- {/* GitHub开源链接 */}
-
+ {/* 分割线 */}
+
);
}
diff --git a/chuan-next/src/components/TextTransfer.tsx b/chuan-next/src/components/TextTransfer.tsx
index 2eecd87..a0a665a 100644
--- a/chuan-next/src/components/TextTransfer.tsx
+++ b/chuan-next/src/components/TextTransfer.tsx
@@ -512,32 +512,63 @@ export default function TextTransfer({
{mode === 'send' ? (
-
-
-
-
+
+ {/* 功能标题和状态 */}
+
+
+
+
+
+
+
传送文字
+
+ {isRoomCreated ? '实时编辑,对方可以同步看到' : '输入要传输的文本内容'}
+
+
-
传送文字
-
- {isRoomCreated ? '实时编辑,对方可以同步看到' : '输入要传输的文本内容'}
-
- {/* 连接状态显示 */}
-
- {isRoomCreated && (
-
-
-
-
- {isConnected ? '实时连接已建立' : '连接断开'}
-
-
- {connectedUsers > 0 && (
-
-
- {connectedUsers} 人在线
-
+
+ {/* 竖线分割 */}
+
+
+ {/* 状态显示 */}
+
+
连接状态
+
+ {/* WebSocket状态 */}
+
+ {isRoomCreated ? (
+ isConnected ? (
+ <>
+
+
WS
+ >
+ ) : (
+ <>
+
+
WS
+ >
+ )
+ ) : (
+ <>
+
+
WS
+ >
)}
+
+ {/* 分隔符 */}
+
|
+
+ {/* WebRTC状态 */}
+
+
+ {connectedUsers > 0 && (
+
+ {connectedUsers} 人在线
+
)}
@@ -690,33 +721,68 @@ export default function TextTransfer({
) : (
-
-
-
-
+
+ {/* 功能标题和状态 */}
+
+
+
+
+
+
+
加入房间
+
+ {(receivedText || textContent) ?
+ (isConnected ? '已连接,可以实时查看和编辑' : '连接断开,等待重连') :
+ '输入6位房间码来获取文字内容'
+ }
+
+
-
加入房间
-
输入6位房间码来获取文字内容
- {/* 连接状态显示 */}
- {(receivedText || textContent) && (
-
-
-
-
-
- {isConnected ? '实时连接已建立' : '连接断开'}
-
-
- {connectedUsers > 0 && (
-
-
- {connectedUsers} 人在线
-
+ {/* 竖线分割 */}
+
+
+ {/* 状态显示 */}
+
+
连接状态
+
+ {/* WebSocket状态 */}
+
+ {(receivedText || textContent) ? (
+ isConnected ? (
+ <>
+
+
WS
+ >
+ ) : (
+ <>
+
+
WS
+ >
+ )
+ ) : (
+ <>
+
+
WS
+ >
)}
+
+ {/* 分隔符 */}
+
|
+
+ {/* WebRTC状态 */}
+
- )}
+ {connectedUsers > 0 && (
+
+ {connectedUsers} 人在线
+
+ )}
+
diff --git a/chuan-next/src/components/WebRTCFileTransfer.tsx b/chuan-next/src/components/WebRTCFileTransfer.tsx
new file mode 100644
index 0000000..da7eac1
--- /dev/null
+++ b/chuan-next/src/components/WebRTCFileTransfer.tsx
@@ -0,0 +1,511 @@
+"use client";
+
+import React, { useState, useEffect, useRef, useCallback } from 'react';
+import { useSearchParams, useRouter } from 'next/navigation';
+import { useWebRTCTransfer } from '@/hooks/useWebRTCTransfer';
+import { Button } from '@/components/ui/button';
+import { toast } from '@/hooks/use-toast';
+import { Upload, Download } from 'lucide-react';
+import { WebRTCFileUpload } from '@/components/webrtc/WebRTCFileUpload';
+import { WebRTCFileReceive } from '@/components/webrtc/WebRTCFileReceive';
+
+interface FileInfo {
+ id: string;
+ name: string;
+ size: number;
+ type: string;
+ status: 'ready' | 'downloading' | 'completed';
+ progress: number;
+}
+
+export const WebRTCFileTransfer: React.FC = () => {
+ const searchParams = useSearchParams();
+ const router = useRouter();
+
+ // 独立的文件状态
+ const [selectedFiles, setSelectedFiles] = useState
([]);
+ const [fileList, setFileList] = useState([]);
+ const [downloadedFiles, setDownloadedFiles] = useState