← back to Dw War Room

src/data/WebSocketClient.ts

76 lines

// ═══════════════════════════════════════════════
// DW War Room 3D — Dual WebSocket Client
// Connects to both Governance and Boardroom WS servers
// ═══════════════════════════════════════════════

import type { WSEvent } from '../types';

type EventHandler = (event: WSEvent, source: 'governance' | 'boardroom') => void;

export class DualWebSocketClient {
  private govWs: WebSocket | null = null;
  private brWs: WebSocket | null = null;
  private handlers: EventHandler[] = [];
  private reconnectTimers: { gov?: number; br?: number } = {};

  constructor(
    private govUrl: string = import.meta.env.VITE_GOVERNANCE_WS || 'ws://45.61.58.125:4020/ws',
    private brUrl: string = import.meta.env.VITE_BOARDROOM_WS || 'ws://45.61.58.125:4040/ws'
  ) {}

  onEvent(handler: EventHandler): void {
    this.handlers.push(handler);
  }

  connect(): void {
    this.connectGov();
    this.connectBr();
  }

  disconnect(): void {
    if (this.govWs) this.govWs.close();
    if (this.brWs) this.brWs.close();
    if (this.reconnectTimers.gov) clearTimeout(this.reconnectTimers.gov);
    if (this.reconnectTimers.br) clearTimeout(this.reconnectTimers.br);
  }

  private connectGov(): void {
    try {
      this.govWs = new WebSocket(this.govUrl);
      this.govWs.onmessage = (evt) => this.dispatch(evt.data, 'governance');
      this.govWs.onclose = () => {
        this.reconnectTimers.gov = window.setTimeout(() => this.connectGov(), 5000);
      };
      this.govWs.onerror = () => this.govWs?.close();
    } catch {}
  }

  private connectBr(): void {
    try {
      this.brWs = new WebSocket(this.brUrl);
      this.brWs.onmessage = (evt) => this.dispatch(evt.data, 'boardroom');
      this.brWs.onclose = () => {
        this.reconnectTimers.br = window.setTimeout(() => this.connectBr(), 5000);
      };
      this.brWs.onerror = () => this.brWs?.close();
    } catch {}
  }

  private dispatch(data: string, source: 'governance' | 'boardroom'): void {
    try {
      const event: WSEvent = JSON.parse(data);
      for (const handler of this.handlers) {
        handler(event, source);
      }
    } catch {}
  }

  get govConnected(): boolean {
    return this.govWs?.readyState === WebSocket.OPEN;
  }

  get brConnected(): boolean {
    return this.brWs?.readyState === WebSocket.OPEN;
  }
}