From 3a4a762cc9f522196db485d65db34bc06930f853 Mon Sep 17 00:00:00 2001 From: MatrixSeven Date: Sat, 2 Aug 2025 21:56:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:UI=E5=A4=A7=E8=B0=83=E6=95=B4,WEBRTC?= =?UTF-8?q?=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chuan-next/src/app/HomePage-new.tsx | 293 ++-------- chuan-next/src/app/api/room-info/route.ts | 38 -- chuan-next/src/app/api/room-status/route.ts | 38 -- chuan-next/src/app/api/update-files/route.ts | 31 -- chuan-next/src/components/DesktopShare.tsx | 123 ++++- chuan-next/src/components/FileReceive.tsx | 266 --------- chuan-next/src/components/FileTransfer.tsx | 135 ----- chuan-next/src/components/Hero.tsx | 31 +- chuan-next/src/components/TextTransfer.tsx | 156 ++++-- .../src/components/WebRTCFileTransfer.tsx | 511 ++++++++++++++++++ .../components/webrtc/WebRTCFileReceive.tsx | 438 +++++++++++++++ .../WebRTCFileUpload.tsx} | 150 ++++- chuan-next/src/hooks/useRoomManager.ts | 183 ------- chuan-next/src/hooks/useWebRTCTransfer.new.ts | 145 +++++ chuan-next/src/hooks/useWebRTCTransfer.ts | 146 +++++ chuan-next/src/hooks/useWebSocket.ts | 157 ------ .../src/hooks/webrtc/useFileTransfer.ts | 299 ++++++++++ .../src/hooks/webrtc/useWebRTCConnection.ts | 203 +++++++ chuan-next/src/lib/config.ts | 2 +- cmd/main.go | 8 +- internal/handlers/handlers.go | 138 +---- internal/services/webrtc_service.go | 271 ++++++---- internal/web/frontend.go | 6 +- 23 files changed, 2307 insertions(+), 1461 deletions(-) delete mode 100644 chuan-next/src/app/api/room-info/route.ts delete mode 100644 chuan-next/src/app/api/room-status/route.ts delete mode 100644 chuan-next/src/app/api/update-files/route.ts delete mode 100644 chuan-next/src/components/FileReceive.tsx delete mode 100644 chuan-next/src/components/FileTransfer.tsx create mode 100644 chuan-next/src/components/WebRTCFileTransfer.tsx create mode 100644 chuan-next/src/components/webrtc/WebRTCFileReceive.tsx rename chuan-next/src/components/{FileUpload.tsx => webrtc/WebRTCFileUpload.tsx} (72%) delete mode 100644 chuan-next/src/hooks/useRoomManager.ts create mode 100644 chuan-next/src/hooks/useWebRTCTransfer.new.ts create mode 100644 chuan-next/src/hooks/useWebRTCTransfer.ts delete mode 100644 chuan-next/src/hooks/useWebSocket.ts create mode 100644 chuan-next/src/hooks/webrtc/useFileTransfer.ts create mode 100644 chuan-next/src/hooks/webrtc/useWebRTCConnection.ts 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状态 */} +
+
+ WS +
+ + {/* 分隔符 */} +
|
+ + {/* WebRTC状态 */} +
+ {isSharing ? ( + <> +
+ RTC + + ) : ( + <> +
+ RTC + + )} +
+
+ {isSharing && connectionCode && ( +
+ {connectionCode} +
+ )}
-

共享桌面

-

- {isSharing ? '桌面共享进行中' : '开始共享您的桌面屏幕'} -

@@ -202,15 +245,63 @@ export default function DesktopShare({ onStartSharing, onStopSharing, onJoinShar
) : ( -
-
-
- +
+ {/* 功能标题和状态 */} +
+
+
+ +
+
+

观看桌面

+

+ {isViewing ? '正在观看桌面共享' : '输入连接码观看他人的桌面'} +

+
+
+ + {/* 竖线分割 */} +
+ + {/* 状态显示 */} +
+
连接状态
+
+ {/* WebSocket状态 */} +
+
+ WS +
+ + {/* 分隔符 */} +
|
+ + {/* 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