lml-main
Version:
This is now a mono repository published into many standalone packages.
87 lines (77 loc) • 3.54 kB
text/typescript
import * as Pusher from 'pusher-js'
import { get } from 'lodash'
import { Observable } from 'rxjs'
import { combineEpics, ActionsObservable } from 'redux-observable'
import { Store } from 'redux'
import { AUTHENTICATED } from '@lml/cosmo-ui-auth'
import { RootState } from '../reducers'
import { Pusher as PusherInterface } from 'pusher-js'
import { formatChannelName } from '../utils'
import { PusherMetadataModel } from '../interfaces'
import {
setMetadata,
pusherHealthcheckError,
PUSHER_CONNECT,
SUBSCRIBE_CHANNEL,
pusherConnectionSuccess,
pusherConnectionError,
PusherConnectAction,
PusherSubscribeChannelAction,
} from '../actions'
// not exactly functional but lets expose it anyway
// use at your own risk - no guarantees
export let pusherClient: PusherInterface
export const connectToPusher =
(action$: ActionsObservable<PusherConnectAction>, store: Store<RootState>): Observable<any> =>
action$.ofType(PUSHER_CONNECT)
.mergeMap((action: PusherConnectAction) => Observable.create((observer: any) => {
const options = {
cluster: action.cluster,
...action.options,
}
pusherClient = new Pusher(action.key, options)
pusherClient.connection.bind('connected', (result: any) => {
observer.next(pusherConnectionSuccess(pusherClient, result))
});
pusherClient.connection.bind('error', (error: any) => {
observer.next(pusherConnectionError(error))
});
}))
/**
* An epic to subscribe to a channel
*
* @param action$
* @param store
*/
export const subscribeToChannel =
(action$: ActionsObservable<PusherSubscribeChannelAction>, store: Store<RootState>) =>
action$.ofType(SUBSCRIBE_CHANNEL)
.mergeMap((action: PusherSubscribeChannelAction) =>
Observable.create((observer: any) => {
const { events } = action
const channel = pusherClient.subscribe(formatChannelName(action.channel))
if (Array.isArray(events)) {
events.forEach(e => {
channel.bind(e, (message: any) => {
observer.next(action.callback(message))
})
})
} else {
channel.bind(events, (message: any) => {
observer.next(action.callback(message))
})
}
// always bind to metadata regardless of channel
// the backend team should be providing this as a health check on all channels
channel.bind('METADATA', (message: PusherMetadataModel) => {
const { items } = message
const errors = Object.keys(items)
.filter((event: string) => items[event].error > 0)
if (errors) {
errors.forEach((event: string) => {
observer.next(pusherHealthcheckError(action.channel, event, message))
})
}
observer.next(setMetadata(action.channel, message))
})
}))