Use TypedEmitter for client socket connections

This commit is contained in:
Jerko Steiner 2019-11-15 23:38:19 -03:00
parent 703c13f296
commit 7fa09fa6b8
10 changed files with 37 additions and 29 deletions

View File

@ -46,7 +46,7 @@ describe('CallActions', () => {
it('calls handshake.init when connected & got camera stream', async () => { it('calls handshake.init when connected & got camera stream', async () => {
const promise = callActions.init() const promise = callActions.init()
socket.emit('connect') socket.emit('connect', undefined)
await promise await promise
expect(store.getState().allActions.slice(1)).toEqual([{ expect(store.getState().allActions.slice(1)).toEqual([{
type: constants.NOTIFY, type: constants.NOTIFY,
@ -73,8 +73,8 @@ describe('CallActions', () => {
it('calls dispatches disconnect message on disconnect', async () => { it('calls dispatches disconnect message on disconnect', async () => {
const promise = callActions.init() const promise = callActions.init()
socket.emit('connect') socket.emit('connect', undefined)
socket.emit('disconnect') socket.emit('disconnect', undefined)
expect(store.getState().allActions.slice(1)).toEqual([{ expect(store.getState().allActions.slice(1)).toEqual([{
type: constants.NOTIFY, type: constants.NOTIFY,
payload: { payload: {
@ -96,7 +96,7 @@ describe('CallActions', () => {
it('dispatches alert when failed to get media stream', async () => { it('dispatches alert when failed to get media stream', async () => {
(getUserMedia as any).fail(true) (getUserMedia as any).fail(true)
const promise = callActions.init() const promise = callActions.init()
socket.emit('connect') socket.emit('connect', undefined)
await promise await promise
}) })

View File

@ -2,6 +2,7 @@ import * as constants from '../constants'
import socket from '../socket' import socket from '../socket'
import { Dispatch, ThunkResult } from '../store' import { Dispatch, ThunkResult } from '../store'
import { callId, getUserMedia } from '../window' import { callId, getUserMedia } from '../window'
import { ClientSocket } from '../socket'
import * as NotifyActions from './NotifyActions' import * as NotifyActions from './NotifyActions'
import * as SocketActions from './SocketActions' import * as SocketActions from './SocketActions'
import * as StreamActions from './StreamActions' import * as StreamActions from './StreamActions'
@ -34,7 +35,7 @@ async (dispatch, getState) => {
} }
export const connect = () => (dispatch: Dispatch) => { export const connect = () => (dispatch: Dispatch) => {
return new Promise<SocketIOClient.Socket>(resolve => { return new Promise<ClientSocket>(resolve => {
socket.once('connect', () => { socket.once('connect', () => {
resolve(socket) resolve(socket)
}) })

View File

@ -7,15 +7,16 @@ import { EventEmitter } from 'events'
import { createStore, Store, GetState } from '../store' import { createStore, Store, GetState } from '../store'
import { play } from '../window' import { play } from '../window'
import { Dispatch } from 'redux' import { Dispatch } from 'redux'
import { ClientSocket } from '../socket'
describe('PeerActions', () => { describe('PeerActions', () => {
function createSocket () { function createSocket () {
const socket = new EventEmitter() as unknown as SocketIOClient.Socket const socket = new EventEmitter() as unknown as ClientSocket
socket.id = 'user1' socket.id = 'user1'
return socket return socket
} }
let socket: SocketIOClient.Socket let socket: ClientSocket
let stream: MediaStream let stream: MediaStream
let user: { id: string } let user: { id: string }
let store: Store let store: Store

View File

@ -7,6 +7,7 @@ import forEach from 'lodash/forEach'
import _debug from 'debug' import _debug from 'debug'
import { play, iceServers } from '../window' import { play, iceServers } from '../window'
import { Dispatch, GetState } from '../store' import { Dispatch, GetState } from '../store'
import { ClientSocket } from '../socket'
const debug = _debug('peercalls') const debug = _debug('peercalls')
@ -15,14 +16,14 @@ export interface Peers {
} }
export interface PeerHandlerOptions { export interface PeerHandlerOptions {
socket: SocketIOClient.Socket socket: ClientSocket
user: { id: string } user: { id: string }
dispatch: Dispatch dispatch: Dispatch
getState: GetState getState: GetState
} }
class PeerHandler { class PeerHandler {
socket: SocketIOClient.Socket socket: ClientSocket
user: { id: string } user: { id: string }
dispatch: Dispatch dispatch: Dispatch
getState: GetState getState: GetState
@ -94,7 +95,7 @@ class PeerHandler {
} }
export interface CreatePeerOptions { export interface CreatePeerOptions {
socket: SocketIOClient.Socket socket: ClientSocket
user: { id: string } user: { id: string }
initiator: string initiator: string
stream?: MediaStream stream?: MediaStream

View File

@ -6,12 +6,13 @@ import * as constants from '../constants'
import Peer from 'simple-peer' import Peer from 'simple-peer'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { createStore, Store, GetState } from '../store' import { createStore, Store, GetState } from '../store'
import { ClientSocket } from '../socket'
import { Dispatch } from 'redux' import { Dispatch } from 'redux'
describe('SocketActions', () => { describe('SocketActions', () => {
const roomName = 'bla' const roomName = 'bla'
let socket: SocketIOClient.Socket let socket: ClientSocket
let store: Store let store: Store
let dispatch: Dispatch let dispatch: Dispatch
let getState: GetState let getState: GetState
@ -67,7 +68,7 @@ describe('SocketActions', () => {
it('should forward signal to peer', () => { it('should forward signal to peer', () => {
socket.emit('signal', { socket.emit('signal', {
userId: 'b', userId: 'b',
data, signal: data,
}) })
expect(instances.length).toBe(1) expect(instances.length).toBe(1)
@ -77,7 +78,7 @@ describe('SocketActions', () => {
it('does nothing if no peer', () => { it('does nothing if no peer', () => {
socket.emit('signal', { socket.emit('signal', {
userId: 'a', userId: 'a',
data, signal: data,
}) })
expect(instances.length).toBe(1) expect(instances.length).toBe(1)

View File

@ -5,11 +5,12 @@ import keyBy from 'lodash/keyBy'
import _debug from 'debug' import _debug from 'debug'
import { SignalData } from 'simple-peer' import { SignalData } from 'simple-peer'
import { Dispatch, GetState } from '../store' import { Dispatch, GetState } from '../store'
import { ClientSocket } from '../socket'
const debug = _debug('peercalls') const debug = _debug('peercalls')
export interface SocketHandlerOptions { export interface SocketHandlerOptions {
socket: SocketIOClient.Socket socket: ClientSocket
roomName: string roomName: string
stream?: MediaStream stream?: MediaStream
dispatch: Dispatch dispatch: Dispatch
@ -27,7 +28,7 @@ export interface UsersOptions {
} }
class SocketHandler { class SocketHandler {
socket: SocketIOClient.Socket socket: ClientSocket
roomName: string roomName: string
stream?: MediaStream stream?: MediaStream
dispatch: Dispatch dispatch: Dispatch
@ -70,7 +71,7 @@ class SocketHandler {
} }
export interface HandshakeOptions { export interface HandshakeOptions {
socket: SocketIOClient.Socket socket: ClientSocket
roomName: string roomName: string
stream?: MediaStream stream?: MediaStream
} }

View File

@ -1,3 +1,7 @@
import SocketIOClient from 'socket.io-client' import SocketIOClient from 'socket.io-client'
import { baseUrl } from './window' import { baseUrl } from './window'
export default SocketIOClient('', { path: baseUrl + '/ws' }) import { TypedEmitterKeys, SocketEvent, TypedEmitter } from '../shared'
export type ClientSocket = Omit<SocketIOClient.Socket, TypedEmitterKeys> &
TypedEmitter<SocketEvent>
const socket: ClientSocket = SocketIOClient('', { path: baseUrl + '/ws' })
export default socket

View File

@ -11,10 +11,11 @@ export interface SocketEvent {
} }
signal: { signal: {
userId: string userId: string
signal: unknown // eslint-disable-next-line
signal: any
} }
connect: void connect: undefined
disconnect: void disconnect: undefined
ready: string ready: string
} }

View File

@ -38,12 +38,12 @@ describe('TypedEmitter', () => {
let emitter: TypedEmitter<Events> let emitter: TypedEmitter<Events>
beforeEach(() => { beforeEach(() => {
emitter = new EventEmitter() emitter = new EventEmitter()
emitter.addListener('test1', listener1) emitter.on('test1', listener1)
emitter.on('test2', listener2) emitter.on('test2', listener2)
emitter.once('test3', listener3) emitter.once('test3', listener3)
}) })
describe('addListener & on', () => { describe('on & on', () => {
it('adds an event emitter', () => { it('adds an event emitter', () => {
emitter.emit('test1', 'value') emitter.emit('test1', 'value')
emitter.emit('test2', 3) emitter.emit('test2', 3)

View File

@ -1,21 +1,19 @@
import { EventEmitter } from 'events'
type Callback<A> = (a: A) => void type Callback<A> = (a: A) => void
// eslint-disable-next-line // eslint-disable-next-line
type Events = Record<string | symbol, any> type Events = Record<string | symbol, any>
export type TypedEmitterKeys = export type TypedEmitterKeys =
'addListener' |
'removeListener' | 'removeListener' |
'on' | 'on' |
'once' | 'once' |
'off' | 'off' |
'emit' 'emit'
export interface TypedEmitter<E extends Events> // Some methods might be missing and we do not extend EventEmitter because
extends EventEmitter { // SocketIOClient.Socket does not inherit from EventEmitter, and the method
addListener<K extends keyof E>(t: K, callback: Callback<E[K]>): this // signatures differ slightly.
export interface TypedEmitter<E extends Events> {
removeListener<K extends keyof E>(t: K, callback: Callback<E[K]>): this removeListener<K extends keyof E>(t: K, callback: Callback<E[K]>): this
on<K extends keyof E>(t: K, callback: Callback<E[K]>): this on<K extends keyof E>(t: K, callback: Callback<E[K]>): this
@ -23,5 +21,5 @@ extends EventEmitter {
off<K extends keyof E>(t: K, callback: Callback<E[K]>): this off<K extends keyof E>(t: K, callback: Callback<E[K]>): this
emit<K extends keyof E>(t: K, value: E[K]): boolean emit<K extends keyof E>(t: K, value: E[K]): void
} }