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 AuthSelectors from '../auth/selectors' import * as PopupActions from '../popup/actions' import * as PopupSelectors from '../popup/selectors' 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) export const triggerFetchActivitiesEpic = (action$: ActionsObservable, store: Store) => { return socketSuccessEpic(action$) .switchMap(() => { // delay on reconnect to avoid herding if (selectors.isLoaded(store.getState())) { const delayMs = Math.random() * 60000 return Observable.timer(delayMs) .first() .map(() => actions.fetchActivities()) } else { return Observable.of(actions.fetchActivities()) } }) } export const fetchActivities = (action$: ActionsObservable, store: Store) => { return action$.ofType(actions.fetchActivities.toString()) .pluck('payload') .map(() => ({ accessToken: AuthSelectors.accessToken(store.getState()) })) .mergeMap((options) => { return api.fetchActivities(options) .map(activities => actions.fetchActivitiesSuccess(activities)) .startWith(actions.fetchActivitiesPending()) .catch(() => Observable.of(actions.fetchActivitiesFailure())) }) } export const fetchActivitiesByEmail = (action$: ActionsObservable, store: Store) => { return action$.ofType(PopupActions.updateSearchQuery.toString()) .pluck('payload') .filter(query => !!query) .map((query: string) => ({ email: query, accessToken: AuthSelectors.accessToken(store.getState()) })) .switchMap((options) => { return api.fetchActivities(options) .map(activities => actions.fetchActivitiesByEmailSuccess(activities)) .startWith(actions.fetchActivitiesPending()) .catch(() => Observable.of(actions.fetchActivitiesFailure())) }) } interface ChannelPayload { body: any } export const joinUserChannelPendingEpic = (action$: ActionsObservable, store: Store) => { return socketPendingEpic(action$) .switchMap(({ channel }) => { return Observable.create((observer: Observer) => { channel.on('outbound', ({ body }: ChannelPayload) => { observer.next(actions.receiveActivities([body])) const searchQuery = PopupSelectors.searchQuery(store.getState()) if (searchQuery && body.email.indexOf(searchQuery) !== -1) { observer.next(actions.receiveActivitiesByEmail([body])) } }) }) }) } export const epic = combineEpics( triggerFetchActivitiesEpic, fetchActivities, fetchActivitiesByEmail, joinUserChannelPendingEpic, )