Skip to content

Commit

Permalink
Merge pull request #1614 from RoboSats/refactor-nostr-websockets
Browse files Browse the repository at this point in the history
Refactor nostr websocket to be preared for mobile
  • Loading branch information
KoalaSat authored Nov 12, 2024
2 parents 6b8b694 + 3907983 commit 02ac9e5
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,13 @@ const EncryptedSocketChat: React.FC<Props> = ({
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]);
Expand Down Expand Up @@ -173,10 +176,13 @@ const EncryptedSocketChat: React.FC<Props> = ({
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-----`) {
Expand Down Expand Up @@ -242,10 +248,13 @@ const EncryptedSocketChat: React.FC<Props> = ({
}
// 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('');
}

Expand All @@ -257,10 +266,13 @@ const EncryptedSocketChat: React.FC<Props> = ({
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) => {
Expand Down
64 changes: 31 additions & 33 deletions frontend/src/services/RoboPool/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -39,63 +40,60 @@ class RoboPool {
public relays: string[];
public network: string;

public webSockets: WebSocket[] = [];
public webSockets: Record<string, WebsocketConnection | null> = {};
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}`);
});

connect();
this.webSockets.push(ws);
this.webSockets[url] = connection;
});
};
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);
});
};

Expand Down
44 changes: 0 additions & 44 deletions frontend/src/services/Websocket/WebsocketConnectionWeb/index.ts

This file was deleted.

39 changes: 38 additions & 1 deletion frontend/src/services/Websocket/WebsocketWebClient/index.ts
Original file line number Diff line number Diff line change
@@ -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<WebsocketConnection> = async (path) => {
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/services/Websocket/index.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down

0 comments on commit 02ac9e5

Please sign in to comment.