Relay firehose browser tools: https://compare.hose.cam

disconnect timer

10 mins

Changed files
+39 -8
src
+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
··· 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}