"use client"; import React, { useState, useRef, useEffect, useCallback } from 'react'; import { useSharedWebRTCManager } from '@/hooks/webrtc/useSharedWebRTCManager'; import { useTextTransferBusiness } from '@/hooks/webrtc/useTextTransferBusiness'; import { useFileTransferBusiness } from '@/hooks/webrtc/useFileTransferBusiness'; 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'; interface WebRTCTextSenderProps { onRestart?: () => void; onPreviewImage?: (imageUrl: string) => void; onConnectionChange?: (connection: any) => void; } export const WebRTCTextSender: React.FC = ({ onRestart, onPreviewImage, onConnectionChange }) => { const { showToast } = useToast(); // 状态管理 const [pickupCode, setPickupCode] = useState(''); const [textInput, setTextInput] = useState(''); const [isTyping, setIsTyping] = useState(false); const [sentImages, setSentImages] = useState>([]); // Refs const fileInputRef = useRef(null); const textareaRef = useRef(null); const typingTimeoutRef = useRef(null); // 创建共享连接 const connection = useSharedWebRTCManager(); // 使用共享连接创建业务层 const textTransfer = useTextTransferBusiness(connection); const fileTransfer = useFileTransferBusiness(connection); // 连接所有传输通道 const connectAll = useCallback(async (code: string, role: 'sender' | 'receiver') => { console.log('=== 连接所有传输通道 ===', { code, role }); // 只需要连接一次,因为使用的是共享连接 await connection.connect(code, role); }, [connection]); // 是否有任何连接 const hasAnyConnection = textTransfer.isConnected || fileTransfer.isConnected; // 是否正在连接 const isAnyConnecting = textTransfer.isConnecting || fileTransfer.isConnecting; // 通知父组件连接状态变化 useEffect(() => { if (onConnectionChange) { onConnectionChange(connection); } }, [onConnectionChange, connection.isConnected, connection.isConnecting, connection.isPeerConnected]); // 是否有任何错误 const hasAnyError = textTransfer.connectionError || fileTransfer.connectionError; // 重新开始 const restart = () => { setPickupCode(''); setTextInput(''); setIsTyping(false); // 清理发送的图片URL sentImages.forEach(img => URL.revokeObjectURL(img.url)); setSentImages([]); // 断开连接(只需要断开一次) connection.disconnect(); if (onRestart) { onRestart(); } }; // 监听实时文本同步(发送方可以看到自己发的内容被对方接收) useEffect(() => { const cleanup = textTransfer.onTextSync((text: string) => { // 这里可以处理对方的实时文本,但通常发送方不需要监听自己发送的内容 console.log('收到对方的实时文本同步:', text); }); return cleanup; }, [textTransfer.onTextSync]); // 监听打字状态 useEffect(() => { const cleanup = textTransfer.onTypingStatus((typing: boolean) => { setIsTyping(typing); }); return cleanup; }, [textTransfer.onTypingStatus]); // 监听文件(图片)接收 useEffect(() => { const cleanup = fileTransfer.onFileReceived((fileData) => { if (fileData.file.type.startsWith('image/')) { // 只显示toast提示,不保存消息记录 showToast(`收到图片: ${fileData.file.name}`, "success"); } }); return cleanup; }, [fileTransfer.onFileReceived]); // 创建空房间 const createRoom = useCallback(async () => { try { console.log('=== 开始创建房间 ==='); const currentText = textInput.trim(); const response = await fetch('/api/create-room', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'message', initialText: currentText || '', hasImages: false, maxFileSize: 5 * 1024 * 1024, settings: { enableRealTimeText: true, enableImageTransfer: true } }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || '创建房间失败'); } const code = data.code; console.log('=== 房间创建成功 ===', code); setPickupCode(code); await connectAll(code, 'sender'); // 如果有初始文本,发送它 if (currentText) { setTimeout(() => { if (connection.isPeerConnected && textTransfer.isConnected) { // 发送实时文本同步 textTransfer.sendTextSync(currentText); // 重置自动调整高度 if (textareaRef.current) { textareaRef.current.style.height = '40px'; } } }, 1000); } showToast(`消息房间创建成功!取件码: ${code}`, "success"); } catch (error) { console.error('创建房间失败:', error); showToast(error instanceof Error ? error.message : '创建房间失败', "error"); } }, [textInput, connectAll, showToast, textTransfer]); // 处理文本输入变化(实时同步) const handleTextInputChange = (e: React.ChangeEvent) => { const value = e.target.value; setTextInput(value); // 自动调整高度 - 修复高度计算 const textarea = e.target; textarea.style.height = 'auto'; // 先重置为auto const newHeight = Math.min(Math.max(textarea.scrollHeight, 100), 300); // 最小100px,最大300px textarea.style.height = `${newHeight}px`; // 实时同步文本内容(如果P2P连接已建立) if (connection.isPeerConnected && textTransfer.isConnected) { // 发送实时文本同步 textTransfer.sendTextSync(value); // 发送打字状态 textTransfer.sendTypingStatus(value.length > 0); // 清除之前的定时器 if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current); } // 设置新的定时器来停止打字状态 if (value.length > 0) { typingTimeoutRef.current = setTimeout(() => { textTransfer.sendTypingStatus(false); }, 1000); // 缩短到1秒 } } }; // 处理图片发送(文件选择或粘贴) const handleImageSend = async (file: File) => { if (!file.type.startsWith('image/')) { showToast('请选择图片文件', "error"); return; } if (file.size > 5 * 1024 * 1024) { showToast('图片文件大小不能超过5MB', "error"); return; } // 创建预览URL并添加到显示列表 const imageUrl = URL.createObjectURL(file); const imageId = `img_${Date.now()}`; setSentImages(prev => [...prev, { id: imageId, url: imageUrl, fileName: file.name }]); // 发送文件 if (connection.isPeerConnected && fileTransfer.isConnected) { fileTransfer.sendFile(file); showToast('图片发送中...', "success"); } else if (!connection.isPeerConnected) { showToast('等待对方加入P2P网络...', "error"); } else { showToast('请先连接到房间', "error"); } }; // 处理图片选择 const handleImageSelect = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; handleImageSend(file); event.target.value = ''; }; // 处理键盘粘贴 const handlePaste = async (event: React.ClipboardEvent) => { const items = event.clipboardData?.items; if (!items) return; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.type.indexOf('image') !== -1) { event.preventDefault(); const file = item.getAsFile(); if (file) { await handleImageSend(file); } break; } } }; // 复制分享链接 const copyShareLink = () => { const baseUrl = window.location.origin + window.location.pathname; const shareLink = `${baseUrl}?type=message&mode=receive&code=${pickupCode}`; navigator.clipboard.writeText(shareLink).then(() => { showToast('分享链接已复制', "success"); }).catch(() => { showToast('复制失败,请手动复制', "error"); }); }; // 复制取件码 const copyCode = () => { navigator.clipboard.writeText(pickupCode); showToast("取件码已复制", "success"); }; const pickupLink = pickupCode ? `${typeof window !== 'undefined' ? window.location.origin : ''}?type=message&mode=receive&code=${pickupCode}` : ''; return (
{!pickupCode ? ( // 创建房间前的界面
{/* 功能标题和状态 */}

传送文字

输入要传输的文本内容

{/* 连接状态 */}

创建文字传输房间

创建房间后可以实时同步文字内容

) : ( // 房间已创建,显示取件码和文本传输界面
{/* 功能标题和状态 */} {/* 功能标题和状态 */}

发送文本

输入您想要传输的文本内容

{/* 文字编辑区域 - 移到最上面 */}

文字内容

{textInput.length} / 50,000 字符 {textTransfer.isConnected && (
WebRTC实时同步
)} {textTransfer.isWebSocketConnected && !textTransfer.isConnected && (
建立数据通道中
)}