'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _keys = require('babel-runtime/core-js/object/keys');

var _keys2 = _interopRequireDefault(_keys);

var _objectAssign = require('object-assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Private action type for controlled-store
var ActionTypes = {
  UPDATE: '@@controlled/UPDATE'

  /**
   * A Redux store enhancer that allows a redux app to operate as a controlled
   * component, selectively moving state out of the app and into a container.
   *
   * Enhances the store with an additional method `controlledUpdate()` that will
   * override the redux store state. Any keys on the state object passed to
   * `controlledUpdate()` will be "locked" in that any actions in the app will no
   * longer directly update that part of the state, but instead call the `onChange`
   * function passed to the constructor, with the state key that has been updated
   * and the new value.
   *
   * @param {Function} onChange
   * @param {Object} stateOverride
   * @return {Function} Redux Store Enhancer
   */
};
exports.default = function (onChange) {
  var initialStateOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  return function (createStore) {
    // These properties of the app state are now controlled
    var controlledProps = (0, _keys2.default)(initialStateOverride);

    return function (reducer, initialState, enhancer) {
      initialState = (0, _objectAssign2.default)({}, initialState, initialStateOverride);
      // Create the store with an enhanced reducer
      var store = createStore(controlledReducer, initialState, enhancer);

      // Enhance the store with an additional method `controlledUpdate()`
      return (0, _objectAssign2.default)({}, store, {
        controlledUpdate: controlledUpdate
      });

      function controlledReducer(state, action) {
        // Controlled updates skip app reducers and override the state
        if (action.type === ActionTypes.UPDATE) {
          return (0, _objectAssign2.default)({}, state, action.payload);
        }
        var hasChanged = false;
        var newState = reducer(state, action);
        (0, _keys2.default)(newState).forEach(function (key) {
          if (newState[key] === state[key]) return;
          var value = newState[key];
          process.nextTick(function () {
            return onChange(key, value);
          });
          if (controlledProps.indexOf(key) > -1) {
            // If any controlled props of the state are updated, we hide the
            // initial change in state from the redux store and instead
            // call the `onChange` function with the key that has been updated
            // and the new value. Needs to run on nextTick to avoid `controlledUpdate()`
            // being called in the same tick and resulting in a `store.dispatch()`
            // inside this reducer.
            newState[key] = state[key];
          } else {
            // Unless an uncontrolled prop has been changed, we'll just return the existing state
            hasChanged = true;
          }
        });
        return hasChanged ? newState : state;
      }

      function controlledUpdate(stateOverride) {
        controlledProps = (0, _keys2.default)(stateOverride);
        store.dispatch({
          type: ActionTypes.UPDATE,
          payload: stateOverride
        });
      }
    };
  };
};
//# sourceMappingURL=controlled_store.js.map