ignite-jhipster
Version:
A React Native boilerplate for JHipster apps.
210 lines (185 loc) • 4.8 kB
JavaScript
import { call, take } from 'redux-saga/effects'
import { eventChannel } from 'redux-saga'
import SockJS from 'sockjs-client'
import AppConfig from '../../config/app-config'
import { processWebsocketMessage } from './websocket.sagas'
const webstomp = require('stompjs')
let em
let connection = createConnection()
let alreadyConnectedOnce = false
let connected = false
let initialConnect = true
let connectedPromise = null
let socket = null
let stompClient = null
let wsSessionId = null
let token = null
let attempts = 0
let chatSubscriber
const subscriptions = {}
function onConnect () {
setWsSessionId(socket)
connectedPromise('success')
connectedPromise = null
connected = true
if (!initialConnect) {
console.tron.log('Resubscribing...')
Object.keys(subscriptions).forEach(function (key) {
if (key === 'chat') {
subscribeToChat()
}
})
}
initialConnect = false
}
function onError (error) {
console.tron.error(`Error connecting to websockets ${error}`)
const time = generateInterval(attempts)
alreadyConnectedOnce = false
connected = false
connectedPromise = null
setTimeout(function () {
attempts++
connect()
}, time)
console.tron.error(`STOMP: Reconnecting in ${Math.round(time / 1000)} seconds`)
}
function createConnection () {
return new Promise((resolve, reject) => {
connectedPromise = resolve
})
}
// methods for connecting/disconnecting
function connect () {
// connection
if (!alreadyConnectedOnce) {
if (connectedPromise === null) connection = createConnection()
var url = AppConfig.apiUrl + 'websocket/chat'
if (token) {
url += '?access_token=' + token
}
socket = new SockJS(url)
stompClient = webstomp.over(socket)
// stompClient.debug = null
console.tron.log(`Connecting to ${url}`)
var headers = {}
stompClient.connect(headers, onConnect, onError)
alreadyConnectedOnce = true
}
}
function disconnect () {
console.tron.log('Disconnecting')
if (stompClient !== null && connected) {
connectedPromise = null
stompClient.disconnect()
stompClient = null
}
connected = false
alreadyConnectedOnce = false
}
// methods for subscribing
function subscribeToChat () {
console.tron.log('Subscribing to Chat')
if (!Object.prototype.hasOwnProperty.call(subscriptions, 'chat')) {
subscriptions.chat = { subscribed: true }
connection.then(() => {
chatSubscriber = stompClient.subscribe('/topic/chat', onMessage.bind(this, 'chat'))
})
}
}
// methods for unsubscribing
function unsubscribeChat () {
delete subscriptions.chat
if (chatSubscriber) {
chatSubscriber.unsubscribe()
}
}
// methods for sending
function sendChat (ev) {
if (stompClient !== null && stompClient.connected) {
var p = '/topic/chat'
stompClient.send(p, {}, JSON.stringify(ev))
}
}
// when the message is received, send it to the WebsocketSaga
function onMessage (subscription, fullMessage) {
let msg = null
try {
msg = JSON.parse(fullMessage.body)
} catch (e) {
console.tron.error(`Error parsing : ${fullMessage}`)
console.tron.error(e)
}
if (msg) {
return em({ subscription, msg })
}
}
// getters and setters
function getToken () {
return token
}
function setToken (jwtToken) {
token = jwtToken
if (connected) {
disconnect()
connect()
}
}
function getSubscriptions () {
return subscriptions
}
function getWsSessionId () {
return wsSessionId
}
function setWsSessionId (socketObject) {
const splitUrl = socketObject._transport.url.split('/')
wsSessionId = splitUrl[5]
console.tron.log(`Set WS Session ID to ${wsSessionId}`)
}
function getConnectedPromise () {
return connection
}
// exponential backoff for reconnections
function generateInterval (k) {
let maxInterval = (Math.pow(2, k) - 1) * 1000
if (maxInterval > 30 * 1000) {
// If the generated interval is more than 30 seconds, truncate it down to 30 seconds.
maxInterval = 30 * 1000
}
// generate the interval to a random number between 0 and the maxInterval determined from above
return Math.random() * maxInterval
}
export default {
onConnect,
onError,
subscribeToChat,
unsubscribeChat,
connect,
disconnect,
createConnection,
sendChat,
getSubscriptions,
getToken,
setToken,
getWsSessionId,
getConnectedPromise,
initWebsocket,
websocketSagas
}
// connects to the websocket and sends events
function initWebsocket () {
return eventChannel(emitter => {
em = emitter
// unsubscribe function
return () => {
console.tron.log('Socket off')
}
})
}
function * websocketSagas () {
const channel = yield call(initWebsocket)
while (true) {
const action = yield take(channel)
yield call(processWebsocketMessage, action)
}
}