Skip to content

Commit 1c6fa6c

Browse files
committed
Revert "Temporarily remove socketio until this can be fixed"
This reverts commit 0e1d35c.
1 parent 9f2eaa5 commit 1c6fa6c

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import * as io from 'socket.io-client';
2+
import {camelCase} from 'lodash';
3+
import SocketEmitter from './emitter';
4+
import {Store} from "vuex";
5+
6+
const SYSTEM_EVENTS: Array<string> = [
7+
'connect',
8+
'error',
9+
'disconnect',
10+
'reconnect',
11+
'reconnect_attempt',
12+
'reconnecting',
13+
'reconnect_error',
14+
'reconnect_failed',
15+
'connect_error',
16+
'connect_timeout',
17+
'connecting',
18+
'ping',
19+
'pong',
20+
];
21+
22+
export default class SocketioConnector {
23+
/**
24+
* The socket instance.
25+
*/
26+
socket: null | SocketIOClient.Socket;
27+
28+
/**
29+
* The vuex store being used to persist data and socket state.
30+
*/
31+
store: Store<any> | undefined;
32+
33+
constructor(store: Store<any> | undefined) {
34+
this.socket = null;
35+
this.store = store;
36+
}
37+
38+
/**
39+
* Initialize a new Socket connection.
40+
*
41+
* @param {io} socket
42+
*/
43+
connect(socket: SocketIOClient.Socket) {
44+
this.socket = socket;
45+
this.registerEventListeners();
46+
}
47+
48+
/**
49+
* Return the socket instance we are working with.
50+
*/
51+
instance(): SocketIOClient.Socket | null {
52+
return this.socket;
53+
}
54+
55+
/**
56+
* Register the event listeners for this socket including user-defined ones in the store as
57+
* well as global system events from Socekt.io.
58+
*/
59+
registerEventListeners() {
60+
if (!this.socket) {
61+
return;
62+
}
63+
64+
// @ts-ignore
65+
this.socket['onevent'] = (packet: { data: Array<any> }): void => {
66+
const [event, ...args] = packet.data;
67+
SocketEmitter.emit(event, ...args);
68+
69+
this.passToStore(event, args);
70+
};
71+
72+
SYSTEM_EVENTS.forEach((event: string): void => {
73+
if (!this.socket) {
74+
return;
75+
}
76+
77+
this.socket.on(event, (payload: any) => {
78+
SocketEmitter.emit(event, payload);
79+
80+
this.passToStore(event, payload);
81+
});
82+
});
83+
}
84+
85+
/**
86+
* Pass event calls off to the Vuex store if there is a corresponding function.
87+
*/
88+
passToStore(event: string | number, payload: Array<any>) {
89+
if (!this.store) {
90+
return;
91+
}
92+
93+
const s: Store<any> = this.store;
94+
const mutation = `SOCKET_${String(event).toUpperCase()}`;
95+
const action = `socket_${camelCase(String(event))}`;
96+
97+
// @ts-ignore
98+
Object.keys(this.store._mutations).filter((namespaced: string): boolean => {
99+
return namespaced.split('/').pop() === mutation;
100+
}).forEach((namespaced: string): void => {
101+
s.commit(namespaced, this.unwrap(payload));
102+
});
103+
104+
// @ts-ignore
105+
Object.keys(this.store._actions).filter((namespaced: string): boolean => {
106+
return namespaced.split('/').pop() === action;
107+
}).forEach((namespaced: string): void => {
108+
s.dispatch(namespaced, this.unwrap(payload)).catch(console.error);
109+
});
110+
}
111+
112+
unwrap(args: Array<any>) {
113+
return (args && args.length <= 1) ? args[0] : args;
114+
}
115+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {isFunction} from 'lodash';
2+
import {ComponentOptions} from "vue";
3+
import {Vue} from "vue/types/vue";
4+
5+
export default new class SocketEmitter {
6+
listeners: Map<string | number, Array<{
7+
callback: (a: ComponentOptions<Vue>) => void,
8+
vm: ComponentOptions<Vue>,
9+
}>>;
10+
11+
constructor() {
12+
this.listeners = new Map();
13+
}
14+
15+
/**
16+
* Add an event listener for socket events.
17+
*/
18+
addListener(event: string | number, callback: (data: any) => void, vm: ComponentOptions<Vue>) {
19+
if (!isFunction(callback)) {
20+
return;
21+
}
22+
23+
if (!this.listeners.has(event)) {
24+
this.listeners.set(event, []);
25+
}
26+
27+
// @ts-ignore
28+
this.listeners.get(event).push({callback, vm});
29+
}
30+
31+
/**
32+
* Remove an event listener for socket events based on the context passed through.
33+
*/
34+
removeListener(event: string | number, callback: (data: any) => void, vm: ComponentOptions<Vue>) {
35+
if (!isFunction(callback) || !this.listeners.has(event)) {
36+
return;
37+
}
38+
39+
// @ts-ignore
40+
const filtered = this.listeners.get(event).filter((listener) => {
41+
return listener.callback !== callback || listener.vm !== vm;
42+
});
43+
44+
if (filtered.length > 0) {
45+
this.listeners.set(event, filtered);
46+
} else {
47+
this.listeners.delete(event);
48+
}
49+
}
50+
51+
/**
52+
* Emit a socket event.
53+
*/
54+
emit(event: string | number, ...args: any) {
55+
(this.listeners.get(event) || []).forEach((listener) => {
56+
listener.callback.call(listener.vm, args);
57+
});
58+
}
59+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import SocketEmitter from './emitter';
2+
import SocketioConnector from './connector';
3+
import {ComponentOptions} from 'vue';
4+
import {Vue} from "vue/types/vue";
5+
6+
let connector: SocketioConnector | null = null;
7+
8+
export const Socketio: ComponentOptions<Vue> = {
9+
/**
10+
* Setup the socket when we create the first component using the mixin. This is the Server.vue
11+
* file, unless you mess up all of this code. Subsequent components to use this mixin will
12+
* receive the existing connector instance, so it is very important that the top-most component
13+
* calls the connectors destroy function when it is destroyed.
14+
*/
15+
created: function () {
16+
if (!connector) {
17+
connector = new SocketioConnector(this.$store);
18+
}
19+
20+
const sockets = (this.$options || {}).sockets || {};
21+
Object.keys(sockets).forEach((event) => {
22+
SocketEmitter.addListener(event, sockets[event], this);
23+
});
24+
},
25+
26+
/**
27+
* Before destroying the component we need to remove any event listeners registered for it.
28+
*/
29+
beforeDestroy: function () {
30+
const sockets = (this.$options || {}).sockets || {};
31+
Object.keys(sockets).forEach((event) => {
32+
SocketEmitter.removeListener(event, sockets[event], this);
33+
});
34+
},
35+
36+
methods: {
37+
/**
38+
* @return {SocketioConnector}
39+
*/
40+
'$socket': function () {
41+
return connector;
42+
},
43+
44+
/**
45+
* Disconnects from the active socket and sets the connector to null.
46+
*/
47+
removeSocket: function () {
48+
if (!connector) {
49+
return;
50+
}
51+
52+
const instance: SocketIOClient.Socket | null = connector.instance();
53+
if (instance) {
54+
instance.close();
55+
}
56+
57+
connector = null;
58+
},
59+
},
60+
};

0 commit comments

Comments
 (0)