Files
video-create/web/client/src/lib/websocket.ts

61 lines
1.8 KiB
TypeScript
Raw Normal View History

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;
private intentionallyClosed = false;
connect() {
this.intentionallyClosed = false;
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.emit('disconnected', {});
if (!this.intentionallyClosed) {
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) {
const idx = list.indexOf(handler);
if (idx !== -1) list.splice(idx, 1);
}
}
send(type: string, data: Record<string, unknown> = {}) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type, ...data }));
}
}
stop() { this.send('stop'); }
private emit(type: string, data: Record<string, unknown>) {
(this.handlers.get(type) || []).forEach((h) => h(data));
}
disconnect() {
this.intentionallyClosed = true;
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
this.ws?.close();
this.ws = null;
}
}
export const chatSocket = new ChatSocket();