import isPromise from './isPromise';

const defaultTypes = ['PENDING', 'FULFILLED', 'REJECTED'];

export default function promiseMiddleware(config = {}) {
  const promiseTypeSuffixes = config.promiseTypeSuffixes || defaultTypes;

  return (_ref) => {
    const dispatch = _ref.dispatch;

    return next => action => {
      if (!isPromise(action.payload)) {
        return next(action);
      }

      const { type, payload, meta } = action;
      const { promise, data } = payload;
      const [ PENDING, FULFILLED, REJECTED ] = (meta || {}).promiseTypeSuffixes || promiseTypeSuffixes;

     /**
      * Dispatch the first async handler. This tells the
      * reducer that an async action has been dispatched.
      */
      next({
        type: `${type}_${PENDING}`,
        ...!!data && { payload: data },
        ...!!meta && { meta }
      });

      const isAction = resolved => resolved.meta || resolved.payload;
      const isThunk = resolved => typeof resolved === 'function';
      const getResolveAction = (isError) => ({
        type: `${type}_${isError ? REJECTED : FULFILLED}`,
        ...!!meta && { meta },
        ...!!isError && { error: true }
      });

      /**
       * Re-dispatch one of:
       *  1. a thunk, bound to a resolved/rejected object containing ?meta and type
       *  2. the resolved/rejected object, if it looks like an action, merged into action
       *  3. a resolve/rejected action with the resolve/rejected object as a payload
       */
      promise.then(
        (resolved={}) => {
          const resolveAction = getResolveAction();
          dispatch(isThunk(resolved) ? resolved.bind(null, resolveAction) : {
            ...resolveAction,
            ...isAction(resolved) ? resolved : {
              ...!!resolved && { payload: resolved }
            }
          });
        },
        (rejected={}) => {
          const resolveAction = getResolveAction(true);
          dispatch(isThunk(rejected) ? rejected.bind(null, resolveAction) : {
            ...resolveAction,
            ...isAction(rejected) ? rejected : {
              ...!!rejected && { payload: rejected }
            }
          });
        },
      );
    };
  };
}

import isPromise from './isPromise';

const defaultTypes = ['PENDING', 'FULFILLED', 'REJECTED'];

export default function promiseMiddleware(config = {}) {
  const promiseTypeSuffixes = config.promiseTypeSuffixes || defaultTypes;

  return (_ref) => {
    const dispatch = _ref.dispatch;

    return next => action => {
      if (!isPromise(action.payload)) {
        return next(action);
      }

      const { type, payload, meta } = action;
      const { promise, data } = payload;
      const [ PENDING, FULFILLED, REJECTED ] = (meta || {}).promiseTypeSuffixes || promiseTypeSuffixes;

     /**
      * Dispatch the first async handler. This tells the
      * reducer that an async action has been dispatched.
      */
      next({
        type: `${type}_${PENDING}`,
        ...!!data && { payload: data },
        ...!!meta && { meta }
      });

      const isAction = resolved => resolved.meta || resolved.payload;
      const isThunk = resolved => typeof resolved === 'function';
      const getResolveAction = (isError) => ({
        type: `${type}_${isError ? REJECTED : FULFILLED}`,
        ...!!meta && { meta },
        ...!!isError && { error: true }
      });

      /**
       * Re-dispatch one of:
       *  1. a thunk, bound to a resolved/rejected object containing ?meta and type
       *  2. the resolved/rejected object, if it looks like an action, merged into action
       *  3. a resolve/rejected action with the resolve/rejected object as a payload
       */
      action.payload.promise = promise.then(
        (resolved={}) => {
          const resolveAction = getResolveAction();
          return dispatch(isThunk(resolved) ? resolved.bind(null, resolveAction) : {
            ...resolveAction,
            ...isAction(resolved) ? resolved : {
              ...!!resolved && { payload: resolved }
            }
          });
        },
        (rejected={}) => {
          const resolveAction = getResolveAction(true);
          return dispatch(isThunk(rejected) ? rejected.bind(null, resolveAction) : {
            ...resolveAction,
            ...isAction(rejected) ? rejected : {
              ...!!rejected && { payload: rejected }
            }
          });
        },
      );

      return action;
    };
  };
}

modify promise of action middleware to allow chaining of newly dispatched promise actions, also return original action in our middlware for consumers to listen to promise payload