import { Dispatch , MiddlewareAPI } from 'redux' ; import { beginReconnect , broken , closed , error , message , open , reconnectAttempt , reconnected , } from './actions' ; import { Action , Serializer , Deserializer } from './types' ; interface ReduxWebSocketOptions { prefix : string ; reconnectInterval : number ; reconnectOnClose : boolean ; reconnectOnError : boolean ; onOpen ? : ( s : WebSocket ) => void ; serializer ? : Serializer ; deserializer ? : Deserializer ; } class export default class ReduxWebSocket { private options : ReduxWebSocketOptions ; private websocket : WebSocket | null = null ; private reconnectCount : number = 0 ; private reconnectionInterval : NodeJS . Timeout | null = null ; private lastConnectPayload : { url : string ; protocols : string [ ] | undefined ; } | null = null ; private hasOpened = false ; constructor param { ReduxWebSocketOptions } options constructor ( options ) { this . options = options ; } param { MiddlewareAPI } store param { Action } action connect = ( { dispatch } , { payload } ) => { this . close ( ) ; const { prefix , deserializer } = this . options ; this . lastConnectPayload = payload ; this . websocket = payload . protocols ? new WebSocket ( payload . url , payload . protocols ) : new WebSocket ( payload . url ) ; this . websocket . addEventListener ( 'close' , ( event ) => this . handleClose ( dispatch , prefix , event ) ) ; this . websocket . addEventListener ( 'error' , ( ) => this . handleError ( dispatch , prefix ) ) ; this . websocket . addEventListener ( 'open' , ( event ) => { this . handleOpen ( dispatch , prefix , this . options . onOpen , event ) ; } ) ; this . websocket . addEventListener ( 'message' , ( event ) => this . handleMessage ( dispatch , prefix , deserializer , event ) ) ; } ; throws disconnect = ( ) => { if ( this . websocket ) { this . close ( ) ; } else { throw new Error ( 'Socket connection not initialized. Dispatch WEBSOCKET_CONNECT first' ) ; } } ; param { MiddlewareAPI } _store param { Action } action throws send = ( _store , { payload } ) => { if ( this . websocket ) { if ( this . options . serializer ) { this . websocket . send ( this . options . serializer ( payload ) ) ; } else { throw new Error ( 'Serializer not provided' ) ; } } else { throw new Error ( 'Socket connection not initialized. Dispatch WEBSOCKET_CONNECT first' ) ; } } ; param { Dispatch } dispatch param { string } prefix param { Event } event private handleClose = ( dispatch , prefix , event ) => { dispatch ( closed ( event , prefix ) ) ; const { reconnectOnClose } = this . options ; if ( reconnectOnClose && this . canAttemptReconnect ( ) ) { this . handleBrokenConnection ( dispatch ) ; } } ; param { Dispatch } dispatch param { string } prefix param { Event } event private handleError = ( dispatch , prefix ) => { dispatch ( error ( null , new Error ( '`redux-websocket` error' ) , prefix ) ) ; const { reconnectOnError } = this . options ; if ( reconnectOnError && this . canAttemptReconnect ( ) ) { this . handleBrokenConnection ( dispatch ) ; } } ; param { Dispatch } dispatch param { string } prefix param { ( s : WebSocket ) => void | undefined } onOpen param { Event } event private handleOpen = ( dispatch , prefix , onOpen , event ) => { if ( this . reconnectionInterval ) { clearInterval ( this . reconnectionInterval ) ; this . reconnectionInterval = null ; this . reconnectCount = 0 ; dispatch ( reconnected ( prefix ) ) ; } if ( onOpen && this . websocket != null ) { onOpen ( this . websocket ) ; } dispatch ( open ( event , prefix ) ) ; this . hasOpened = true ; } ; param { Dispatch } dispatch param { string } prefix param { MessageEvent } event private handleMessage = ( dispatch , prefix , deserializer , event ) => { dispatch ( message ( event , prefix , deserializer ) ) ; } ; private param { number } code param { string } reason private close = ( code ? , reason ? ) => { if ( this . websocket ) { this . websocket . close ( code || 1000 , reason || 'WebSocket connection closed by redux-websocket.' ) ; this . websocket = null ; this . hasOpened = false ; } } ; private param { Dispatch } dispatch private handleBrokenConnection = ( dispatch ) => { const { prefix , reconnectInterval } = this . options ; this . websocket = null ; dispatch ( broken ( prefix ) ) ; dispatch ( beginReconnect ( prefix ) ) ; this . reconnectCount = 1 ; dispatch ( reconnectAttempt ( this . reconnectCount , prefix ) ) ; this . connect ( { dispatch } as MiddlewareAPI , { payload : this . lastConnectPayload } as Action ) ; this . reconnectionInterval = setInterval ( ( ) => { this . reconnectCount += 1 ; dispatch ( reconnectAttempt ( this . reconnectCount , prefix ) ) ; this . connect ( { dispatch } as MiddlewareAPI , { payload : this . lastConnectPayload } as Action ) ; } , reconnectInterval ) ; } ; private canAttemptReconnect ( ) { return this . hasOpened && this . reconnectionInterval == null ; } }