56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
|
|
type MessageHandler = (data: Record<string, unknown>) => void;
|
||
|
|
|
||
|
|
class ChatSocket {
|
||
|
|
private ws: WebSocket | null = null;
|
||
|
|
private handlers: Map<string, MessageHandler[]> = new Map();
|
||
|
|
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||
|
|
|
||
|
|
connect() {
|
||
|
|
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||
|
|
const url = `${protocol}//${location.host}/ws`;
|
||
|
|
this.ws = new WebSocket(url);
|
||
|
|
|
||
|
|
this.ws.onopen = () => {
|
||
|
|
this.emit('connected', {});
|
||
|
|
};
|
||
|
|
|
||
|
|
this.ws.onmessage = (event) => {
|
||
|
|
try {
|
||
|
|
const { type, data } = JSON.parse(event.data);
|
||
|
|
this.emit(type, data);
|
||
|
|
} catch {}
|
||
|
|
};
|
||
|
|
|
||
|
|
this.ws.onclose = () => {
|
||
|
|
this.reconnectTimer = setTimeout(() => this.connect(), 3000);
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
on(type: string, handler: MessageHandler) {
|
||
|
|
if (!this.handlers.has(type)) this.handlers.set(type, []);
|
||
|
|
this.handlers.get(type)!.push(handler);
|
||
|
|
}
|
||
|
|
|
||
|
|
off(type: string, handler: MessageHandler) {
|
||
|
|
const list = this.handlers.get(type);
|
||
|
|
if (list) this.handlers.set(type, list.filter((h) => h !== handler));
|
||
|
|
}
|
||
|
|
|
||
|
|
send(type: string, data: Record<string, unknown> = {}) {
|
||
|
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
||
|
|
this.ws.send(JSON.stringify({ type, ...data }));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private emit(type: string, data: Record<string, unknown>) {
|
||
|
|
(this.handlers.get(type) || []).forEach((h) => h(data));
|
||
|
|
}
|
||
|
|
|
||
|
|
disconnect() {
|
||
|
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
||
|
|
this.ws?.close();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export const chatSocket = new ChatSocket();
|