fix: close/open websocket on freeze/resume events (#1195)
* fix: close/open websocket on freeze/resume events attempt to address #14 * fix unit tests
This commit is contained in:
parent
0887196db4
commit
70da9a92a6
9
bin/browser-shim.js
Normal file
9
bin/browser-shim.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
// browser shims that run in node, so that page-lifecycle won't error
|
||||
global.self = global
|
||||
global.document = {
|
||||
visibilityState: 'visible',
|
||||
hasFocus: () => true,
|
||||
wasDiscarded: false
|
||||
}
|
||||
global.addEventListener = () => {}
|
||||
global.removeEventListener = () => {}
|
|
@ -28,7 +28,7 @@
|
|||
"testcafe": "run-s testcafe-suite0 testcafe-suite1",
|
||||
"testcafe-suite0": "cross-env-shell testcafe --hostname localhost --skip-js-errors -c 4 $BROWSER tests/spec/0*",
|
||||
"testcafe-suite1": "cross-env-shell testcafe --hostname localhost --skip-js-errors $BROWSER tests/spec/1*",
|
||||
"test-unit": "mocha -r esm tests/unit/",
|
||||
"test-unit": "mocha -r esm -r bin/browser-shim.js tests/unit/",
|
||||
"wait-for-mastodon-to-start": "node -r esm bin/wait-for-mastodon-to-start.js",
|
||||
"wait-for-mastodon-data": "node -r esm bin/wait-for-mastodon-data.js",
|
||||
"deploy-prod": "DEPLOY_TYPE=prod ./bin/deploy.sh",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { paramsString } from '../_utils/ajax'
|
||||
import noop from 'lodash-es/noop'
|
||||
import { importWebSocketClient } from '../_utils/asyncModules'
|
||||
import WebSocketClient from '@gamestdio/websocket'
|
||||
import lifecycle from 'page-lifecycle/dist/lifecycle.mjs'
|
||||
|
||||
function getStreamName (timeline) {
|
||||
switch (timeline) {
|
||||
|
@ -46,27 +47,60 @@ function getUrl (streamingApi, accessToken, timeline) {
|
|||
|
||||
export class TimelineStream {
|
||||
constructor (streamingApi, accessToken, timeline, opts) {
|
||||
let url = getUrl(streamingApi, accessToken, timeline)
|
||||
importWebSocketClient().then(WebSocketClient => {
|
||||
if (this.__closed) {
|
||||
return
|
||||
}
|
||||
const ws = new WebSocketClient(url, null, { backoff: 'exponential' })
|
||||
const onMessage = opts.onMessage || noop
|
||||
|
||||
ws.onopen = opts.onOpen || noop
|
||||
ws.onmessage = e => onMessage(JSON.parse(e.data))
|
||||
ws.onclose = opts.onClose || noop
|
||||
ws.onreconnect = opts.onReconnect || noop
|
||||
|
||||
this._ws = ws
|
||||
})
|
||||
this._streamingApi = streamingApi
|
||||
this._accessToken = accessToken
|
||||
this._timeline = timeline
|
||||
this._opts = opts
|
||||
this._onStateChange = this._onStateChange.bind(this)
|
||||
this._setupWebSocket()
|
||||
this._setupLifecycle()
|
||||
}
|
||||
|
||||
close () {
|
||||
this.__closed = true
|
||||
this._closed = true
|
||||
this._closeWebSocket()
|
||||
this._teardownLifecycle()
|
||||
}
|
||||
|
||||
_closeWebSocket () {
|
||||
if (this._ws) {
|
||||
this._ws.close()
|
||||
this._ws = null
|
||||
}
|
||||
}
|
||||
|
||||
_setupWebSocket () {
|
||||
const url = getUrl(this._streamingApi, this._accessToken, this._timeline)
|
||||
const ws = new WebSocketClient(url, null, { backoff: 'exponential' })
|
||||
|
||||
ws.onopen = this._opts.onOpen || noop
|
||||
ws.onmessage = this._opts.onMessage ? e => this._opts.onMessage(JSON.parse(e.data)) : noop
|
||||
ws.onclose = this._opts.onClose || noop
|
||||
ws.onreconnect = this._opts.onReconnect || noop
|
||||
|
||||
this._ws = ws
|
||||
}
|
||||
|
||||
_setupLifecycle () {
|
||||
lifecycle.addEventListener('statechange', this._onStateChange)
|
||||
}
|
||||
|
||||
_teardownLifecycle () {
|
||||
lifecycle.removeEventListener('statechange', this._onStateChange)
|
||||
}
|
||||
|
||||
_onStateChange (event) {
|
||||
if (this._closed) {
|
||||
return
|
||||
}
|
||||
// when the page enters or exits a frozen state, pause or resume websocket polling
|
||||
if (event.newState === 'frozen') { // page is frozen
|
||||
console.log('frozen')
|
||||
this._closeWebSocket()
|
||||
} else if (event.oldState === 'frozen') { // page is unfrozen
|
||||
console.log('unfrozen')
|
||||
this._closeWebSocket()
|
||||
this._setupWebSocket()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Store } from 'svelte/store'
|
||||
import { safeLocalStorage as LS } from '../_utils/safeLocalStorage'
|
||||
import { importPageLifecycle } from '../_utils/asyncModules'
|
||||
import lifecycle from 'page-lifecycle/dist/lifecycle.mjs'
|
||||
|
||||
function safeParse (str) {
|
||||
return !str ? undefined : (str === 'undefined' ? undefined : JSON.parse(str))
|
||||
|
@ -31,14 +31,12 @@ export class LocalStorageStore extends Store {
|
|||
})
|
||||
})
|
||||
if (process.browser) {
|
||||
importPageLifecycle().then(lifecycle => {
|
||||
lifecycle.addEventListener('statechange', e => {
|
||||
if (e.newState === 'passive') {
|
||||
console.log('saving LocalStorageStore...')
|
||||
this.save()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,6 @@ export const importTimeline = () => import(
|
|||
/* webpackChunkName: 'Timeline' */ '../_components/timeline/Timeline.html'
|
||||
).then(getDefault)
|
||||
|
||||
export const importPageLifecycle = () => import(
|
||||
/* webpackChunkName: 'page-lifecycle' */ 'page-lifecycle/dist/lifecycle.mjs'
|
||||
).then(getDefault)
|
||||
|
||||
export const importWebSocketClient = () => import(
|
||||
/* webpackChunkName: '@gamestdio/websocket' */ '@gamestdio/websocket'
|
||||
).then(getDefault)
|
||||
|
||||
export const importVirtualList = () => import(
|
||||
/* webpackChunkName: 'VirtualList.html' */ '../_components/virtualList/VirtualList.html'
|
||||
).then(getDefault)
|
||||
|
|
|
@ -3,11 +3,14 @@ const config = require('sapper/config/webpack.js')
|
|||
const pkg = require('../package.json')
|
||||
const { mode, dev, resolve, inlineSvgs } = require('./shared.config')
|
||||
|
||||
const serverResolve = JSON.parse(JSON.stringify(resolve))
|
||||
serverResolve.alias['page-lifecycle/dist/lifecycle.mjs'] = 'lodash-es/noop' // page lifecycle fails in Node
|
||||
|
||||
module.exports = {
|
||||
entry: config.server.entry(),
|
||||
output: config.server.output(),
|
||||
target: 'node',
|
||||
resolve,
|
||||
resolve: serverResolve,
|
||||
externals: Object.keys(pkg.dependencies),
|
||||
module: {
|
||||
rules: [
|
||||
|
|
Loading…
Reference in a new issue