feat:降级

This commit is contained in:
MatrixSeven
2025-09-15 18:28:16 +08:00
parent 50d30f23bf
commit 550be8bcc6
29 changed files with 1189 additions and 431 deletions

View File

@@ -1,12 +1,13 @@
"use client";
import React, { useState, useCallback, useRef } from 'react';
import { ConnectionStatus } from '@/components/ConnectionStatus';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Download, FileText, Image, Video, Music, Archive, Clock, Zap } from 'lucide-react';
import { useToast } from '@/components/ui/toast-simple';
import { ConnectionStatus } from '@/components/ConnectionStatus';
import { TransferProgressTracker, formatTransferSpeed, formatTime } from '@/lib/transfer-utils';
import { useReadConnectState } from '@/hooks/connection/state/useWebConnectStateManager';
import { TransferProgressTracker } from '@/lib/transfer-utils';
import { Archive, Clock, Download, FileText, Image, Music, Video, Zap } from 'lucide-react';
import React, { useCallback, useRef, useState } from 'react';
interface FileInfo {
id: string;
@@ -39,9 +40,6 @@ interface WebRTCFileReceiveProps {
onJoinRoom: (code: string) => void;
files: FileInfo[];
onDownloadFile: (fileId: string) => void;
isConnected: boolean;
isConnecting: boolean;
isWebSocketConnected?: boolean;
downloadedFiles?: Map<string, File>;
error?: string | null;
onReset?: () => void;
@@ -52,9 +50,6 @@ export function WebRTCFileReceive({
onJoinRoom,
files,
onDownloadFile,
isConnected,
isConnecting,
isWebSocketConnected = false,
downloadedFiles,
error = null,
onReset,
@@ -64,12 +59,16 @@ export function WebRTCFileReceive({
const [isValidating, setIsValidating] = useState(false);
const { showToast } = useToast();
// 用于跟踪传输进度的trackers
const transferTrackers = useRef<Map<string, TransferProgressTracker>>(new Map());
// 使用传入的取件码或本地状态的取件码
const displayPickupCode = propPickupCode || pickupCode;
const { getConnectState } = useReadConnectState();
// 验证取件码是否存在
const validatePickupCode = async (code: string): Promise<boolean> => {
try {
@@ -146,7 +145,7 @@ export function WebRTCFileReceive({
// 当验证失败时重置输入状态
React.useEffect(() => {
if (error && !isConnecting && !isConnected && !isValidating) {
if (error && !getConnectState().isConnecting && !getConnectState().isConnected && !isValidating) {
// 延迟重置,确保用户能看到错误信息
const timer = setTimeout(() => {
console.log('重置取件码输入');
@@ -155,10 +154,10 @@ export function WebRTCFileReceive({
return () => clearTimeout(timer);
}
}, [error, isConnecting, isConnected, isValidating]);
}, [error, getConnectState, isValidating]);
// 如果已经连接但没有文件,显示等待界面
if ((isConnected || isConnecting) && files.length === 0) {
if ((getConnectState().isConnected || getConnectState().isConnecting) && files.length === 0) {
return (
<div>
{/* 功能标题和状态 */}
@@ -168,6 +167,7 @@ export function WebRTCFileReceive({
<Download className="w-5 h-5 text-white" />
</div>
<div>
{getConnectState().isWebSocketConnected}
<h3 className="text-lg font-semibold text-slate-800"></h3>
<p className="text-sm text-slate-600">: {displayPickupCode}</p>
</div>
@@ -190,9 +190,9 @@ export function WebRTCFileReceive({
{/* 连接状态指示器 */}
<div className="flex items-center justify-center space-x-4 mb-6">
<div className="flex items-center">
<div className={`w-3 h-3 rounded-full mr-2 ${isConnected ? 'bg-emerald-500 animate-pulse' : 'bg-orange-500 animate-spin'}`}></div>
<span className={`text-sm font-medium ${isConnected ? 'text-emerald-600' : 'text-orange-600'}`}>
{isConnected ? '连接已建立' : '连接中...'}
<div className={`w-3 h-3 rounded-full mr-2 ${getConnectState().isConnected ? 'bg-emerald-500 animate-pulse' : 'bg-orange-500 animate-spin'}`}></div>
<span className={`text-sm font-medium ${getConnectState().isConnected ? 'text-emerald-600' : 'text-orange-600'}`}>
{getConnectState().isConnected ? '连接已建立' : '连接中...'}
</span>
</div>
</div>
@@ -326,7 +326,7 @@ export function WebRTCFileReceive({
</div>
<Button
onClick={() => onDownloadFile(file.id)}
disabled={!isConnected || isDownloading}
disabled={!getConnectState().isConnected || isDownloading}
className={`px-6 py-2 rounded-lg font-medium shadow-lg transition-all duration-200 hover:shadow-xl ${
hasDownloadedFile
? 'bg-gradient-to-r from-blue-500 to-indigo-500 hover:from-blue-600 hover:to-indigo-600 text-white'
@@ -399,7 +399,7 @@ export function WebRTCFileReceive({
placeholder="请输入取件码"
className="text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-emerald-500 focus:ring-emerald-500 bg-white/80 backdrop-blur-sm pb-2 sm:pb-4"
maxLength={6}
disabled={isValidating || isConnecting}
disabled={isValidating || getConnectState().isConnecting}
/>
<div className="absolute inset-x-0 -bottom-4 sm:-bottom-6 flex justify-center space-x-1 sm:space-x-2">
{[...Array(6)].map((_, i) => (
@@ -423,14 +423,14 @@ export function WebRTCFileReceive({
<Button
type="submit"
className="w-full h-10 sm:h-12 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white text-base sm:text-lg font-medium rounded-xl shadow-lg transition-all duration-200 hover:shadow-xl hover:scale-105 disabled:opacity-50 disabled:scale-100"
disabled={pickupCode.length !== 6 || isValidating || isConnecting}
disabled={pickupCode.length !== 6 || isValidating || getConnectState().isConnecting}
>
{isValidating ? (
<div className="flex items-center space-x-2">
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
<span>...</span>
</div>
) : isConnecting ? (
) : getConnectState().isConnecting ? (
<div className="flex items-center space-x-2">
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
<span>...</span>

View File

@@ -1,14 +1,14 @@
"use client";
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { useSharedWebRTCManager } from '@/hooks/connection';
import { useTextTransferBusiness } from '@/hooks/text-transfer';
import { useFileTransferBusiness } from '@/hooks/file-transfer';
import { ConnectionStatus } from '@/components/ConnectionStatus';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useToast } from '@/components/ui/toast-simple';
import { MessageSquare, Image, Download } from 'lucide-react';
import { ConnectionStatus } from '@/components/ConnectionStatus';
import { useSharedWebRTCManager } from '@/hooks/connection';
import { useFileTransferBusiness } from '@/hooks/file-transfer';
import { useTextTransferBusiness } from '@/hooks/text-transfer';
import { Download, Image, MessageSquare } from 'lucide-react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
interface WebRTCTextReceiverProps {
initialCode?: string;
@@ -38,6 +38,8 @@ export const WebRTCTextReceiver: React.FC<WebRTCTextReceiverProps> = ({
// 创建共享连接
const connection = useSharedWebRTCManager();
const {getConnectState} = connection;
// 使用共享连接创建业务层
const textTransfer = useTextTransferBusiness(connection);
@@ -61,7 +63,7 @@ export const WebRTCTextReceiver: React.FC<WebRTCTextReceiverProps> = ({
if (onConnectionChange) {
onConnectionChange(connection);
}
}, [onConnectionChange, connection.isConnected, connection.isConnecting, connection.isPeerConnected]);
}, [onConnectionChange, getConnectState().isConnected, getConnectState().isConnecting, getConnectState().isPeerConnected]);
// 是否有任何错误
const hasAnyError = textTransfer.connectionError || fileTransfer.connectionError;
@@ -336,7 +338,7 @@ export const WebRTCTextReceiver: React.FC<WebRTCTextReceiverProps> = ({
<div className="flex flex-col items-center justify-center h-full text-slate-400 space-y-3">
<MessageSquare className="w-12 h-12 text-slate-300" />
<p className="text-center">
{connection.isPeerConnected ?
{getConnectState().isPeerConnected ?
'等待对方发送文字内容...' :
'等待连接建立...'}
</p>

View File

@@ -1,14 +1,14 @@
"use client";
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { useSharedWebRTCManager } from '@/hooks/connection';
import { useTextTransferBusiness } from '@/hooks/text-transfer';
import { useFileTransferBusiness } from '@/hooks/file-transfer';
import { ConnectionStatus } from '@/components/ConnectionStatus';
import RoomInfoDisplay from '@/components/RoomInfoDisplay';
import { Button } from '@/components/ui/button';
import { useToast } from '@/components/ui/toast-simple';
import { MessageSquare, Image, Send, Copy } from 'lucide-react';
import RoomInfoDisplay from '@/components/RoomInfoDisplay';
import { ConnectionStatus } from '@/components/ConnectionStatus';
import { useSharedWebRTCManager } from '@/hooks/connection';
import { useFileTransferBusiness } from '@/hooks/file-transfer';
import { useTextTransferBusiness } from '@/hooks/text-transfer';
import { Image, MessageSquare, Send } from 'lucide-react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
interface WebRTCTextSenderProps {
onRestart?: () => void;
@@ -32,7 +32,9 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
// 创建共享连接
const connection = useSharedWebRTCManager();
const { getConnectState } = connection;
// 使用共享连接创建业务层
const textTransfer = useTextTransferBusiness(connection);
const fileTransfer = useFileTransferBusiness(connection);
@@ -43,9 +45,6 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
// 只需要连接一次,因为使用的是共享连接
await connection.connect(code, role);
}, [connection]);
// 是否有任何连接
const hasAnyConnection = textTransfer.isConnected || fileTransfer.isConnected;
// 是否正在连接
const isAnyConnecting = textTransfer.isConnecting || fileTransfer.isConnecting;
@@ -55,10 +54,8 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
if (onConnectionChange) {
onConnectionChange(connection);
}
}, [onConnectionChange, connection.isConnected, connection.isConnecting, connection.isPeerConnected]);
}, [onConnectionChange, getConnectState().isConnected, getConnectState().isConnecting, getConnectState().isPeerConnected]);
// 是否有任何错误
const hasAnyError = textTransfer.connectionError || fileTransfer.connectionError;
// 重新开始
const restart = () => {
@@ -140,7 +137,7 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
// 如果有初始文本,发送它
if (currentText) {
setTimeout(() => {
if (connection.isPeerConnected && textTransfer.isConnected) {
if (getConnectState().isPeerConnected && textTransfer.isConnected) {
// 发送实时文本同步
textTransfer.sendTextSync(currentText);
@@ -171,7 +168,7 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
textarea.style.height = `${newHeight}px`;
// 实时同步文本内容如果P2P连接已建立
if (connection.isPeerConnected && textTransfer.isConnected) {
if (getConnectState().isPeerConnected && textTransfer.isConnected) {
// 发送实时文本同步
textTransfer.sendTextSync(value);
@@ -214,10 +211,10 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
}]);
// 发送文件
if (connection.isPeerConnected && fileTransfer.isConnected) {
if (getConnectState().isPeerConnected && fileTransfer.isConnected) {
fileTransfer.sendFile(file);
showToast('图片发送中...', "success");
} else if (!connection.isPeerConnected) {
} else if (!getConnectState().isPeerConnected) {
showToast('等待对方加入P2P网络...', "error");
} else {
showToast('请先连接到房间', "error");
@@ -362,19 +359,18 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
)}
</div>
</div>
<textarea
ref={textareaRef}
value={textInput}
onChange={handleTextInputChange}
onPaste={handlePaste}
disabled={!connection.isPeerConnected}
placeholder={connection.isPeerConnected
disabled={!getConnectState().isPeerConnected}
placeholder={getConnectState().isPeerConnected
? "在这里编辑文字内容...&#10;&#10;💡 支持实时同步编辑,对方可以看到你的修改&#10;💡 可以直接粘贴图片 (Ctrl+V)"
: "等待对方加入P2P网络...&#10;&#10;📡 建立连接后即可开始输入文字"
}
className={`w-full h-40 px-4 py-3 border rounded-lg resize-none text-slate-700 ${
connection.isPeerConnected
getConnectState().isPeerConnected
? "border-slate-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent placeholder-slate-400"
: "border-slate-200 bg-slate-50 cursor-not-allowed placeholder-slate-300"
}`}
@@ -386,9 +382,9 @@ export const WebRTCTextSender: React.FC<WebRTCTextSenderProps> = ({ onRestart, o
onClick={() => fileInputRef.current?.click()}
variant="outline"
size="sm"
disabled={!connection.isPeerConnected}
disabled={!getConnectState().isPeerConnected}
className={`flex items-center space-x-1 ${
!connection.isPeerConnected ? 'cursor-not-allowed opacity-50' : ''
!getConnectState().isPeerConnected ? 'cursor-not-allowed opacity-50' : ''
}`}
>
<Image className="w-4 h-4" />