Websocket refactor: Pull it out of the UI and support callbacks (#104)
* Websocket refactor: Pull it out of the UI and support listeners * Changes required for Safari to be happy with modules * Move to explicit ad-hoc callback registration
This commit is contained in:
@@ -181,16 +181,16 @@
|
|||||||
|
|
||||||
<script src="js/usercolors.js"></script>
|
<script src="js/usercolors.js"></script>
|
||||||
<script src="js/utils.js?v=2"></script>
|
<script src="js/utils.js?v=2"></script>
|
||||||
<script src="js/message.js?v=2"></script>
|
<script type="module" src="js/message.js?v=2"></script>
|
||||||
<script src="js/social.js"></script>
|
<script src="js/social.js"></script>
|
||||||
<script src="js/components.js"></script>
|
<script src="js/components.js"></script>
|
||||||
<script src="js/player.js"></script>
|
<script type="module">
|
||||||
<script src="js/app.js?v=2"></script>
|
import Owncast from './js/app.js';
|
||||||
<script>
|
|
||||||
(function () {
|
(function () {
|
||||||
const app = new Owncast();
|
const app = new Owncast();
|
||||||
app.init();
|
app.init();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|||||||
@@ -1,14 +1,33 @@
|
|||||||
|
import Websocket from './websocket.js';
|
||||||
|
import { MessagingInterface, Message } from './message.js';
|
||||||
|
import SOCKET_MESSAGE_TYPES from './chat/socketMessageTypes.js';
|
||||||
|
import { OwncastPlayer } from './player.js';
|
||||||
|
|
||||||
|
const MESSAGE_OFFLINE = 'Stream is offline.';
|
||||||
|
const MESSAGE_ONLINE = 'Stream is online';
|
||||||
|
|
||||||
|
const TEMP_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
||||||
|
|
||||||
|
const LOCAL_TEST = window.location.href.indexOf('localhost:') >= 0;
|
||||||
|
|
||||||
|
const URL_PREFIX = LOCAL_TEST ? 'http://localhost:8080' : '';
|
||||||
|
const URL_CONFIG = `${URL_PREFIX}/config`;
|
||||||
|
const URL_STATUS = `${URL_PREFIX}/status`;
|
||||||
|
const URL_CHAT_HISTORY = `${URL_PREFIX}/chat`;
|
||||||
|
|
||||||
|
const TIMER_STATUS_UPDATE = 5000; // ms
|
||||||
|
const TIMER_DISABLE_CHAT_AFTER_OFFLINE = 5 * 60 * 1000; // 5 mins
|
||||||
|
const TIMER_STREAM_DURATION_COUNTER = 1000;
|
||||||
|
|
||||||
class Owncast {
|
class Owncast {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.player;
|
this.player;
|
||||||
|
|
||||||
this.websocket = null;
|
|
||||||
this.configData;
|
this.configData;
|
||||||
this.vueApp;
|
this.vueApp;
|
||||||
this.messagingInterface = null;
|
this.messagingInterface = null;
|
||||||
|
|
||||||
// timers
|
// timers
|
||||||
this.websocketReconnectTimer = null;
|
|
||||||
this.playerRestartTimer = null;
|
this.playerRestartTimer = null;
|
||||||
this.offlineTimer = null;
|
this.offlineTimer = null;
|
||||||
this.statusTimer = null;
|
this.statusTimer = null;
|
||||||
@@ -23,7 +42,6 @@ class Owncast {
|
|||||||
// bindings
|
// bindings
|
||||||
this.vueAppMounted = this.vueAppMounted.bind(this);
|
this.vueAppMounted = this.vueAppMounted.bind(this);
|
||||||
this.setConfigData = this.setConfigData.bind(this);
|
this.setConfigData = this.setConfigData.bind(this);
|
||||||
this.setupWebsocket = this.setupWebsocket.bind(this);
|
|
||||||
this.getStreamStatus = this.getStreamStatus.bind(this);
|
this.getStreamStatus = this.getStreamStatus.bind(this);
|
||||||
this.getExtraUserContent = this.getExtraUserContent.bind(this);
|
this.getExtraUserContent = this.getExtraUserContent.bind(this);
|
||||||
this.updateStreamStatus = this.updateStreamStatus.bind(this);
|
this.updateStreamStatus = this.updateStreamStatus.bind(this);
|
||||||
@@ -40,7 +58,7 @@ class Owncast {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.messagingInterface = new MessagingInterface();
|
this.messagingInterface = new MessagingInterface();
|
||||||
this.websocket = this.setupWebsocket();
|
this.setupWebsocket();
|
||||||
|
|
||||||
this.vueApp = new Vue({
|
this.vueApp = new Vue({
|
||||||
el: '#app-container',
|
el: '#app-container',
|
||||||
@@ -109,53 +127,17 @@ class Owncast {
|
|||||||
|
|
||||||
// websocket for messaging
|
// websocket for messaging
|
||||||
setupWebsocket() {
|
setupWebsocket() {
|
||||||
var ws = new WebSocket(URL_WEBSOCKET);
|
this.websocket = new Websocket();
|
||||||
ws.onopen = (e) => {
|
this.websocket.addListener('rawWebsocketMessageReceived', this.receivedWebsocketMessage.bind(this));
|
||||||
if (this.websocketReconnectTimer) {
|
this.messagingInterface.send = this.websocket.send;
|
||||||
clearTimeout(this.websocketReconnectTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're "online" then enable the chat.
|
|
||||||
if (this.streamStatus && this.streamStatus.online) {
|
|
||||||
this.messagingInterface.enableChat();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ws.onclose = (e) => {
|
|
||||||
// connection closed, discard old websocket and create a new one in 5s
|
|
||||||
this.websocket = null;
|
|
||||||
this.messagingInterface.disableChat();
|
|
||||||
this.handleNetworkingError('Websocket closed.');
|
|
||||||
this.websocketReconnectTimer = setTimeout(this.setupWebsocket, TIMER_WEBSOCKET_RECONNECT);
|
|
||||||
};
|
|
||||||
// On ws error just close the socket and let it re-connect again for now.
|
|
||||||
ws.onerror = e => {
|
|
||||||
this.handleNetworkingError(`Socket error: ${JSON.parse(e)}`);
|
|
||||||
ws.close();
|
|
||||||
};
|
|
||||||
ws.onmessage = (e) => {
|
|
||||||
const model = JSON.parse(e.data);
|
|
||||||
|
|
||||||
// Send PONGs
|
|
||||||
if (model.type === SOCKET_MESSAGE_TYPES.PING) {
|
|
||||||
this.sendPong(ws);
|
|
||||||
return;
|
|
||||||
} else if (model.type === SOCKET_MESSAGE_TYPES.CHAT) {
|
|
||||||
const message = new Message(model);
|
|
||||||
this.addMessage(message);
|
|
||||||
} else if (model.type === SOCKET_MESSAGE_TYPES.NAME_CHANGE) {
|
|
||||||
this.addMessage(model);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.websocket = ws;
|
|
||||||
this.messagingInterface.setWebsocket(this.websocket);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sendPong(ws) {
|
receivedWebsocketMessage(model) {
|
||||||
try {
|
if (model.type === SOCKET_MESSAGE_TYPES.CHAT) {
|
||||||
const pong = { type: SOCKET_MESSAGE_TYPES.PONG };
|
const message = new Message(model);
|
||||||
ws.send(JSON.stringify(pong));
|
this.addMessage(message);
|
||||||
} catch (e) {
|
} else if (model.type === SOCKET_MESSAGE_TYPES.NAME_CHANGE) {
|
||||||
console.log('PONG error:', e);
|
this.addMessage(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,3 +331,5 @@ class Owncast {
|
|||||||
this.handlePlayerEnded();
|
this.handlePlayerEnded();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default Owncast;
|
||||||
11
webroot/js/chat/socketMessageTypes.js
Normal file
11
webroot/js/chat/socketMessageTypes.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* These are the types of messages that we can handle with the websocket.
|
||||||
|
* Mostly used by `websocket.js` but if other components need to handle
|
||||||
|
* different types then it can import this file.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
CHAT: 'CHAT',
|
||||||
|
PING: 'PING',
|
||||||
|
NAME_CHANGE: 'NAME_CHANGE',
|
||||||
|
PONG: 'PONG'
|
||||||
|
}
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
import SOCKET_MESSAGE_TYPES from './chat/socketMessageTypes.js';
|
||||||
|
|
||||||
|
const KEY_USERNAME = 'owncast_username';
|
||||||
|
const KEY_AVATAR = 'owncast_avatar';
|
||||||
|
const KEY_CHAT_DISPLAYED = 'owncast_chat';
|
||||||
|
const KEY_CHAT_FIRST_MESSAGE_SENT = 'owncast_first_message_sent';
|
||||||
|
const CHAT_INITIAL_PLACEHOLDER_TEXT = 'Type here to chat, no account necessary.';
|
||||||
|
const CHAT_PLACEHOLDER_TEXT = 'Message';
|
||||||
|
const CHAT_PLACEHOLDER_OFFLINE = 'Chat is offline.';
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
constructor(model) {
|
constructor(model) {
|
||||||
this.author = model.author;
|
this.author = model.author;
|
||||||
@@ -36,7 +46,6 @@ class Message {
|
|||||||
|
|
||||||
class MessagingInterface {
|
class MessagingInterface {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.websocket = null;
|
|
||||||
this.chatDisplayed = false;
|
this.chatDisplayed = false;
|
||||||
this.username = '';
|
this.username = '';
|
||||||
this.messageCharCount = 0;
|
this.messageCharCount = 0;
|
||||||
@@ -89,11 +98,6 @@ class MessagingInterface {
|
|||||||
window.addEventListener("orientationchange", setVHvar);
|
window.addEventListener("orientationchange", setVHvar);
|
||||||
this.tagAppContainer.classList.add('touch-screen');
|
this.tagAppContainer.classList.add('touch-screen');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
setWebsocket(socket) {
|
|
||||||
this.websocket = socket;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initLocalStates() {
|
initLocalStates() {
|
||||||
@@ -188,9 +192,7 @@ class MessagingInterface {
|
|||||||
image: image,
|
image: image,
|
||||||
};
|
};
|
||||||
|
|
||||||
const jsonMessage = JSON.stringify(nameChange);
|
this.send(nameChange);
|
||||||
|
|
||||||
this.websocket.send(jsonMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageInputKeydown(event) {
|
handleMessageInputKeydown(event) {
|
||||||
@@ -252,15 +254,7 @@ class MessagingInterface {
|
|||||||
image: this.imgUsernameAvatar.src,
|
image: this.imgUsernameAvatar.src,
|
||||||
type: SOCKET_MESSAGE_TYPES.CHAT,
|
type: SOCKET_MESSAGE_TYPES.CHAT,
|
||||||
});
|
});
|
||||||
const messageJSON = JSON.stringify(message);
|
this.send(message);
|
||||||
if (this.websocket) {
|
|
||||||
try {
|
|
||||||
this.websocket.send(messageJSON);
|
|
||||||
} catch(e) {
|
|
||||||
console.log('Message send error:', e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear out things.
|
// clear out things.
|
||||||
this.formMessageInput.value = '';
|
this.formMessageInput.value = '';
|
||||||
@@ -298,4 +292,10 @@ class MessagingInterface {
|
|||||||
jumpToBottom(this.scrollableMessagesContainer);
|
jumpToBottom(this.scrollableMessagesContainer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
send(messageJSON) {
|
||||||
|
console.error('MessagingInterface send() is not linked to the websocket component.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Message, MessagingInterface }
|
||||||
@@ -1,5 +1,33 @@
|
|||||||
// https://docs.videojs.com/player
|
// https://docs.videojs.com/player
|
||||||
|
|
||||||
|
const VIDEO_ID = 'video';
|
||||||
|
const LOCAL_TEST = window.location.href.indexOf('localhost:') >= 0;
|
||||||
|
const URL_PREFIX = LOCAL_TEST ? 'http://localhost:8080' : '';
|
||||||
|
const URL_STREAM = `${URL_PREFIX}/hls/stream.m3u8`;
|
||||||
|
|
||||||
|
// Video setup
|
||||||
|
const VIDEO_SRC = {
|
||||||
|
src: URL_STREAM,
|
||||||
|
type: 'application/x-mpegURL',
|
||||||
|
};
|
||||||
|
const VIDEO_OPTIONS = {
|
||||||
|
autoplay: false,
|
||||||
|
liveui: true, // try this
|
||||||
|
preload: 'auto',
|
||||||
|
html5: {
|
||||||
|
vhs: {
|
||||||
|
// used to select the lowest bitrate playlist initially. This helps to decrease playback start time. This setting is false by default.
|
||||||
|
enableLowInitialPlaylist: true,
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
liveTracker: {
|
||||||
|
trackingThreshold: 0,
|
||||||
|
},
|
||||||
|
sources: [VIDEO_SRC],
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class OwncastPlayer {
|
class OwncastPlayer {
|
||||||
constructor() {
|
constructor() {
|
||||||
window.VIDEOJS_NO_DYNAMIC_STYLE = true; // style override
|
window.VIDEOJS_NO_DYNAMIC_STYLE = true; // style override
|
||||||
@@ -124,3 +152,4 @@ class OwncastPlayer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { OwncastPlayer };
|
||||||
@@ -1,75 +1,12 @@
|
|||||||
|
|
||||||
const LOCAL_TEST = window.location.href.indexOf('localhost:') >= 0;
|
const LOCAL_TEST = window.location.href.indexOf('localhost:') >= 0;
|
||||||
|
|
||||||
|
|
||||||
const URL_PREFIX = LOCAL_TEST ? 'http://localhost:8080' : '';
|
const URL_PREFIX = LOCAL_TEST ? 'http://localhost:8080' : '';
|
||||||
const URL_STATUS = `${URL_PREFIX}/status`;
|
|
||||||
const URL_CHAT_HISTORY = `${URL_PREFIX}/chat`;
|
|
||||||
const URL_STREAM = `${URL_PREFIX}/hls/stream.m3u8`;
|
|
||||||
const URL_WEBSOCKET = LOCAL_TEST
|
|
||||||
? 'wss://goth.land/entry'
|
|
||||||
: `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/entry`;
|
|
||||||
|
|
||||||
const POSTER_DEFAULT = `${URL_PREFIX}/img/logo.png`;
|
const POSTER_DEFAULT = `${URL_PREFIX}/img/logo.png`;
|
||||||
const POSTER_THUMB = `${URL_PREFIX}/thumbnail.jpg`;
|
const POSTER_THUMB = `${URL_PREFIX}/thumbnail.jpg`;
|
||||||
|
|
||||||
const URL_CONFIG = `${URL_PREFIX}/config`;
|
|
||||||
|
|
||||||
const URL_OWNCAST = 'https://github.com/gabek/owncast'; // used in footer
|
const URL_OWNCAST = 'https://github.com/gabek/owncast'; // used in footer
|
||||||
|
|
||||||
|
|
||||||
// Webscoket setup
|
|
||||||
const SOCKET_MESSAGE_TYPES = {
|
|
||||||
CHAT: 'CHAT',
|
|
||||||
PING: 'PING',
|
|
||||||
NAME_CHANGE: 'NAME_CHANGE',
|
|
||||||
PONG: 'PONG'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Video setup
|
|
||||||
const VIDEO_ID = 'video';
|
|
||||||
const VIDEO_SRC = {
|
|
||||||
src: URL_STREAM,
|
|
||||||
type: 'application/x-mpegURL',
|
|
||||||
};
|
|
||||||
const VIDEO_OPTIONS = {
|
|
||||||
autoplay: false,
|
|
||||||
liveui: true, // try this
|
|
||||||
preload: 'auto',
|
|
||||||
html5: {
|
|
||||||
vhs: {
|
|
||||||
// used to select the lowest bitrate playlist initially. This helps to decrease playback start time. This setting is false by default.
|
|
||||||
enableLowInitialPlaylist: true,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
liveTracker: {
|
|
||||||
trackingThreshold: 0,
|
|
||||||
},
|
|
||||||
sources: [VIDEO_SRC],
|
|
||||||
};
|
|
||||||
|
|
||||||
// local storage keys for chat
|
|
||||||
const KEY_USERNAME = 'owncast_username';
|
|
||||||
const KEY_AVATAR = 'owncast_avatar';
|
|
||||||
const KEY_CHAT_DISPLAYED = 'owncast_chat';
|
|
||||||
const KEY_CHAT_FIRST_MESSAGE_SENT = 'owncast_first_message_sent';
|
|
||||||
|
|
||||||
const TIMER_STATUS_UPDATE = 5000; // ms
|
|
||||||
const TIMER_WEBSOCKET_RECONNECT = 5000; // ms
|
|
||||||
const TIMER_DISABLE_CHAT_AFTER_OFFLINE = 5 * 60 * 1000; // 5 mins
|
|
||||||
const TIMER_STREAM_DURATION_COUNTER = 1000;
|
|
||||||
|
|
||||||
const TEMP_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
|
||||||
|
|
||||||
const MESSAGE_OFFLINE = 'Stream is offline.';
|
|
||||||
const MESSAGE_ONLINE = 'Stream is online';
|
|
||||||
|
|
||||||
const CHAT_INITIAL_PLACEHOLDER_TEXT = 'Type here to chat, no account necessary.';
|
|
||||||
const CHAT_PLACEHOLDER_TEXT = 'Message';
|
|
||||||
const CHAT_PLACEHOLDER_OFFLINE = 'Chat is offline.';
|
|
||||||
|
|
||||||
|
|
||||||
function getLocalStorage(key) {
|
function getLocalStorage(key) {
|
||||||
try {
|
try {
|
||||||
return localStorage.getItem(key);
|
return localStorage.getItem(key);
|
||||||
@@ -182,3 +119,7 @@ function setVHvar() {
|
|||||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||||
console.log("== new vh", vh)
|
console.log("== new vh", vh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function doesObjectSupportFunction(object, functionName) {
|
||||||
|
return typeof object[functionName] === "function";
|
||||||
|
}
|
||||||
142
webroot/js/websocket.js
Normal file
142
webroot/js/websocket.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import SOCKET_MESSAGE_TYPES from './chat/socketMessageTypes.js';
|
||||||
|
|
||||||
|
const LOCAL_TEST = window.location.href.indexOf('localhost:') >= 0;
|
||||||
|
const URL_WEBSOCKET = LOCAL_TEST
|
||||||
|
? 'wss://goth.land/entry'
|
||||||
|
: `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/entry`;
|
||||||
|
|
||||||
|
const TIMER_WEBSOCKET_RECONNECT = 5000; // ms
|
||||||
|
|
||||||
|
const CALLBACKS = {
|
||||||
|
RAW_WEBSOCKET_MESSAGE_RECEIVED: 'rawWebsocketMessageReceived',
|
||||||
|
WEBSOCKET_CONNECTED: 'websocketConnected',
|
||||||
|
WEBSOCKET_DISCONNECTED: 'websocketDisconnected',
|
||||||
|
}
|
||||||
|
|
||||||
|
class Websocket {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.websocket = null;
|
||||||
|
this.websocketReconnectTimer = null;
|
||||||
|
|
||||||
|
this.websocketConnectedListeners = [];
|
||||||
|
this.websocketDisconnectListeners = [];
|
||||||
|
this.rawMessageListeners = [];
|
||||||
|
|
||||||
|
this.send = this.send.bind(this);
|
||||||
|
|
||||||
|
const ws = new WebSocket(URL_WEBSOCKET);
|
||||||
|
ws.onopen = this.onOpen.bind(this);
|
||||||
|
ws.onclose = this.onClose.bind(this);
|
||||||
|
ws.onerror = this.onError.bind(this);
|
||||||
|
ws.onmessage = this.onMessage.bind(this);
|
||||||
|
|
||||||
|
this.websocket = ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other components should register for websocket callbacks.
|
||||||
|
addListener(type, callback) {
|
||||||
|
if (type == CALLBACKS.WEBSOCKET_CONNECTED) {
|
||||||
|
this.websocketConnectedListeners.push(callback);
|
||||||
|
} else if (type == CALLBACKS.WEBSOCKET_DISCONNECTED) {
|
||||||
|
this.websocketDisconnectListeners.push(callback);
|
||||||
|
} else if (type == CALLBACKS.RAW_WEBSOCKET_MESSAGE_RECEIVED) {
|
||||||
|
this.rawMessageListeners.push(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Interface with other components
|
||||||
|
|
||||||
|
// Outbound: Other components can pass an object to `send`.
|
||||||
|
send(message) {
|
||||||
|
// Sanity check that what we're sending is a valid type.
|
||||||
|
if (!message.type || !SOCKET_MESSAGE_TYPES[message.type]) {
|
||||||
|
console.warn(`Outbound message: Unknown socket message type: "${message.type}" sent.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageJSON = JSON.stringify(message);
|
||||||
|
this.websocket.send(messageJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private methods
|
||||||
|
|
||||||
|
// Fire the callbacks of the listeners.
|
||||||
|
|
||||||
|
notifyWebsocketConnectedListeners(message) {
|
||||||
|
this.websocketConnectedListeners.forEach(function (callback) {
|
||||||
|
callback(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyWebsocketDisconnectedListeners(message) {
|
||||||
|
this.websocketDisconnectListeners.forEach(function (callback) {
|
||||||
|
callback(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notifyRawMessageListeners(message) {
|
||||||
|
this.rawMessageListeners.forEach(function (callback) {
|
||||||
|
callback(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal websocket callbacks
|
||||||
|
|
||||||
|
onOpen(e) {
|
||||||
|
if (this.websocketReconnectTimer) {
|
||||||
|
clearTimeout(this.websocketReconnectTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifyWebsocketConnectedListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose(e) {
|
||||||
|
// connection closed, discard old websocket and create a new one in 5s
|
||||||
|
this.websocket = null;
|
||||||
|
this.notifyWebsocketDisconnectedListeners();
|
||||||
|
this.handleNetworkingError('Websocket closed.');
|
||||||
|
this.websocketReconnectTimer = setTimeout(this.setupWebsocket, TIMER_WEBSOCKET_RECONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// On ws error just close the socket and let it re-connect again for now.
|
||||||
|
onError(e) {
|
||||||
|
this.handleNetworkingError(`Socket error: ${JSON.parse(e)}`);
|
||||||
|
this.websocket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
onMessage is fired when an inbound object comes across the websocket.
|
||||||
|
If the message is of type `PING` we send a `PONG` back and do not
|
||||||
|
pass it along to listeners.
|
||||||
|
*/
|
||||||
|
onMessage(e) {
|
||||||
|
try {
|
||||||
|
var model = JSON.parse(e.data);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send PONGs
|
||||||
|
if (model.type === SOCKET_MESSAGE_TYPES.PING) {
|
||||||
|
this.sendPong();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify any of the listeners via the raw socket message callback.
|
||||||
|
this.notifyRawMessageListeners(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply to a PING as a keep alive.
|
||||||
|
sendPong() {
|
||||||
|
const pong = { type: SOCKET_MESSAGE_TYPES.PONG };
|
||||||
|
this.send(pong);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNetworkingError(error) {
|
||||||
|
console.error(`Websocket Error: ${error}`)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Websocket;
|
||||||
Reference in New Issue
Block a user