Skip to content

Commit 3477e2b

Browse files
committed
automatically reconnect to server
1 parent 2a43df7 commit 3477e2b

File tree

2 files changed

+74
-17
lines changed

2 files changed

+74
-17
lines changed

src/idom/client/app/src/index.js

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { mountLayout } from "idom-client-react";
2+
import { unmountComponentAtNode } from "react-dom";
3+
4+
const maxReconnectTimeout = 45;
5+
const initialReconnectTimeoutRange = 5;
26

37
const userPackages = import("./user-packages.js").then((module) => {
48
for (const pkgName in module.default) {
@@ -8,23 +12,6 @@ const userPackages = import("./user-packages.js").then((module) => {
812
}
913
});
1014

11-
export function mountLayoutWithWebSocket(element, endpoint, importSourceURL) {
12-
const ws = new WebSocket(endpoint || defaultWebSocketEndpoint());
13-
14-
function saveUpdateHook(update) {
15-
ws.onmessage = (event) => {
16-
const [pathPrefix, patch] = JSON.parse(event.data);
17-
update(pathPrefix, patch);
18-
};
19-
}
20-
21-
function sendCallback(event) {
22-
ws.send(JSON.stringify(event));
23-
}
24-
25-
mountLayout(element, saveUpdateHook, sendCallback, importSourceURL || "./");
26-
}
27-
2815
function defaultWebSocketEndpoint() {
2916
const uri = document.location.hostname + ":" + document.location.port;
3017
const url = (uri + document.location.pathname).split("/").slice(0, -1);
@@ -40,3 +27,71 @@ function defaultWebSocketEndpoint() {
4027

4128
return protocol + "//" + url.join("/") + window.location.search;
4229
}
30+
31+
export function mountLayoutWithWebSocket(
32+
element,
33+
endpoint = defaultWebSocketEndpoint(),
34+
importSourceURL = "./",
35+
mountState = {
36+
everMounted: false,
37+
reconnectAttempts: 0,
38+
reconnectTimeoutRange: initialReconnectTimeoutRange,
39+
}
40+
) {
41+
const socket = new WebSocket(endpoint);
42+
43+
let resolveUpdateHook = null;
44+
let rejectUpdateHook = null;
45+
const updateHookPromise = new Promise((resolve, reject) => {
46+
resolveUpdateHook = resolve;
47+
rejectUpdateHook = reject;
48+
});
49+
50+
socket.onopen = (event) => {
51+
console.log(`Connected to ${endpoint}`);
52+
if (mountState.everMounted) {
53+
unmountComponentAtNode(element);
54+
}
55+
mountLayout(
56+
element,
57+
(updateHook) => resolveUpdateHook(updateHook),
58+
(event) => socket.send(JSON.stringify(event)),
59+
importSourceURL
60+
);
61+
_setOpenMountState(mountState);
62+
};
63+
64+
socket.onmessage = (event) => {
65+
updateHookPromise.then((update) => {
66+
const [pathPrefix, patch] = JSON.parse(event.data);
67+
update(pathPrefix, patch);
68+
});
69+
};
70+
71+
socket.onclose = (event) => {
72+
const reconnectTimeout = _nextReconnectTimeout(mountState);
73+
console.log(`Connection lost, reconnecting in ${reconnectTimeout} seconds`);
74+
setTimeout(function () {
75+
mountState.reconnectAttempts++;
76+
mountLayoutWithWebSocket(element, endpoint, importSourceURL, mountState);
77+
}, reconnectTimeout * 1000);
78+
};
79+
}
80+
81+
function _setOpenMountState(mountState) {
82+
mountState.everMounted = true;
83+
mountState.reconnectAttempts = 0;
84+
mountState.reconnectTimeoutRange = initialReconnectTimeoutRange;
85+
}
86+
87+
function _nextReconnectTimeout(mountState) {
88+
const timeout = Math.floor(Math.random() * mountState.reconnectTimeoutRange);
89+
mountState.reconnectTimeoutRange =
90+
(mountState.reconnectTimeoutRange + 5) % maxReconnectTimeout;
91+
if (mountState.reconnectAttempts == 3) {
92+
window.alert(
93+
"Server connection was lost. Attempts to reconnect are being made in the background."
94+
);
95+
}
96+
return timeout;
97+
}

tests/test_client/test_app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test_automatic_reconnect():
2+
assert False

0 commit comments

Comments
 (0)