feat: 调整UI/RTC逻辑

This commit is contained in:
MatrixSeven
2025-08-04 21:35:50 +08:00
parent 324408f6b2
commit ef02e88ee9
22 changed files with 1042 additions and 285 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import React from 'react';
import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Upload, MessageSquare, Monitor, Github, ExternalLink } from 'lucide-react';
import Hero from '@/components/Hero';
@@ -10,6 +11,21 @@ import { WebRTCFileTransfer } from '@/components/WebRTCFileTransfer';
import { Button } from '@/components/ui/button';
export default function HomePage() {
const searchParams = useSearchParams();
const [activeTab, setActiveTab] = useState('webrtc');
const [hasInitialized, setHasInitialized] = useState(false);
// 根据URL参数设置初始标签仅首次加载时
useEffect(() => {
if (!hasInitialized) {
const urlType = searchParams.get('type');
if (urlType && ['webrtc', 'text', 'desktop'].includes(urlType)) {
setActiveTab(urlType);
}
setHasInitialized(true);
}
}, [searchParams, hasInitialized]);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50">
<div className="container mx-auto px-4 py-4 sm:py-6 md:py-8">
@@ -20,7 +36,7 @@ export default function HomePage() {
{/* Main Content */}
<div className="max-w-4xl mx-auto">
<Tabs defaultValue="webrtc" className="w-full">
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
{/* Tabs Navigation - 横向布局 */}
<div className="mb-6">
<TabsList className="grid w-full grid-cols-3 max-w-lg mx-auto h-auto bg-white/90 backdrop-blur-sm shadow-lg rounded-xl p-2 border border-slate-200">

View File

@@ -0,0 +1,38 @@
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(
{ success: false, message: '取件码不能为空' },
{ status: 400 }
);
}
console.log('API Route: Getting WebRTC room status, proxying to:', `${GO_BACKEND_URL}/api/webrtc-room-status?code=${code}`);
const response = await fetch(`${GO_BACKEND_URL}/api/webrtc-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(
{ success: false, message: '获取房间状态失败', details: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
);
}
}

View File

@@ -36,6 +36,7 @@ export const WebRTCFileTransfer: React.FC = () => {
// 房间状态
const [pickupCode, setPickupCode] = useState('');
const [mode, setMode] = useState<'send' | 'receive'>('send');
const [hasProcessedInitialUrl, setHasProcessedInitialUrl] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const {
@@ -54,27 +55,43 @@ export const WebRTCFileTransfer: React.FC = () => {
onFileProgress
} = useWebRTCTransfer();
// 从URL参数中获取初始模式
// 从URL参数中获取初始模式(仅在首次加载时处理)
useEffect(() => {
const urlMode = searchParams.get('mode') as 'send' | 'receive';
const type = searchParams.get('type');
const code = searchParams.get('code');
if (type === 'webrtc' && urlMode && ['send', 'receive'].includes(urlMode)) {
// 只在首次加载且URL中有webrtc类型时处理
if (!hasProcessedInitialUrl && type === 'webrtc' && urlMode && ['send', 'receive'].includes(urlMode)) {
console.log('=== 处理初始URL参数 ===');
console.log('URL模式:', urlMode, '类型:', type, '取件码:', code);
setMode(urlMode);
setHasProcessedInitialUrl(true);
if (code && urlMode === 'receive') {
// 自动加入房间
// 自动加入房间,使用房间状态检查
console.log('URL中有取件码自动加入房间');
joinRoom(code);
}
}
}, [searchParams]);
}, [searchParams, hasProcessedInitialUrl]);
// 更新URL参数
const updateMode = useCallback((newMode: 'send' | 'receive') => {
console.log('=== 手动切换模式 ===');
console.log('新模式:', newMode);
setMode(newMode);
const params = new URLSearchParams(searchParams.toString());
params.set('type', 'webrtc');
params.set('mode', newMode);
// 如果切换到发送模式移除code参数
if (newMode === 'send') {
params.delete('code');
}
router.push(`?${params.toString()}`, { scroll: false });
}, [searchParams, router]);
@@ -164,14 +181,46 @@ export const WebRTCFileTransfer: React.FC = () => {
};
// 加入房间 (接收模式)
const joinRoom = (code: string) => {
const joinRoom = async (code: string) => {
console.log('=== 加入房间 ===');
console.log('取件码:', code);
setPickupCode(code.trim());
connect(code.trim(), 'receiver');
const trimmedCode = code.trim();
showToast(`正在连接到房间: ${code}`, "info");
// 检查取件码格式
if (!trimmedCode || trimmedCode.length !== 6) {
showToast('请输入正确的6位取件码', "error");
return;
}
try {
// 先检查房间状态
console.log('检查房间状态...');
showToast('正在检查房间状态...', "info");
const response = await fetch(`/api/webrtc-room-status?code=${trimmedCode}`);
const result = await response.json();
if (!result.success) {
showToast(result.message || '房间不存在或已过期', "error");
return;
}
// 检查发送方是否在线
if (!result.sender_online) {
showToast('发送方不在线,请确认取件码是否正确', "error");
return;
}
console.log('房间状态检查通过,开始连接...');
setPickupCode(trimmedCode);
connect(trimmedCode, 'receiver');
showToast(`正在连接到房间: ${trimmedCode}`, "info");
} catch (error) {
console.error('检查房间状态失败:', error);
showToast('检查房间状态失败,请重试', "error");
}
};
// 重置连接状态 (用于连接失败后重新输入)

View File

@@ -1,5 +1,5 @@
import { useState, useCallback, useRef } from 'react';
import { FileInfo, TransferProgress } from '@/types';
import { useState, useCallback } from 'react';
import { TransferProgress } from '@/types';
import { useToast } from '@/components/ui/toast-simple';
interface FileTransferData {

View File

@@ -20,9 +20,9 @@ export function useWebRTCTransfer() {
// 设置数据通道消息处理
useEffect(() => {
const dataChannel = connection.getDataChannel();
const dataChannel = connection.localDataChannel || connection.remoteDataChannel;
if (dataChannel && dataChannel.readyState === 'open') {
console.log('设置数据通道消息处理器');
console.log('设置数据通道消息处理器, 通道类型:', connection.localDataChannel ? '本地' : '远程');
// 扩展消息处理以包含文件列表
const originalHandler = fileTransfer.handleMessage;
@@ -50,7 +50,7 @@ export function useWebRTCTransfer() {
originalHandler(event);
};
}
}, [connection.isConnected, connection.getDataChannel, fileTransfer.handleMessage]);
}, [connection.localDataChannel, connection.remoteDataChannel, fileTransfer.handleMessage]);
// 发送文件
const sendFile = useCallback((file: File, fileId?: string) => {

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
* 用于在静态导出模式下直接与 Go 后端通信
*/
import { config, getApiUrl, getDirectBackendUrl } from './config';
import { config } from './config';
interface ApiResponse {
success: boolean;

View File

@@ -27,11 +27,16 @@ const getCurrentBaseUrl = () => {
// 动态获取 WebSocket URL
const getCurrentWsUrl = () => {
if (typeof window !== 'undefined') {
// 在开发模式下始终使用后端的WebSocket地址
if (window.location.hostname === 'localhost' && (window.location.port === '3000' || window.location.port === '3001')) {
// 检查是否是 Next.js 开发服务器(端口 3000 或 3001
const isNextDevServer = window.location.hostname === 'localhost' &&
(window.location.port === '3000' || window.location.port === '3001');
if (isNextDevServer) {
// 开发模式:通过 Next.js 开发服务器访问,连接到后端 WebSocket
return 'ws://localhost:8080/ws/p2p';
}
// 在生产模式下,使用当前域名
// 生产模式或通过 Go 服务器访问:使用当前域名和端口
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
return `${protocol}//${window.location.host}/ws/p2p`;
}

View File

@@ -27,7 +27,9 @@ export const apiRoutes = [
// 客户端API配置用于静态导出时的客户端请求
export const clientApiConfig = {
// 直接连接到 Go 后端
baseUrl: 'http://localhost:8080', // 构建时可通过环境变量替换
wsUrl: 'ws://localhost:8080/ws', // 构建时可通过环境变量替换
// 直接连接到 Go 后端 - 动态获取当前域名
baseUrl: typeof window !== 'undefined' ? window.location.origin : '',
wsUrl: typeof window !== 'undefined'
? `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`
: '',
}