import { Client, Message } from "revolt.js/dist"; import { ClientboundNotification } from "revolt.js/dist/websocket/notifications"; import { StateUpdater } from "preact/hooks"; import { dispatch } from "../../redux"; import { ClientOperations, ClientStatus } from "./RevoltClient"; export var preventReconnect = false; let preventUntil = 0; export function setReconnectDisallowed(allowed: boolean) { preventReconnect = allowed; } export function registerEvents( { operations }: { operations: ClientOperations }, setStatus: StateUpdater<ClientStatus>, client: Client, ) { function attemptReconnect() { if (preventReconnect) return; function reconnect() { preventUntil = +new Date() + 2000; client.websocket.connect().catch((err) => console.error(err)); } if (+new Date() > preventUntil) { setTimeout(reconnect, 2000); } else { reconnect(); } } let listeners: Record<string, (...args: any[]) => void> = { connecting: () => operations.ready() && setStatus(ClientStatus.CONNECTING), dropped: () => { if (operations.ready()) { setStatus(ClientStatus.DISCONNECTED); attemptReconnect(); } }, packet: (packet: ClientboundNotification) => { switch (packet.type) { case "ChannelStartTyping": { if (packet.user === client.user?._id) return; dispatch({ type: "TYPING_START", channel: packet.id, user: packet.user, }); break; } case "ChannelStopTyping": { if (packet.user === client.user?._id) return; dispatch({ type: "TYPING_STOP", channel: packet.id, user: packet.user, }); break; } case "ChannelAck": { dispatch({ type: "UNREADS_MARK_READ", channel: packet.id, message: packet.message_id, }); break; } } }, message: (message: Message) => { if (message.mentions?.includes(client.user!._id)) { dispatch({ type: "UNREADS_MENTION", channel: message.channel, message: message._id, }); } }, ready: () => setStatus(ClientStatus.ONLINE), }; if (import.meta.env.DEV) { listeners = new Proxy(listeners, { get: (target, listener, receiver) => (...args: unknown[]) => { console.debug(`Calling ${listener.toString()} with`, args); Reflect.get(target, listener)(...args); }, }); } // TODO: clean this a bit and properly handle types for (const listener in listeners) { client.addListener(listener, listeners[listener]); } function logMutation(target: string, key: string) { console.log("(o) Object mutated", target, "\nChanged:", key); } if (import.meta.env.DEV) { client.users.addListener("mutation", logMutation); client.servers.addListener("mutation", logMutation); client.channels.addListener("mutation", logMutation); client.servers.members.addListener("mutation", logMutation); } const online = () => { if (operations.ready()) { setStatus(ClientStatus.RECONNECTING); setReconnectDisallowed(false); attemptReconnect(); } }; const offline = () => { if (operations.ready()) { setReconnectDisallowed(true); client.websocket.disconnect(); setStatus(ClientStatus.OFFLINE); } }; window.addEventListener("online", online); window.addEventListener("offline", offline); return () => { for (const listener in listeners) { client.removeListener( listener, listeners[listener as keyof typeof listeners], ); } if (import.meta.env.DEV) { client.users.removeListener("mutation", logMutation); client.servers.removeListener("mutation", logMutation); client.channels.removeListener("mutation", logMutation); client.servers.members.removeListener("mutation", logMutation); } window.removeEventListener("online", online); window.removeEventListener("offline", offline); }; }