Files
janichat/frontend/src/api/ws.ts

71 lines
1.9 KiB
TypeScript

type Handler = (payload: any) => void;
class WsClient {
private ws: WebSocket | null = null;
private handlers = new Map<string, Handler[]>();
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
private token: string | null = null;
connect(token: string) {
this.token = token;
const wsBase = import.meta.env.VITE_WS_URL || `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}`;
this.ws = new WebSocket(`${wsBase}/ws?token=${token}`);
this.ws.onopen = () => {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
this.emit('connected', null);
};
this.ws.onmessage = (event) => {
try {
const { type, payload } = JSON.parse(event.data);
const hs = this.handlers.get(type) || [];
hs.forEach(h => h(payload));
} catch {}
};
this.ws.onclose = () => {
this.emit('disconnected', null);
this.reconnectTimer = setTimeout(() => {
if (this.token) this.connect(this.token);
}, 3000);
};
this.ws.onerror = () => {
this.ws?.close();
};
}
on(type: string, handler: Handler) {
if (!this.handlers.has(type)) this.handlers.set(type, []);
this.handlers.get(type)!.push(handler);
return () => {
const hs = this.handlers.get(type) || [];
this.handlers.set(type, hs.filter(h => h !== handler));
};
}
private emit(type: string, payload: any) {
const hs = this.handlers.get(type) || [];
hs.forEach(h => h(payload));
}
send(type: string, payload: any) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type, payload }));
}
}
disconnect() {
this.token = null;
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
this.ws?.close();
this.ws = null;
}
}
export const wsClient = new WsClient();