Spaces:
Runtime error
Runtime error
File size: 1,797 Bytes
0ce9643 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | import type { CollabC2S, CollabS2C } from '../types/collab';
const WS_BASE = import.meta.env.VITE_WS_BASE_URL || `ws://${window.location.host}`;
type MessageHandler = (msg: CollabS2C) => void;
export class CollabSocket {
private ws: WebSocket | null = null;
private onMessage: MessageHandler;
private onDisconnect: (() => void) | null;
private pingInterval: ReturnType<typeof setInterval> | null = null;
constructor(onMessage: MessageHandler, onDisconnect?: () => void) {
this.onMessage = onMessage;
this.onDisconnect = onDisconnect ?? null;
}
connect(sessionId: string): Promise<void> {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(`${WS_BASE}/ws/collab/${sessionId}`);
this.ws.onopen = () => {
this.pingInterval = setInterval(() => {
this.send({ type: 'c2s_ping' });
}, 30_000);
resolve();
};
this.ws.onmessage = (event) => {
try {
const msg: CollabS2C = JSON.parse(event.data);
this.onMessage(msg);
} catch {
// ignore malformed
}
};
this.ws.onerror = () => reject(new Error('Collab WebSocket connection failed'));
this.ws.onclose = () => {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
this.onDisconnect?.();
};
});
}
send(msg: CollabC2S): void {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(msg));
}
}
close(): void {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
this.ws?.close();
this.ws = null;
}
get connected(): boolean {
return this.ws?.readyState === WebSocket.OPEN;
}
}
|