diff --git a/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx b/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx index 0710e94d..00225d8a 100644 --- a/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx +++ b/frontend/src/components/TradeBox/EncryptedChat/EncryptedSocketChat/index.tsx @@ -127,10 +127,13 @@ const EncryptedSocketChat: React.FC = ({ setConnection(connection); setConnected(true); - connection.send({ - message: robot?.pubKey, - nick: userNick, - }); + connection.send( + JSON.stringify({ + type: 'message', + message: robot?.pubKey, + nick: userNick, + }), + ); connection.onMessage((message) => { setServerMessages((prev) => [...prev, message]); @@ -173,10 +176,13 @@ const EncryptedSocketChat: React.FC = ({ dataFromServer.message !== robot.pubKey ) { setPeerPubKey(dataFromServer.message); - connection.send({ - message: `-----SERVE HISTORY-----`, - nick: userNick, - }); + connection.send( + JSON.stringify({ + type: 'message', + message: `-----SERVE HISTORY-----`, + nick: userNick, + }), + ); } // If we receive an encrypted message else if (dataFromServer.message.substring(0, 27) === `-----BEGIN PGP MESSAGE-----`) { @@ -242,10 +248,13 @@ const EncryptedSocketChat: React.FC = ({ } // If input string contains '#' send unencrypted and unlogged message else if (connection != null && value.substring(0, 1) === '#') { - connection.send({ - message: value, - nick: userNick, - }); + connection.send( + JSON.stringify({ + type: 'message', + message: value, + nick: userNick, + }), + ); setValue(''); } @@ -257,10 +266,13 @@ const EncryptedSocketChat: React.FC = ({ encryptMessage(value, robot.pubKey, peerPubKey, robot.encPrivKey, slot.token) .then((encryptedMessage) => { if (connection != null) { - connection.send({ - message: String(encryptedMessage).split('\n').join('\\'), - nick: userNick, - }); + connection.send( + JSON.stringify({ + type: 'message', + message: String(encryptedMessage).split('\n').join('\\'), + nick: userNick, + }), + ); } }) .catch((error) => { diff --git a/frontend/src/services/RoboPool/index.ts b/frontend/src/services/RoboPool/index.ts index ee423d94..901c328f 100644 --- a/frontend/src/services/RoboPool/index.ts +++ b/frontend/src/services/RoboPool/index.ts @@ -1,6 +1,7 @@ import { type Event } from 'nostr-tools'; import { type Settings } from '../../models'; import defaultFederation from '../../../static/federation.json'; +import { websocketClient, WebsocketConnection, WebsocketState } from '../Websocket'; interface RoboPoolEvents { onevent: (event: Event) => void; @@ -39,63 +40,60 @@ class RoboPool { public relays: string[]; public network: string; - public webSockets: WebSocket[] = []; + public webSockets: Record = {}; private readonly messageHandlers: Array<(url: string, event: MessageEvent) => void> = []; connect = (): void => { - this.relays.forEach((url) => { - if (this.webSockets.find((w: WebSocket) => w.url === url)) return; + this.relays.forEach((url: string) => { + if (Object.keys(this.webSockets).find((wUrl) => wUrl === url)) return; - let ws: WebSocket; + this.webSockets[url] = null; - const connect = (): void => { - ws = new WebSocket(url); - - // Add event listeners for the WebSocket - ws.onopen = () => { + const connectRelay = () => { + websocketClient.open(url).then((connection) => { console.log(`Connected to ${url}`); - }; - ws.onmessage = (event) => { - this.messageHandlers.forEach((handler) => { - handler(url, event); + connection.onMessage((event) => { + this.messageHandlers.forEach((handler) => { + handler(url, event); + }); }); - }; - ws.onerror = (error) => { - console.error(`WebSocket error on ${url}:`, error); - }; + connection.onError((error) => { + console.error(`WebSocket error on ${url}:`, error); + }); - ws.onclose = () => { - console.log(`Disconnected from ${url}. Attempting to reconnect...`); - setTimeout(connect, 1000); // Reconnect after 1 second - }; + connection.onClose(() => { + console.log(`Disconnected from ${url}`); + }); + + this.webSockets[url] = connection; + }); }; - - connect(); - this.webSockets.push(ws); + connectRelay(); }); }; close = (): void => { - this.webSockets.forEach((ws) => { - ws.close(); + Object.values(this.webSockets).forEach((ws) => { + ws?.close(); }); + this.webSockets = {}; }; sendMessage = (message: string): void => { - const send = (index: number, message: string): void => { - const ws = this.webSockets[index]; + const send = (url: string, message: string): void => { + const ws = this.webSockets[url]; - if (ws.readyState === WebSocket.OPEN) { + if (!ws || ws.getReadyState() === WebsocketState.CONNECTING) { + setTimeout(send, 500, url, message); + } else if (ws.getReadyState() === WebsocketState.OPEN) { ws.send(message); - } else if (ws.readyState === WebSocket.CONNECTING) { - setTimeout(send, 500, index, message); } }; - this.webSockets.forEach((_ws, index) => { - send(index, message); + Object.keys(this.webSockets).forEach((url) => { + send(url, message); }); }; diff --git a/frontend/src/services/Websocket/WebsocketConnectionWeb/index.ts b/frontend/src/services/Websocket/WebsocketConnectionWeb/index.ts deleted file mode 100644 index 70fbf3d3..00000000 --- a/frontend/src/services/Websocket/WebsocketConnectionWeb/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import ReconnectingWebSocket from 'reconnecting-websocket'; -import { type WebsocketConnection } from '..'; - -class WebsocketConnectionWeb implements WebsocketConnection { - constructor(path: string) { - this.rws = new ReconnectingWebSocket(path, [], { - WebSocket, - minReconnectionDelay: 15000, - connectionTimeout: 15000, - reconnectionDelayGrowFactor: 2, - maxRetries: 4, - maxReconnectionDelay: 1000000, - }); - } - - public rws: ReconnectingWebSocket; - - public send: (message: object) => void = (message: object) => { - this.rws.send( - JSON.stringify({ - type: 'message', - ...message, - }), - ); - }; - - public close: () => void = () => { - this.rws.close(); - }; - - public onMessage: (event: (message: any) => void) => void = (event) => { - this.rws.addEventListener('message', event); - }; - - public onClose: (event: () => void) => void = (event) => { - this.rws.addEventListener('close', event); - }; - - public onError: (event: () => void) => void = (event) => { - this.rws.addEventListener('error', event); - }; -} - -export default WebsocketConnectionWeb; diff --git a/frontend/src/services/Websocket/WebsocketWebClient/index.ts b/frontend/src/services/Websocket/WebsocketWebClient/index.ts index c6656ff6..5de02aa6 100644 --- a/frontend/src/services/Websocket/WebsocketWebClient/index.ts +++ b/frontend/src/services/Websocket/WebsocketWebClient/index.ts @@ -1,5 +1,42 @@ +import ReconnectingWebSocket from 'reconnecting-websocket'; import { type WebsocketClient, type WebsocketConnection } from '..'; -import WebsocketConnectionWeb from '../WebsocketConnectionWeb'; + +class WebsocketConnectionWeb implements WebsocketConnection { + constructor(path: string) { + this.rws = new ReconnectingWebSocket(path, [], { + WebSocket, + minReconnectionDelay: 15000, + connectionTimeout: 15000, + reconnectionDelayGrowFactor: 2, + maxRetries: 4, + maxReconnectionDelay: 1000000, + }); + } + + public rws: ReconnectingWebSocket; + + public send: (message: string) => void = (message: string) => { + this.rws.send(message); + }; + + public close: () => void = () => { + this.rws.close(); + }; + + public onMessage: (event: (message: any) => void) => void = (event) => { + this.rws.addEventListener('message', event); + }; + + public onClose: (event: () => void) => void = (event) => { + this.rws.addEventListener('close', event); + }; + + public onError: (event: (error: any) => void) => void = (event) => { + this.rws.addEventListener('error', event); + }; + + public getReadyState: () => number = () => this.rws.readyState; +} class WebsocketWebClient implements WebsocketClient { public open: (path: string) => Promise = async (path) => { diff --git a/frontend/src/services/Websocket/index.ts b/frontend/src/services/Websocket/index.ts index 00fdb178..bae22f21 100644 --- a/frontend/src/services/Websocket/index.ts +++ b/frontend/src/services/Websocket/index.ts @@ -1,11 +1,19 @@ import WebsocketWebClient from './WebsocketWebClient'; +export const WebsocketState = { + CONNECTING: 0, + OPEN: 1, + CLOSING: 2, + CLOSED: 3, +} as const; + export interface WebsocketConnection { - send: (message: object) => void; + send: (message: string) => void; onMessage: (event: (message: any) => void) => void; onClose: (event: () => void) => void; - onError: (event: () => void) => void; + onError: (event: (error: any) => void) => void; close: () => void; + getReadyState: () => number; } export interface WebsocketClient {