fenghuo/apps/web/app/websocket/page.tsx

162 lines
4.5 KiB
TypeScript
Raw Normal View History

2025-05-27 15:46:34 +08:00
'use client';
import { useHello, useTRPC, useWebSocket, MessageType } from '@repo/client';
import { useQuery } from '@tanstack/react-query';
import { useRef, useState } from 'react';
export default function WebSocketPage() {
const trpc = useTRPC();
const { data, isLoading } = useQuery(trpc.user.getUser.queryOptions());
const [message, setMessage] = useState('');
const [roomId, setRoomId] = useState('');
const messagesEndRef = useRef<HTMLDivElement>(null);
// 使用 WebSocket hook
const { messages, currentRoom, connecting, joinRoom, leaveRoom, sendMessage } = useWebSocket();
// 滚动到底部
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
setTimeout(scrollToBottom, 100);
};
const handleJoinRoom = async () => {
const success = await joinRoom(roomId.trim());
if (success) {
setRoomId('');
}
};
const handleLeaveRoom = async () => {
await leaveRoom();
};
const handleSendMessage = async () => {
const success = await sendMessage({
text: message,
type: MessageType.TEXT,
});
if (success) {
setMessage('');
}
};
// 处理按回车发送消息
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">WebSocket </h1>
{/* 房间管理 */}
<div className="mb-6 p-4 border rounded">
<h2 className="text-xl mb-2"></h2>
{!currentRoom ? (
<div className="flex gap-2">
<input
type="text"
value={roomId}
onChange={(e) => setRoomId(e.target.value)}
onKeyPress={handleKeyPress}
disabled={connecting}
className="border border-gray-300 rounded px-3 py-2 flex-1"
placeholder="输入房间ID..."
/>
<button
onClick={handleJoinRoom}
disabled={connecting}
className={`px-4 py-2 rounded text-white ${
connecting ? 'bg-gray-400 cursor-not-allowed' : 'bg-green-500 hover:bg-green-600'
}`}
>
{connecting ? '连接中...' : '加入房间'}
</button>
</div>
) : (
<div className="flex gap-2 items-center">
<span>: {currentRoom}</span>
<button
onClick={handleLeaveRoom}
disabled={connecting}
className={`px-4 py-2 rounded text-white ${
connecting ? 'bg-gray-400 cursor-not-allowed' : 'bg-red-500 hover:bg-red-600'
}`}
>
</button>
</div>
)}
</div>
{/* 消息显示区域 */}
{currentRoom && (
<div className="mb-4">
<div className="border rounded p-4 h-[400px] overflow-y-auto mb-4 bg-gray-50">
{messages.map((msg, index) => (
<div
key={index}
className={`mb-2 p-2 rounded ${
msg.action === 'system'
? 'bg-gray-200 text-gray-700'
: msg.action === 'error'
? 'bg-red-100 text-red-700'
: 'bg-blue-100 text-blue-700'
}`}
>
<div className="flex items-center gap-2">
{msg.data.type === MessageType.IMAGE && msg.data.fileUri && (
<img src={msg.data.fileUri} alt="图片消息" className="max-w-xs" />
)}
{msg.data.type === MessageType.FILE && msg.data.fileUri && (
<a
href={msg.data.fileUri}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline"
>
</a>
)}
{msg.data.text && <div>{msg.data.text}</div>}
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="flex gap-2">
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={handleKeyPress}
disabled={connecting}
className="border border-gray-300 rounded px-3 py-2 flex-1"
placeholder="输入消息..."
/>
<button
onClick={handleSendMessage}
disabled={connecting}
className={`px-4 py-2 rounded text-white ${
connecting ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-500 hover:bg-blue-600'
}`}
>
</button>
</div>
</div>
)}
{!currentRoom && (
<div>
<p className="text-gray-600">提示: 请先加入一个房间开始聊天</p>
<p className="text-gray-600">ID来测试房间内通信</p>
</div>
)}
</div>
);
}