+1
-2
index.html
+1
-2
index.html
+38
-6
src/App.tsx
+38
-6
src/App.tsx
···
7
8
const INTERVAL = 1600;
9
const SERIES_LEN = 6;
10
11
function App() {
12
const [relays, setRelays] = useState([]);
13
const [receiver, setReceiver] = useState(() => () => null);
14
const [rateBars, setRateBars] = useState({ series: [] });
15
16
useEffect(() => {
17
let lastChangeover = performance.now();
18
let currentCounts = {};
19
let series = [];
20
21
setReceiver(() => (url, type, event) => {
22
if (!currentCounts[url]) currentCounts[url] = 0;
23
currentCounts[url] += 1;
24
});
25
26
const nextBlock = setInterval(() => {
27
let now = performance.now();
28
let dt = now - lastChangeover;
···
36
currentCounts = {};
37
}, INTERVAL);
38
39
-
const update = () => {
40
let now = performance.now();
41
let dt = (now - lastChangeover) / 1000;
42
const relays = Object.keys(series.at(-1)?.counts || {}).toSorted();
···
64
65
raf = requestAnimationFrame(update);
66
};
67
-
let raf = requestAnimationFrame(update);
68
69
return () => {
70
setReceiver(() => () => null);
71
clearInterval(nextBlock);
72
cancelAnimationFrame(raf);
73
};
74
}, []);
75
76
return (
77
<>
78
<h1>compare hoses</h1>
···
84
<label>
85
<input
86
type="checkbox"
87
-
onInput={e => e.target.checked
88
-
? relays.includes(url) || setRelays([...relays, url])
89
-
: setRelays(relays.filter(u => u !== url))
90
-
}
91
/>
92
{ ` ${desc} ` }
93
(<code>{ url.slice('wss://'.length) }</code>)
···
110
);
111
})}
112
</div>
113
<div className="throughputs">
114
<BarChart
115
height={300}
···
7
8
const INTERVAL = 1600;
9
const SERIES_LEN = 6;
10
+
const KEEPALIVE = 10 * 60 * 1000;
11
12
function App() {
13
const [relays, setRelays] = useState([]);
14
const [receiver, setReceiver] = useState(() => () => null);
15
+
const [keepalive, setKeepalive] = useState(() => () => null);
16
const [rateBars, setRateBars] = useState({ series: [] });
17
+
const [died, setDied] = useState(false);
18
19
useEffect(() => {
20
let lastChangeover = performance.now();
21
let currentCounts = {};
22
let series = [];
23
+
let raf = requestAnimationFrame(update);
24
+
let ttl = setTimeout(die, KEEPALIVE);
25
26
setReceiver(() => (url, type, event) => {
27
if (!currentCounts[url]) currentCounts[url] = 0;
28
currentCounts[url] += 1;
29
});
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
+
44
const nextBlock = setInterval(() => {
45
let now = performance.now();
46
let dt = now - lastChangeover;
···
54
currentCounts = {};
55
}, INTERVAL);
56
57
+
function update() {
58
let now = performance.now();
59
let dt = (now - lastChangeover) / 1000;
60
const relays = Object.keys(series.at(-1)?.counts || {}).toSorted();
···
82
83
raf = requestAnimationFrame(update);
84
};
85
86
return () => {
87
setReceiver(() => () => null);
88
+
setKeepalive(() => () => null);
89
clearInterval(nextBlock);
90
cancelAnimationFrame(raf);
91
};
92
}, []);
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
+
105
return (
106
<>
107
<h1>compare hoses</h1>
···
113
<label>
114
<input
115
type="checkbox"
116
+
onChange={e => showRelay(url, e.target.checked)}
117
+
checked={relays.includes(url)}
118
/>
119
{ ` ${desc} ` }
120
(<code>{ url.slice('wss://'.length) }</code>)
···
137
);
138
})}
139
</div>
140
+
141
+
{died && (
142
+
<p><em>disconnected to save bandwidth due to inactivity</em></p>
143
+
)}
144
+
145
<div className="throughputs">
146
<BarChart
147
height={300}