import logging from "./logging"; import { getAuthToken, getItem } from "./storage"; export const GatewayPayloadType = { Hello: 0, Authenticate: 1, Ready: 2, Ping: 3, ChannelCreate: 110, ChannelUpdate: 111, ChannelDelete: 112, MessageCreate: 120, MessageUpdate: 121, MessageDelete: 122, } export const GatewayEventType = { ...GatewayPayloadType, Open: -5, Close: -4 } const log = logging.logger("Gateway"); export default { ws: null, authenticated: false, open: false, heartbeatInterval: null, user: null, channels: null, reconnectDelay: 400, reconnectTimeout: null, handlers: new Map(), init() { const token = getAuthToken(); if (!token) { return false; } this.ws = new WebSocket(getItem("gatewayBase")); this.ws.onopen = () => { if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } this.open = true; this.dispatch(GatewayEventType.Open, null); log("open"); }; this.ws.onmessage = (event) => { const payload = JSON.parse(event.data); switch (payload.t) { case GatewayPayloadType.Hello: { this.send({ t: GatewayPayloadType.Authenticate, d: token }); this.heartbeatInterval = setInterval(() => { this.send({ t: GatewayPayloadType.Ping, d: 0 }); }, payload.d.pingInterval); log("hello"); break; } case GatewayPayloadType.Ready: { this.user = payload.d.user; this.channels = payload.d.channels; this.reconnectDelay = 400; log("ready"); break; } } this.dispatch(payload.t, payload.d); }; this.ws.onclose = () => { if (this.reconnectDelay < 60000) { this.reconnectDelay *= 2; } this.authenticated = false; this.user = null; this.channels = null; this.open = false; if (this.heartbeatInterval) { clearInterval(this.heartbeatInterval); } this.reconnectTimeout = setTimeout(() => { this.init(); }, this.reconnectDelay); this.dispatch(GatewayEventType.Close, null); log("close"); }; return true; }, send(data) { return this.ws.send(JSON.stringify(data)); }, dispatch(event, payload) { const eventHandlers = this.handlers.get(event); if (!eventHandlers) return; eventHandlers.forEach((e) => { e(payload); }); }, subscribe(event, handler) { if (!this.handlers.get(event)) { this.handlers.set(event, new Set()); } this.handlers.get(event).add(handler); return handler; // can later be used for unsubscribe() }, unsubscribe(event, handler) { const eventHandlers = this.handlers.get(event); if (!eventHandlers) return; eventHandlers.delete(handler); if (eventHandlers.size < 1) { this.handlers.delete(event); } } };