Cleanup socketio stuff for typescript

This commit is contained in:
Dane Everitt 2018-12-16 18:57:34 -08:00
parent 3ad4422a94
commit 5e4ca8ef83
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
22 changed files with 246 additions and 210 deletions

View file

@ -1,103 +0,0 @@
import io from 'socket.io-client';
import camelCase from 'camelcase';
import SocketEmitter from './emitter';
const SYSTEM_EVENTS = [
'connect',
'error',
'disconnect',
'reconnect',
'reconnect_attempt',
'reconnecting',
'reconnect_error',
'reconnect_failed',
'connect_error',
'connect_timeout',
'connecting',
'ping',
'pong',
];
export default class SocketioConnector {
constructor (store = null) {
this.socket = null;
this.store = store;
}
/**
* Initialize a new Socket connection.
*
* @param {io} socket
*/
connect (socket) {
if (!socket instanceof io) {
throw new Error('First argument passed to connect() should be an instance of socket.io-client.');
}
this.socket = socket;
this.registerEventListeners();
}
/**
* Return the socket instance we are working with.
*
* @return {io|null}
*/
instance () {
return this.socket;
}
/**
* Register the event listeners for this socket including user-defined ones in the store as
* well as global system events from Socekt.io.
*/
registerEventListeners () {
this.socket['onevent'] = (packet) => {
const [event, ...args] = packet.data;
SocketEmitter.emit(event, ...args);
this.passToStore(event, args);
};
SYSTEM_EVENTS.forEach((event) => {
this.socket.on(event, (payload) => {
SocketEmitter.emit(event, payload);
this.passToStore(event, payload);
})
});
}
/**
* Pass event calls off to the Vuex store if there is a corresponding function.
*
* @param {String|Number|Symbol} event
* @param {Array} payload
*/
passToStore (event, payload) {
if (!this.store) {
return;
}
const mutation = `SOCKET_${event.toUpperCase()}`;
const action = `socket_${camelCase(event)}`;
Object.keys(this.store._mutations).filter((namespaced) => {
return namespaced.split('/').pop() === mutation;
}).forEach((namespaced) => {
this.store.commit(namespaced, this.unwrap(payload));
});
Object.keys(this.store._actions).filter((namespaced) => {
return namespaced.split('/').pop() === action;
}).forEach((namespaced) => {
this.store.dispatch(namespaced, this.unwrap(payload));
});
}
/**
* @param {Array} args
* @return {Array<Object>|Object}
*/
unwrap (args) {
return (args && args.length <= 1) ? args[0] : args;
}
}

View file

@ -0,0 +1,115 @@
import * as io from 'socket.io-client';
import {camelCase} from 'lodash';
import SocketEmitter from './emitter';
import {Store} from "vuex";
const SYSTEM_EVENTS: Array<string> = [
'connect',
'error',
'disconnect',
'reconnect',
'reconnect_attempt',
'reconnecting',
'reconnect_error',
'reconnect_failed',
'connect_error',
'connect_timeout',
'connecting',
'ping',
'pong',
];
export default class SocketioConnector {
/**
* The socket instance.
*/
socket: null | SocketIOClient.Socket;
/**
* The vuex store being used to persist data and socket state.
*/
store: Store<any> | undefined;
constructor(store: Store<any> | undefined) {
this.socket = null;
this.store = store;
}
/**
* Initialize a new Socket connection.
*
* @param {io} socket
*/
connect(socket: SocketIOClient.Socket) {
this.socket = socket;
this.registerEventListeners();
}
/**
* Return the socket instance we are working with.
*/
instance(): SocketIOClient.Socket | null {
return this.socket;
}
/**
* Register the event listeners for this socket including user-defined ones in the store as
* well as global system events from Socekt.io.
*/
registerEventListeners() {
if (!this.socket) {
return;
}
// @ts-ignore
this.socket['onevent'] = (packet: { data: Array<any> }): void => {
const [event, ...args] = packet.data;
SocketEmitter.emit(event, ...args);
this.passToStore(event, args);
};
SYSTEM_EVENTS.forEach((event: string): void => {
if (!this.socket) {
return;
}
this.socket.on(event, (payload: any) => {
SocketEmitter.emit(event, payload);
this.passToStore(event, payload);
});
});
}
/**
* Pass event calls off to the Vuex store if there is a corresponding function.
*/
passToStore(event: string | number, payload: Array<any>) {
if (!this.store) {
return;
}
const s: Store<any> = this.store;
const mutation = `SOCKET_${String(event).toUpperCase()}`;
const action = `socket_${camelCase(String(event))}`;
// @ts-ignore
Object.keys(this.store._mutations).filter((namespaced: string): boolean => {
return namespaced.split('/').pop() === mutation;
}).forEach((namespaced: string): void => {
s.commit(namespaced, this.unwrap(payload));
});
// @ts-ignore
Object.keys(this.store._actions).filter((namespaced: string): boolean => {
return namespaced.split('/').pop() === action;
}).forEach((namespaced: string): void => {
s.dispatch(namespaced, this.unwrap(payload)).catch(console.error);
});
}
unwrap(args: Array<any>) {
return (args && args.length <= 1) ? args[0] : args;
}
}

