Implemented reconnect logic
This commit is contained in:
parent
37a3f85037
commit
8e5788b6e5
3 changed files with 170 additions and 22 deletions
|
@ -54,6 +54,7 @@ body, h1, h2, h3, h4, h5, h6, p, ol, ul {
|
|||
|
||||
[hidden] { display: none !important; }
|
||||
|
||||
/* ---------------------------- Header ---------------------------- */
|
||||
header {
|
||||
border-bottom: 1px solid black;
|
||||
background: var(--header-bg);
|
||||
|
@ -78,10 +79,38 @@ header span {
|
|||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 2 1 auto;
|
||||
header .overlay {
|
||||
background: var(--header-bg);
|
||||
color: var(--foreground);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
header .overlay h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
header .overlay .bar {
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
header .overlay .bar .led {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
/* ---------------------------- Nav ---------------------------- */
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -133,6 +162,15 @@ nav::after {
|
|||
border-top: 1px solid var(--nav-above-border-color);
|
||||
}
|
||||
|
||||
/* ---------------------------- Main ---------------------------- */
|
||||
|
||||
main {
|
||||
flex: 2 1 auto;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------- Extra ---------------------------- */
|
||||
|
||||
/* Taken from https://github.com/aus/led.css */
|
||||
.led {
|
||||
margin-top: 1px;
|
||||
|
|
|
@ -1,10 +1,37 @@
|
|||
class Animator {
|
||||
constructor() {
|
||||
this.running = null
|
||||
this.runningInterval = null
|
||||
}
|
||||
|
||||
begin(time) {
|
||||
if (this.runningInterval === time && this.running) return
|
||||
if (this.running) {
|
||||
clearInterval(this.running)
|
||||
}
|
||||
this.runningInterval = time
|
||||
this.running = setInterval(function() {
|
||||
m.redraw()
|
||||
}, time)
|
||||
}
|
||||
|
||||
stop() {
|
||||
clearInterval(this.running)
|
||||
this.running = null
|
||||
}
|
||||
}
|
||||
|
||||
const animator = new Animator()
|
||||
|
||||
class WSClient
|
||||
{
|
||||
constructor(url = 'ws://localhost:4040') {
|
||||
this.url = url
|
||||
this.socket = null
|
||||
this.connected = false
|
||||
this.connecting = false
|
||||
this.connecting = null
|
||||
this.connectingAt = null
|
||||
this.reconnecting = null
|
||||
this.reconnectingAt = null
|
||||
this.retryDuration = 1000
|
||||
this.open()
|
||||
|
@ -12,22 +39,31 @@ class WSClient
|
|||
|
||||
open() {
|
||||
if (this.connected || this.connecting) return
|
||||
this.connecting = true
|
||||
clearTimeout(this.reconnecting)
|
||||
this.reconnecting = this.reconnectingAt = null
|
||||
this.connectingAt = new Date()
|
||||
this.socket = new WebSocket(this.url)
|
||||
|
||||
this.socket.addEventListener('open', this.onopen.bind(this))
|
||||
this.socket.addEventListener('message', this.onmessage.bind(this))
|
||||
this.socket.addEventListener('error', this.onerror.bind(this))
|
||||
this.socket.addEventListener('close', this.onclose.bind(this))
|
||||
|
||||
animator.begin(1000)
|
||||
this.connecting = setTimeout(() => {
|
||||
this.socket.close()
|
||||
}, 5000)
|
||||
m.redraw()
|
||||
}
|
||||
|
||||
onopen() {
|
||||
animator.stop()
|
||||
this.retryDuration = 1000
|
||||
this.connecting = false
|
||||
clearTimeout(this.connecting)
|
||||
this.connecting = null
|
||||
this.connectingAt = null
|
||||
this.reconnectingAt = null
|
||||
this.connected = true
|
||||
console.log('Sending hello server')
|
||||
this.send({ msg: 'Hello Server!' })
|
||||
m.redraw()
|
||||
}
|
||||
|
@ -37,8 +73,7 @@ class WSClient
|
|||
this.socket.send(JSON.stringify(payload))
|
||||
}
|
||||
|
||||
onerror(err) {
|
||||
console.error(err)
|
||||
onerror() {
|
||||
this.reconnect()
|
||||
}
|
||||
|
||||
|
@ -48,14 +83,17 @@ class WSClient
|
|||
|
||||
reconnect() {
|
||||
if (this.reconnectingAt) return
|
||||
animator.begin(100)
|
||||
this.socket = null
|
||||
this.connected = false
|
||||
this.connecting = false
|
||||
clearTimeout(this.connecting)
|
||||
this.connecting = null
|
||||
this.connectingAt = null
|
||||
|
||||
this.reconnectingAt = new Date(new Date().getTime() + this.retryDuration)
|
||||
setTimeout(() => {
|
||||
this.reconnectingAt = null
|
||||
this.retryDuration *= 1.5
|
||||
clearTimeout(this.reconnecting)
|
||||
this.reconnecting = setTimeout(() => {
|
||||
this.retryDuration = Math.min(this.retryDuration * 1.5, 60000)
|
||||
this.open()
|
||||
}, this.retryDuration)
|
||||
m.redraw()
|
||||
|
@ -73,20 +111,93 @@ const client = new WSClient()
|
|||
|
||||
class Header {
|
||||
view(vnode) {
|
||||
console.log(client.connecting)
|
||||
return [
|
||||
m('h1', 'Fíladelfíu streymi'),
|
||||
m('div.status.green', { hidden: true }, 'No errors'),
|
||||
m('div.filler'),
|
||||
m('span', 'Live:'),
|
||||
m('div.led'),
|
||||
client.connected
|
||||
? null
|
||||
: m('div.overlay', [
|
||||
m('h2', client.connecting ? 'Connecting to server...' : 'Connection lost'),
|
||||
m('')
|
||||
]),
|
||||
m('h2', client.connecting ? 'Connecting to server...' : 'Failed to connect, retrying in...'),
|
||||
client.connecting
|
||||
? this.viewConnectingBar()
|
||||
: this.viewWaitingCircle(),
|
||||
]),
|
||||
m('span', 'Live:'),
|
||||
m('div.led'),
|
||||
]
|
||||
}
|
||||
viewConnectingBar() {
|
||||
let diff = Math.round((new Date() - client.connectingAt) / 1000)
|
||||
let out = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
out.push(m('div.led', { class: diff >= i ? 'led-blue' : '' }))
|
||||
}
|
||||
return m('div.bar', out)
|
||||
}
|
||||
|
||||
viewWaitingCircle() {
|
||||
var diff = Math.min((new Date() - client.reconnectingAt + 200), client.retryDuration)
|
||||
var percentage = Math.abs(Math.min(diff / client.retryDuration, 0))
|
||||
return m('svg', {
|
||||
onclick: function() { client.open() },
|
||||
style: 'width: 150px; transform: rotate(-90deg);',
|
||||
viewBox: '0 0 150 150',
|
||||
preserveAspectRatio: 'xMidYMid meet',
|
||||
}, [
|
||||
m('filter', { id: 'darker' }, [
|
||||
m('feFlood', { 'flood-color': '#000000', 'flood-opacity': '1', in: 'SourceGraphic' }),
|
||||
m('feComposite', { operator: 'in', in2: 'SourceGraphic' }),
|
||||
m('feGaussianBlur', { stdDeviation: '2' }), // approx size, with higher values meaning less intensity
|
||||
m('feComponentTransfer', { result: 'glowd' }, m('feFuncA', { type: 'linear', slope: 1, intercept: 0 })),
|
||||
|
||||
m('feMerge', [
|
||||
m('feMergeNode', { in: 'SourceGraphic' }),
|
||||
m('feMergeNode', { in: 'glowd' }),
|
||||
]),
|
||||
]),
|
||||
m('filter', { id: 'glower' }, [
|
||||
m('feFlood', { 'flood-color': '#0066ff', 'flood-opacity': '1', in: 'SourceGraphic' }),
|
||||
m('feComposite', { operator: 'in', in2: 'SourceGraphic' }),
|
||||
m('feGaussianBlur', { stdDeviation: '10' }), // approx size, with higher values meaning less intensity
|
||||
m('feComponentTransfer', { result: 'glow1' }, m('feFuncA', { type: 'linear', slope: 1, intercept: 0 })),
|
||||
|
||||
m('feFlood', { 'flood-color': '#0066ff', 'flood-opacity': '0.5', in: 'SourceGraphic' }),
|
||||
m('feComposite', { operator: 'in', in2: 'SourceGraphic' }),
|
||||
m('feGaussianBlur', { stdDeviation: '1' }), // approx size, with higher values meaning less intensity
|
||||
m('feComponentTransfer', { result: 'glow2' }, m('feFuncA', { type: 'linear', slope: 2, intercept: 0 })),
|
||||
|
||||
m('feMerge', [
|
||||
m('feMergeNode', { in: 'SourceGraphic' }),
|
||||
m('feMergeNode', { in: 'glow2' }),
|
||||
m('feMergeNode', { in: 'glow1' }),
|
||||
]),
|
||||
]),
|
||||
m('g', { width: '150px', height: '150px', filter: 'url(#darker)',}, [
|
||||
m('rect', { width: '100%', height: '100%', fill: 'transparent' }),
|
||||
m('circle', {
|
||||
cx: '50%',
|
||||
cy: '50%',
|
||||
fill: 'transparent',
|
||||
stroke: '#000000',
|
||||
style: `stroke-width: 12%; transform-origin: 50% 50% 0px; transition: stroke-dashoffset 100ms;`,
|
||||
r: 45,
|
||||
}),
|
||||
]),
|
||||
m('g', { width: '150px', height: '150px', filter: 'url(#glower)',}, [
|
||||
m('rect', { width: '100%', height: '100%', fill: 'transparent' }),
|
||||
m('circle', {
|
||||
cx: '50%',
|
||||
cy: '50%',
|
||||
fill: 'transparent',
|
||||
stroke: '#000000',
|
||||
style: `stroke-dashoffset: ${Math.round(280 * percentage)}px; stroke-dasharray: 280px; stroke-width: 10%; transform-origin: 50% 50% 0px; transition: stroke-dashoffset 100ms;`,
|
||||
r: 45,
|
||||
})
|
||||
]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
class Nav {
|
||||
|
@ -94,7 +205,7 @@ class Nav {
|
|||
let path = m.route.get()
|
||||
return [
|
||||
m(m.route.Link, {
|
||||
class: path === '/' ? 'active' : '',
|
||||
class: path === '/' || path === '' ? 'active' : '',
|
||||
href: '/',
|
||||
}, [
|
||||
m('svg', {
|
||||
|
|
|
@ -42,7 +42,9 @@ export function run(http, port, core) {
|
|||
flaska.get('/::file', serve.serve.bind(serve))
|
||||
|
||||
return flaska.listenAsync(port).then(function() {
|
||||
/*const wss = new WebSocketServer({ server: flaska.server })
|
||||
core.log.info('Server is listening on port ' + port)
|
||||
|
||||
const wss = new WebSocketServer({ server: flaska.server })
|
||||
|
||||
wss.on('connection', function(ws) {
|
||||
console.log('new connection')
|
||||
|
@ -88,8 +90,5 @@ export function run(http, port, core) {
|
|||
wss.on('close', function() {
|
||||
clearInterval(interval)
|
||||
})
|
||||
*/
|
||||
|
||||
core.log.info('Server is listening on port ' + port)
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue