140 lines
3.9 KiB
JavaScript
140 lines
3.9 KiB
JavaScript
import { createConsumer } from '@rails/actioncable';
|
|
import Cookies from 'js-cookie';
|
|
|
|
const PRESENCE_INTERVAL = 20000;
|
|
const RECONNECT_INTERVAL = 1000;
|
|
|
|
class BaseActionCableConnector {
|
|
static isDisconnected = false;
|
|
|
|
constructor(
|
|
app,
|
|
pubsubToken,
|
|
websocketHost = '',
|
|
presenceInterval = PRESENCE_INTERVAL,
|
|
useSessionAuth = true
|
|
) {
|
|
this.consumer = null;
|
|
this.subscription = null;
|
|
this.websocketHost = websocketHost;
|
|
this.pubsubToken = pubsubToken;
|
|
this.useSessionAuth = useSessionAuth;
|
|
this.app = app;
|
|
this.events = {};
|
|
this.reconnectTimer = null;
|
|
this.ticketFailureLogged = false;
|
|
this.isAValidEvent = () => true;
|
|
this.connect();
|
|
this.triggerPresenceInterval = () => {
|
|
setTimeout(() => {
|
|
this.subscription?.updatePresence();
|
|
this.triggerPresenceInterval();
|
|
}, presenceInterval);
|
|
};
|
|
this.triggerPresenceInterval();
|
|
}
|
|
|
|
async connect() {
|
|
const wsOrigin = this.websocketHost || window.location.origin;
|
|
let websocketURL = `${wsOrigin}/cable`;
|
|
if (this.useSessionAuth && Cookies.get('cw_d_session_state')) {
|
|
try {
|
|
const response = await window.axios.post('/api/v1/auth/ws_ticket');
|
|
const ticket = response.data?.data?.ticket;
|
|
if (!ticket) throw new Error('missing websocket ticket');
|
|
websocketURL += `?ticket=${encodeURIComponent(ticket)}`;
|
|
this.usesWSTicket = true;
|
|
this.ticketFailureLogged = false;
|
|
} catch (error) {
|
|
if (!this.ticketFailureLogged) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('WebSocket ticket exchange failed; retrying');
|
|
this.ticketFailureLogged = true;
|
|
}
|
|
this.initReconnectTimer();
|
|
return;
|
|
}
|
|
} else if (this.pubsubToken) {
|
|
websocketURL += `?pubsub_token=${encodeURIComponent(this.pubsubToken)}`;
|
|
this.usesWSTicket = false;
|
|
}
|
|
|
|
this.consumer = createConsumer(websocketURL);
|
|
this.subscription = this.consumer.subscriptions.create(
|
|
{
|
|
channel: 'RoomChannel',
|
|
pubsub_token: this.pubsubToken,
|
|
account_id: this.app.$store.getters.getCurrentAccountId,
|
|
user_id: this.app.$store.getters.getCurrentUserID,
|
|
},
|
|
{
|
|
updatePresence() {
|
|
this.perform('update_presence');
|
|
},
|
|
received: this.onReceived,
|
|
disconnected: () => {
|
|
BaseActionCableConnector.isDisconnected = true;
|
|
if (this.usesWSTicket) {
|
|
this.consumer?.disconnect();
|
|
this.consumer = null;
|
|
this.subscription = null;
|
|
}
|
|
this.onDisconnected();
|
|
this.initReconnectTimer();
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
checkConnection() {
|
|
if (!this.consumer) {
|
|
this.connect();
|
|
return;
|
|
}
|
|
const isConnectionActive = this.consumer.connection.isOpen();
|
|
const isReconnected =
|
|
BaseActionCableConnector.isDisconnected && isConnectionActive;
|
|
if (isReconnected) {
|
|
this.clearReconnectTimer();
|
|
this.onReconnect();
|
|
BaseActionCableConnector.isDisconnected = false;
|
|
} else {
|
|
this.initReconnectTimer();
|
|
}
|
|
}
|
|
|
|
clearReconnectTimer = () => {
|
|
if (this.reconnectTimer) {
|
|
clearTimeout(this.reconnectTimer);
|
|
this.reconnectTimer = null;
|
|
}
|
|
};
|
|
|
|
initReconnectTimer = () => {
|
|
this.clearReconnectTimer();
|
|
this.reconnectTimer = setTimeout(() => {
|
|
this.checkConnection();
|
|
}, RECONNECT_INTERVAL);
|
|
};
|
|
|
|
// eslint-disable-next-line class-methods-use-this
|
|
onReconnect = () => {};
|
|
|
|
// eslint-disable-next-line class-methods-use-this
|
|
onDisconnected = () => {};
|
|
|
|
disconnect() {
|
|
this.consumer?.disconnect();
|
|
}
|
|
|
|
onReceived = ({ event, data } = {}) => {
|
|
if (this.isAValidEvent(data)) {
|
|
if (this.events[event] && typeof this.events[event] === 'function') {
|
|
this.events[event](data);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
export default BaseActionCableConnector;
|