View file

@ -1,18 +1,21 @@
import isFunction from 'lodash/isFunction';
import {isFunction} from 'lodash';
import {ComponentOptions} from "vue";
import {Vue} from "vue/types/vue";
export default new class SocketEmitter {
constructor () {
listeners: Map<string | number, Array<{
callback: (a: ComponentOptions<Vue>) => void,
vm: ComponentOptions<Vue>,
}>>;
constructor() {
this.listeners = new Map();
}
/**
* Add an event listener for socket events.
*
* @param {String|Number|Symbol} event
* @param {Function} callback
* @param {*} vm
*/
addListener (event, callback, vm) {
addListener(event: string | number, callback: (data: any) => void, vm: ComponentOptions<Vue>) {
if (!isFunction(callback)) {
return;
}
@ -21,21 +24,19 @@ export default new class SocketEmitter {
this.listeners.set(event, []);
}
// @ts-ignore
this.listeners.get(event).push({callback, vm});
}
/**
* Remove an event listener for socket events based on the context passed through.
*
* @param {String|Number|Symbol} event
* @param {Function} callback
* @param {*} vm
*/
removeListener (event, callback, vm) {
removeListener(event: string | number, callback: (data: any) => void, vm: ComponentOptions<Vue>) {
if (!isFunction(callback) || !this.listeners.has(event)) {
return;
}
// @ts-ignore
const filtered = this.listeners.get(event).filter((listener) => {
return listener.callback !== callback || listener.vm !== vm;
});
@ -49,13 +50,10 @@ export default new class SocketEmitter {
/**
* Emit a socket event.
*
* @param {String|Number|Symbol} event
* @param {Array} args
*/
emit (event, ...args) {
emit(event: string | number, ...args: any) {
(this.listeners.get(event) || []).forEach((listener) => {
listener.callback.call(listener.vm, ...args);
listener.callback.call(listener.vm, args);
});
}
}

View file

@ -1,9 +1,11 @@
import SocketEmitter from './emitter';
import SocketioConnector from './connector';
import {ComponentOptions} from 'vue';
import {Vue} from "vue/types/vue";
let connector = null;
let connector: SocketioConnector | null = null;
export const Socketio = {
export const Socketio: ComponentOptions<Vue> = {
/**
* Setup the socket when we create the first component using the mixin. This is the Server.vue
* file, unless you mess up all of this code. Subsequent components to use this mixin will
@ -15,7 +17,7 @@ export const Socketio = {
connector = new SocketioConnector(this.$store);
}
const sockets = this.$options.sockets || {};
const sockets = (this.$options || {}).sockets || {};
Object.keys(sockets).forEach((event) => {
SocketEmitter.addListener(event, sockets[event], this);
});
@ -25,7 +27,7 @@ export const Socketio = {
* Before destroying the component we need to remove any event listeners registered for it.
*/
beforeDestroy: function () {
const sockets = this.$options.sockets || {};
const sockets = (this.$options || {}).sockets || {};
Object.keys(sockets).forEach((event) => {
SocketEmitter.removeListener(event, sockets[event], this);
});
@ -43,8 +45,13 @@ export const Socketio = {
* Disconnects from the active socket and sets the connector to null.
*/
removeSocket: function () {
if (connector !== null && connector.instance() !== null) {
connector.instance().close();
if (!connector) {
return;
}
const instance: SocketIOClient.Socket | null = connector.instance();
if (instance) {
instance.close();
}
connector = null;