diff --git a/apps/backend/src/socket.ts b/apps/backend/src/socket.ts index 2ff3db4..27c5269 100644 --- a/apps/backend/src/socket.ts +++ b/apps/backend/src/socket.ts @@ -1,13 +1,22 @@ import { createBunWebSocket } from 'hono/bun'; import type { ServerWebSocket } from 'bun'; import { WSContext } from 'hono/ws'; -import { MessageType } from '../../../packages/client/src/websocket/type'; - +enum MessageType { + TEXT = 'text', + IMAGE = 'image', + FILE = 'file', + RETRACT = 'retract', // 用于实现撤回 +} +interface WSMessageParams { + text?: string; + type?: MessageType; + fileUri?: string; +} // 定义消息类型接口 interface WSMessage { type: 'join' | 'leave' | 'message'; roomId: string; - data: any; + data: WSMessageParams; } // 创建 WebSocket 实例并指定泛型类型 @@ -27,7 +36,7 @@ const wsHandler = upgradeWebSocket((c) => { }, onMessage(message, ws) { try { - const parsedMessage: WSMessage = JSON.parse(message.data); + const parsedMessage: WSMessage = JSON.parse(message.data as any); console.log('收到消息:', parsedMessage); switch (parsedMessage.type) { diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 05d8458..b235c7d 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,161 +1,28 @@ 'use client'; -import { useHello, useTRPC, useWebSocket, MessageType } from '@repo/client'; +import { useHello, useTRPC } from '@repo/client'; import { useQuery } from '@tanstack/react-query'; -import { useRef, useState } from 'react'; +import Link from 'next/link'; export default function Home() { const trpc = useTRPC(); const { data, isLoading } = useQuery(trpc.user.getUser.queryOptions()); - const [message, setMessage] = useState(''); - const [roomId, setRoomId] = useState(''); - const messagesEndRef = useRef(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 (
-

WebSocket 房间测试

- - {/* 房间管理 */} -
-

房间管理

- {!currentRoom ? ( -
- setRoomId(e.target.value)} - onKeyPress={handleKeyPress} - disabled={connecting} - className="border border-gray-300 rounded px-3 py-2 flex-1" - placeholder="输入房间ID..." - /> - -
- ) : ( -
- 当前房间: {currentRoom} - -
- )} +

首页

+
+
+

功能导航

+
    +
  • + + WebSocket 聊天室 + +
  • + {/* 可以在这里添加更多功能链接 */} +
+
- - {/* 消息显示区域 */} - {currentRoom && ( -
-
- {messages.map((msg, index) => ( -
-
- {msg.data.type === MessageType.IMAGE && msg.data.fileUri && ( - 图片消息 - )} - {msg.data.type === MessageType.FILE && msg.data.fileUri && ( - - 下载文件 - - )} - {msg.data.text &&
{msg.data.text}
} -
-
- ))} -
-
-
- setMessage(e.target.value)} - onKeyPress={handleKeyPress} - disabled={connecting} - className="border border-gray-300 rounded px-3 py-2 flex-1" - placeholder="输入消息..." - /> - -
-
- )} - - {!currentRoom && ( -
-

提示: 请先加入一个房间开始聊天

-

打开多个浏览器窗口,输入相同的房间ID来测试房间内通信

-
- )}
); } diff --git a/apps/web/app/websocket/page.tsx b/apps/web/app/websocket/page.tsx new file mode 100644 index 0000000..b044baa --- /dev/null +++ b/apps/web/app/websocket/page.tsx @@ -0,0 +1,161 @@ +'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(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 ( +
+

WebSocket 房间测试

+ + {/* 房间管理 */} +
+

房间管理

+ {!currentRoom ? ( +
+ setRoomId(e.target.value)} + onKeyPress={handleKeyPress} + disabled={connecting} + className="border border-gray-300 rounded px-3 py-2 flex-1" + placeholder="输入房间ID..." + /> + +
+ ) : ( +
+ 当前房间: {currentRoom} + +
+ )} +
+ + {/* 消息显示区域 */} + {currentRoom && ( +
+
+ {messages.map((msg, index) => ( +
+
+ {msg.data.type === MessageType.IMAGE && msg.data.fileUri && ( + 图片消息 + )} + {msg.data.type === MessageType.FILE && msg.data.fileUri && ( + + 下载文件 + + )} + {msg.data.text &&
{msg.data.text}
} +
+
+ ))} +
+
+
+ setMessage(e.target.value)} + onKeyPress={handleKeyPress} + disabled={connecting} + className="border border-gray-300 rounded px-3 py-2 flex-1" + placeholder="输入消息..." + /> + +
+
+ )} + + {!currentRoom && ( +
+

提示: 请先加入一个房间开始聊天

+

打开多个浏览器窗口,输入相同的房间ID来测试房间内通信

+
+ )} +
+ ); +}