Skip to content

Commit b282fc2

Browse files
committed
turn app into a package
1 parent 4b4f9b7 commit b282fc2

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "idom-app-react",
3+
"description": "A client application for IDOM implemented in React",
4+
"version": "0.1.0",
5+
"author": "Ryan Morshead",
6+
"license": "MIT",
7+
"repository": {
8+
"type": "git",
9+
"url": "https://github.com/idom-team/idom"
10+
},
11+
"scripts": {
12+
"format": "prettier --write ./src",
13+
"test": "echo 'no tests'"
14+
},
15+
"devDependencies": {
16+
"prettier": "^2.2.1"
17+
},
18+
"dependencies": {
19+
"idom-client-react": "file:packages/idom-client-react",
20+
"react": "^16.13.1",
21+
"react-dom": "^16.13.1"
22+
}
23+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { mountLayout } from "idom-client-react";
2+
import { unmountComponentAtNode } from "react-dom";
3+
4+
const maxReconnectTimeout = 45;
5+
const initialReconnectTimeoutRange = 5;
6+
7+
const userPackages = import("./user-packages.js").then((module) => {
8+
for (const pkgName in module.default) {
9+
module.default[pkgName].then((pkg) => {
10+
console.log(`Loaded module '${pkgName}'`);
11+
});
12+
}
13+
});
14+
15+
function defaultWebSocketEndpoint() {
16+
const uri = document.location.hostname + ":" + document.location.port;
17+
const url = (uri + document.location.pathname).split("/").slice(0, -1);
18+
url[url.length - 1] = "stream";
19+
const secure = document.location.protocol === "https:";
20+
21+
let protocol;
22+
if (secure) {
23+
protocol = "wss:";
24+
} else {
25+
protocol = "ws:";
26+
}
27+
28+
return protocol + "//" + url.join("/") + "?" + queryParams.user.toString();
29+
}
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.`);
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+
if (!shouldReconnect()) {
73+
console.log(`Connection lost.`);
74+
return;
75+
}
76+
const reconnectTimeout = _nextReconnectTimeout(mountState);
77+
console.log(`Connection lost, reconnecting in ${reconnectTimeout} seconds`);
78+
setTimeout(function () {
79+
mountState.reconnectAttempts++;
80+
mountLayoutWithWebSocket(element, endpoint, importSourceURL, mountState);
81+
}, reconnectTimeout * 1000);
82+
};
83+
}
84+
85+
function _setOpenMountState(mountState) {
86+
mountState.everMounted = true;
87+
mountState.reconnectAttempts = 0;
88+
mountState.reconnectTimeoutRange = initialReconnectTimeoutRange;
89+
}
90+
91+
function _nextReconnectTimeout(mountState) {
92+
const timeout = Math.floor(Math.random() * mountState.reconnectTimeoutRange);
93+
mountState.reconnectTimeoutRange =
94+
(mountState.reconnectTimeoutRange + 5) % maxReconnectTimeout;
95+
if (mountState.reconnectAttempts == 3) {
96+
window.alert(
97+
"Server connection was lost. Attempts to reconnect are being made in the background."
98+
);
99+
}
100+
return timeout;
101+
}
102+
103+
function shouldReconnect() {
104+
return queryParams.reserved.get("noReconnect") === null;
105+
}
106+
107+
const queryParams = (() => {
108+
const reservedParams = new URLSearchParams();
109+
const userParams = new URLSearchParams(window.location.search);
110+
111+
const reservedParamNames = ["noReconnect"];
112+
reservedParamNames.forEach((name) => {
113+
const value = userParams.get(name);
114+
if (value !== null) {
115+
reservedParams.append(name, userParams.get(name));
116+
userParams.delete(name);
117+
}
118+
});
119+
120+
return {
121+
reserved: reservedParams,
122+
user: userParams,
123+
};
124+
})();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default {};

0 commit comments

Comments
 (0)