import kefir from 'kefir'

import assert from '../internal/assert'

import reduxChannelName from './reduxChannelName'


/**
 * This injects the middleware into the AppDispatcher. Middleware can stop propagation
 * of events to observables by not calling "next(action)". Middleware can also
 * transform actions or dispatch their own actions.
 * @param {Object} opts
 * @param {kefirEmitter} opts.AppDispatcher
 * @param {middleware[]} opts.rawMiddleware
 * @returns {MiddlewareFactory} factory
 */
export default function reduxMiddlewareFactory({AppDispatcher, rawMiddleware}) {

  assert(AppDispatcher, 'Need an AppDispatcher')
  assert(rawMiddleware, 'Need rawMiddleware')

  let state = null
  const reduxStore = {

transform redux-formatted message to rflux

    dispatch: args => AppDispatcher.emit({
      channel: reduxChannelName,
      actionType: (args || {}).type,
      payload: args || {}
    }),
    getState: () => state
  }
  const middleware = rawMiddleware.map(__middleware => __middleware(reduxStore))

  return {
    setState: _state => state = _state,

    /**
     * This AppDispatcher is middleware aware.
     * @returns {kefirEmitter} the AppDispatcher
     **/
    appDispatcher() {
      const AppDispatcherObs =
        AppDispatcher
          .flatMap(rfluxAction =>
            middleware
              .reduce(
                (chain, _middleware) => {
                  let transformedReduxAction
                  let allowContinue = false

the “next” action doesn’t actually do anything…. it just tells the chain of filters to continue. Also allows the middleware to transfom the payload

                  const next = reduxAction => {
                    transformedReduxAction = reduxAction
                    allowContinue = true
                  }

                  return chain
                    .filter(reduxAction => {
                      _middleware(next)(reduxAction)

stop if next not called

                      return allowContinue
                    })

transform action (if requested)

                    .map(() => transformedReduxAction)
                },
                kefir.constant(
                  typeof rfluxAction.payload === 'undefined' ? {} : rfluxAction.payload
                )
              )
              .map(reduxAction => ({...rfluxAction, payload: reduxAction}))
          )

Yuck. Turn the observer into a dispatcher by adding an emit method that dispatches events into the original AppDispatcher bus. Recall that the AppDispatcher is a bus (an observable with an emit method).

      Object.assign(AppDispatcherObs, {emit: (...args) => AppDispatcher.emit(...args)})

      return AppDispatcherObs
    }
  }
}
h