+1
-2
index.html
+1
-2
index.html
···
2
2
<html lang="en">
3
3
<head>
4
4
<meta charset="UTF-8" />
5
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
5
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
-
<title>Vite + React + TS</title>
6
+
<title>compare hoses</title>
8
7
</head>
9
8
<body>
10
9
<div id="root"></div>
+38
-6
src/App.tsx
+38
-6
src/App.tsx
···
7
7
8
8
const INTERVAL = 1600;
9
9
const SERIES_LEN = 6;
10
+
const KEEPALIVE = 10 * 60 * 1000;
10
11
11
12
function App() {
12
13
const [relays, setRelays] = useState([]);
13
14
const [receiver, setReceiver] = useState(() => () => null);
15
+
const [keepalive, setKeepalive] = useState(() => () => null);
14
16
const [rateBars, setRateBars] = useState({ series: [] });
17
+
const [died, setDied] = useState(false);
15
18
16
19
useEffect(() => {
17
20
let lastChangeover = performance.now();
18
21
let currentCounts = {};
19
22
let series = [];
23
+
let raf = requestAnimationFrame(update);
24
+
let ttl = setTimeout(die, KEEPALIVE);
20
25
21
26
setReceiver(() => (url, type, event) => {
22
27
if (!currentCounts[url]) currentCounts[url] = 0;
23
28
currentCounts[url] += 1;
24
29
});
25
30
31
+
setKeepalive(() => () => {
32
+
clearTimeout(ttl);
33
+
ttl = setTimeout(die, KEEPALIVE);
34
+
setDied(false);
35
+
console.info('keepalive: disconnection timer reset');
36
+
});
37
+
38
+
function die() {
39
+
console.info('disconnecting due to inactivity');
40
+
setRelays([]);
41
+
setDied(true);
42
+
}
43
+
26
44
const nextBlock = setInterval(() => {
27
45
let now = performance.now();
28
46
let dt = now - lastChangeover;
···
36
54
currentCounts = {};
37
55
}, INTERVAL);
38
56
39
-
const update = () => {
57
+
function update() {
40
58
let now = performance.now();
41
59
let dt = (now - lastChangeover) / 1000;
42
60
const relays = Object.keys(series.at(-1)?.counts || {}).toSorted();
···
64
82
65
83
raf = requestAnimationFrame(update);
66
84
};
67
-
let raf = requestAnimationFrame(update);
68
85
69
86
return () => {
70
87
setReceiver(() => () => null);
88
+
setKeepalive(() => () => null);
71
89
clearInterval(nextBlock);
72
90
cancelAnimationFrame(raf);
73
91
};
74
92
}, []);
75
93
94
+
95
+
function showRelay(url, show) {
96
+
setDied(false);
97
+
if (show) {
98
+
setRelays(rs => rs.includes(url) ? rs : [...rs, url]);
99
+
} else {
100
+
setRelays(rs => rs.includes(url) ? rs.filter(u => u !== url) : rs);
101
+
}
102
+
keepalive();
103
+
}
104
+
76
105
return (
77
106
<>
78
107
<h1>compare hoses</h1>
···
84
113
<label>
85
114
<input
86
115
type="checkbox"
87
-
onInput={e => e.target.checked
88
-
? relays.includes(url) || setRelays([...relays, url])
89
-
: setRelays(relays.filter(u => u !== url))
90
-
}
116
+
onChange={e => showRelay(url, e.target.checked)}
117
+
checked={relays.includes(url)}
91
118
/>
92
119
{ ` ${desc} ` }
93
120
(<code>{ url.slice('wss://'.length) }</code>)
···
110
137
);
111
138
})}
112
139
</div>
140
+
141
+
{died && (
142
+
<p><em>disconnected to save bandwidth due to inactivity</em></p>
143
+
)}
144
+
113
145
<div className="throughputs">
114
146
<BarChart
115
147
height={300}