190 lines
3.8 KiB
TypeScript
190 lines
3.8 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react';
|
|
import { wsClient } from './client';
|
|
import { WSMessage, WSMessageParams, MessageType } from './type';
|
|
|
|
interface UseWebSocketReturn {
|
|
// 状态
|
|
messages: WSMessage[];
|
|
currentRoom: string | null;
|
|
connecting: boolean;
|
|
|
|
// 方法
|
|
joinRoom: (roomId: string) => Promise<boolean>;
|
|
leaveRoom: () => Promise<boolean>;
|
|
sendMessage: (messageParams: WSMessageParams) => Promise<boolean>;
|
|
|
|
// 工具方法
|
|
clearMessages: () => void;
|
|
}
|
|
|
|
export const useWebSocket = (): UseWebSocketReturn => {
|
|
const [messages, setMessages] = useState<WSMessage[]>([]);
|
|
const [currentRoom, setCurrentRoom] = useState<string | null>(null);
|
|
const [connecting, setConnecting] = useState(false);
|
|
|
|
// 消息处理器
|
|
const messageHandler = useCallback((message: WSMessage) => {
|
|
setMessages((prev) => [...prev, message]);
|
|
}, []);
|
|
|
|
// 初始化 WebSocket 连接
|
|
useEffect(() => {
|
|
const initConnection = async () => {
|
|
try {
|
|
setConnecting(true);
|
|
await wsClient.connect();
|
|
const unsubscribe = wsClient.onMessage(messageHandler);
|
|
return unsubscribe;
|
|
} catch (error) {
|
|
console.error('连接失败:', error);
|
|
setMessages((prev) => [
|
|
...prev,
|
|
{
|
|
action: 'error',
|
|
data: {
|
|
text: '连接失败,请刷新页面重试',
|
|
type: MessageType.TEXT,
|
|
},
|
|
},
|
|
]);
|
|
} finally {
|
|
setConnecting(false);
|
|
}
|
|
};
|
|
|
|
const unsubscribePromise = initConnection();
|
|
|
|
return () => {
|
|
unsubscribePromise.then((unsubscribe) => unsubscribe?.());
|
|
wsClient.disconnect();
|
|
};
|
|
}, [messageHandler]);
|
|
|
|
// 加入房间
|
|
const joinRoom = async (roomId: string): Promise<boolean> => {
|
|
// 验证房间ID
|
|
if (!roomId.trim()) {
|
|
setMessages((prev) => [
|
|
...prev,
|
|
{
|
|
action: 'error',
|
|
data: {
|
|
text: '请输入有效的房间ID',
|
|
type: MessageType.TEXT,
|
|
},
|
|
},
|
|
]);
|
|
return false;
|
|
}
|
|
|
|
// 检查是否正在连接
|
|
if (connecting) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
setConnecting(true);
|
|
await wsClient.joinRoom(roomId.trim());
|
|
setCurrentRoom(roomId.trim());
|
|
setMessages([]); // 清空消息历史
|
|
return true;
|
|
} catch (error) {
|
|
console.error('加入房间失败:', error);
|
|
setMessages((prev) => [
|
|
...prev,
|
|
{
|
|
action: 'error',
|
|
data: {
|
|
text: '加入房间失败,请重试',
|
|
type: MessageType.TEXT,
|
|
},
|
|
},
|
|
]);
|
|
return false;
|
|
} finally {
|
|
setConnecting(false);
|
|
}
|
|
};
|
|
|
|
// 离开房间
|
|
const leaveRoom = async (): Promise<boolean> => {
|
|
if (connecting) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
await wsClient.disconnect();
|
|
setCurrentRoom(null);
|
|
setMessages([]); // 清空消息历史
|
|
return true;
|
|
} catch (error) {
|
|
console.error('离开房间失败:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// 发送消息
|
|
const sendMessage = async (messageParams: WSMessageParams): Promise<boolean> => {
|
|
// 验证消息内容
|
|
if (!messageParams.text?.trim() && !messageParams.fileUri) {
|
|
return false;
|
|
}
|
|
|
|
// 检查房间状态
|
|
if (!currentRoom) {
|
|
setMessages((prev) => [
|
|
...prev,
|
|
{
|
|
action: 'error',
|
|
data: {
|
|
text: '请先加入房间',
|
|
type: MessageType.TEXT,
|
|
},
|
|
},
|
|
]);
|
|
return false;
|
|
}
|
|
|
|
// 检查连接状态
|
|
if (connecting) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
await wsClient.sendMessage(messageParams);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('发送消息失败:', error);
|
|
setMessages((prev) => [
|
|
...prev,
|
|
{
|
|
action: 'error',
|
|
data: {
|
|
text: '发送消息失败,请重试',
|
|
type: MessageType.TEXT,
|
|
},
|
|
},
|
|
]);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// 清空消息
|
|
const clearMessages = useCallback(() => {
|
|
setMessages([]);
|
|
}, []);
|
|
|
|
return {
|
|
// 状态
|
|
messages,
|
|
currentRoom,
|
|
connecting,
|
|
// 方法
|
|
joinRoom,
|
|
leaveRoom,
|
|
sendMessage,
|
|
// 工具方法
|
|
clearMessages,
|
|
};
|
|
};
|