import { Observable, Observer } from 'rxjs' import { combineEpics, ActionsObservable } from 'redux-observable' import { Store } from 'redux' import * as api from './api' import * as actions from './actions' import * as selectors from './selectors' import * as AuthSelector from '../auth/selectors' import { fetchUserSuccess } from '../auth/actions' import { ICustomMeetingTypes, IMeeting } from './interfaces' import { SocketCluster, SocketChannel, } from '../socket/constants' import { makeJoinChannelPendingEpic, makeJoinChannelSuccessEpic, } from '../socket/epic' const socketPendingEpic = makeJoinChannelPendingEpic(SocketCluster.MESSAGES, SocketChannel.USER) const socketSuccessEpic = makeJoinChannelSuccessEpic(SocketCluster.MESSAGES, SocketChannel.USER) const fetchDefaultMeetingType = (action$: ActionsObservable, store: Store) => { return action$.ofType(fetchUserSuccess.toString(), actions.refetchMeetingTypes.toString()) .pluck('payload') .mergeMap(() => { const accessToken = AuthSelector.accessToken(store.getState()) return api.fetchDefaultMeeting(accessToken) .map((value: IMeeting) => actions.fetchDefaultMeetingTypeSuccess(value)) .startWith(actions.fetchDefaultMeetingTypePending()) .catch(() => { return Observable.of(actions.fetchDefaultMeetingTypeFailure()) }) }) } const fetchCustomMeetingTypes = (action$: ActionsObservable, store: Store) => { return action$.ofType(fetchUserSuccess.toString(), actions.refetchMeetingTypes.toString()) .pluck('payload') .mergeMap(() => { const accessToken = AuthSelector.accessToken(store.getState()) return api.fetchMeetingTypes(accessToken) .map((value: ICustomMeetingTypes) => actions.fetchCustomMeetingTypesSuccess(value)) .startWith(actions.fetchCustomMeetingTypesPending()) .catch(() => { return Observable.of(actions.fetchCustomMeetingTypesFailure()) }) }) } interface ChannelPayload { body: { event: string, } } export const joinUserChannelPendingEpic = (action$: ActionsObservable, store: Store) => { return socketPendingEpic(action$) .switchMap(({ channel }) => { return Observable.create((observer: Observer) => { channel.on('user-settings', ({ body }: ChannelPayload) => { if (body.event === 'scheduling-settings-change') { observer.next(actions.refetchMeetingTypes()) } }) }) }) } export const triggerMeetingTypesEpic = (action$: ActionsObservable, store: Store) => { return socketSuccessEpic(action$) .switchMap(() => { // delay on reconnect to avoid herding const state = store.getState() if (selectors.customLoading(state) || selectors.defaultLoading(state)) { const delayMs = Math.random() * 60000 return Observable.timer(delayMs) .first() .map(() => actions.refetchMeetingTypes()) } else { return Observable.of(actions.refetchMeetingTypes()) } }) } export const epic = combineEpics( fetchDefaultMeetingType, fetchCustomMeetingTypes, joinUserChannelPendingEpic, )