71 lines
1.9 KiB
TypeScript
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();
|