This is an automated email from the ASF dual-hosted git repository.

amaranhao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git


The following commit(s) were added to refs/heads/master by this push:
     new b27842a  Replication redux (#1070)
b27842a is described below

commit b27842a60149ed6be6a3fa6fa7190b86eefb729c
Author: garren smith <garren.sm...@gmail.com>
AuthorDate: Fri Apr 6 20:18:02 2018 +0200

    Replication redux (#1070)
    
    * Refactor 'replication' addon to use Redux
---
 app/addons/auth/actions.js                         |  31 +-
 app/addons/documents/rev-browser/actions.js        |   1 -
 app/addons/replication/__tests__/actions.test.js   |  17 +-
 app/addons/replication/__tests__/stores.tests.js   |  59 ---
 app/addons/replication/actions.js                  | 257 +++++-------
 app/addons/replication/actiontypes.js              |   4 +-
 app/addons/replication/base.js                     |  17 +-
 .../replication/components/newreplication.js       |   2 +
 app/addons/replication/container.js                | 138 +++++++
 app/addons/replication/controller.js               | 143 +++----
 app/addons/replication/reducers.js                 | 381 +++++++++++++++++
 app/addons/replication/route.js                    |  31 +-
 app/addons/replication/stores.js                   | 459 ---------------------
 13 files changed, 724 insertions(+), 816 deletions(-)

diff --git a/app/addons/auth/actions.js b/app/addons/auth/actions.js
index 9742bc9..a58cd09 100644
--- a/app/addons/auth/actions.js
+++ b/app/addons/auth/actions.js
@@ -16,7 +16,6 @@ import ActionTypes from './actiontypes';
 import Api from './api';
 
 const {
-  AUTH_SHOW_PASSWORD_MODAL,
   AUTH_HIDE_PASSWORD_MODAL,
 } = ActionTypes;
 
@@ -119,24 +118,20 @@ export const authenticate = (username, password, 
onSuccess) => {
     name: username,
     password: password
   })
-    .then(
-      () => {
-        hidePasswordModal();
-        onSuccess(username, password);
-      },
-      () => {
-        FauxtonAPI.addNotification({
-          msg: "Your username or password is incorrect.",
-          type: "error",
-          clear: true
-        });
+    .then((resp) => {
+      if (resp.error) {
+        throw (resp);
       }
-    );
-};
-
-//This is used in the replication store
-export const showPasswordModal = () => {
-  FauxtonAPI.dispatch({ type: AUTH_SHOW_PASSWORD_MODAL });
+      hidePasswordModal();
+      onSuccess(username, password);
+    })
+    .catch(() => {
+      FauxtonAPI.addNotification({
+        msg: "Your username or password is incorrect.",
+        type: "error",
+        clear: true
+      });
+    });
 };
 
 export const hidePasswordModal = () => {
diff --git a/app/addons/documents/rev-browser/actions.js 
b/app/addons/documents/rev-browser/actions.js
index 7e9fcee..116af0e 100644
--- a/app/addons/documents/rev-browser/actions.js
+++ b/app/addons/documents/rev-browser/actions.js
@@ -26,7 +26,6 @@ export const initDiffEditor = (dbName, docId) => dispatch => {
   const url = FauxtonAPI.urls('databaseBaseURL', 'server', dbName);
   db = PouchDB(url);
 
-  // XXX: we need spec compliant promise support and get rid of jQ "deferreds"
   Promise.all([db.get(docId), getTree(db, docId)])
     .then(([doc, tree]) => {
       const conflictingRevs = getConflictingRevs(tree.paths, tree.winner, 
Object.keys(tree.deleted));
diff --git a/app/addons/replication/__tests__/actions.test.js 
b/app/addons/replication/__tests__/actions.test.js
index f12851a..2e090e8 100644
--- a/app/addons/replication/__tests__/actions.test.js
+++ b/app/addons/replication/__tests__/actions.test.js
@@ -36,6 +36,7 @@ describe("Replication Actions", () => {
     afterEach(fetchMock.restore);
 
     it('creates a new database if it does not exist', (done) => {
+      const dispatch = () => {};
       fetchMock.postOnce('/_replicator', {
         status: 404,
         body: {
@@ -69,7 +70,7 @@ describe("Replication Actions", () => {
         replicationTarget: "REPLICATION_TARGET_NEW_LOCAL_DATABASE",
         replicationType: "",
         username: "tester"
-      });
+      })(dispatch);
 
       //this is not pretty, and might cause some false errors. But its tricky 
to tell when this test has completed
       setTimeout(() => {
@@ -119,15 +120,15 @@ describe("Replication Actions", () => {
     };
 
     it('builds up correct state', (done) => {
-      FauxtonAPI.dispatcher.register(({type, options}) => {
+      const dispatch = ({type, options}) => {
         if (ActionTypes.REPLICATION_SET_STATE_FROM_DOC === type) {
           assert.deepEqual(docState, options);
           setTimeout(done);
         }
-      });
+      };
 
       fetchMock.getOnce('/_replicator/7dcea9874a8fcb13c6630a1547001559', doc);
-      getReplicationStateFrom(doc._id);
+      getReplicationStateFrom(doc._id)(dispatch);
     });
   });
 
@@ -171,13 +172,15 @@ describe("Replication Actions", () => {
         status: 200,
         body: resp
       });
-      deleteDocs(docs);
 
-      FauxtonAPI.dispatcher.register(({type}) => {
+
+      const dispatch = ({type}) => {
         if (ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS === type) {
           setTimeout(done);
         }
-      });
+      };
+
+      deleteDocs(docs)(dispatch);
     });
   });
 });
diff --git a/app/addons/replication/__tests__/stores.tests.js 
b/app/addons/replication/__tests__/stores.tests.js
deleted file mode 100644
index bd5fa36..0000000
--- a/app/addons/replication/__tests__/stores.tests.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy 
of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations 
under
-// the License.
-import utils from '../../../../test/mocha/testUtils';
-import Stores from '../stores';
-import Constants from '../constants';
-
-const assert = utils.assert;
-const store = Stores.replicationStore;
-
-describe('Replication Store', function () {
-
-  afterEach(function () {
-    store.reset();
-  });
-
-  it('confirm updateFormField updates all fields', function () {
-    assert.equal(store.getRemoteSource(), '');
-    store.updateFormField('remoteSource', 'SOURCE');
-    assert.equal(store.getRemoteSource(), 'SOURCE');
-
-    assert.equal(store.getRemoteTarget(), '');
-    store.updateFormField('remoteTarget', 'TARGET');
-    assert.equal(store.getRemoteTarget(), 'TARGET');
-
-    assert.equal(store.getlocalTarget(), '');
-    store.updateFormField('localTarget', 'db');
-    assert.equal(store.getlocalTarget(), 'db');
-
-    assert.equal(store.getReplicationType(), 
Constants.REPLICATION_TYPE.ONE_TIME);
-    store.updateFormField('replicationType', 
Constants.REPLICATION_TYPE.CONTINUOUS);
-    assert.equal(store.getReplicationType(), 
Constants.REPLICATION_TYPE.CONTINUOUS);
-
-    assert.equal(store.getReplicationDocName(), '');
-    store.updateFormField('replicationDocName', 'doc-name');
-    assert.equal(store.getReplicationDocName(), 'doc-name');
-
-    assert.equal(store.getReplicationSource(), '');
-    store.updateFormField('replicationSource', 'rsource');
-    assert.equal(store.getReplicationSource(), 'rsource');
-
-    assert.equal(store.getReplicationTarget(), '');
-    store.updateFormField('replicationTarget', 'rtarget');
-    assert.equal(store.getReplicationTarget(), 'rtarget');
-
-    assert.equal(store.getlocalSource(), '');
-    store.updateFormField('localSource', 'source-db');
-    assert.equal(store.getlocalSource(), 'source-db');
-  });
-
-});
diff --git a/app/addons/replication/actions.js 
b/app/addons/replication/actions.js
index b12ffd7..ba8199c 100644
--- a/app/addons/replication/actions.js
+++ b/app/addons/replication/actions.js
@@ -10,6 +10,7 @@
 // License for the specific language governing permissions and limitations 
under
 // the License.
 import FauxtonAPI from '../../core/api';
+import {get, post} from '../../core/ajax';
 import ActionTypes from './actiontypes';
 import Helpers from './helpers';
 import Constants from './constants';
@@ -22,55 +23,40 @@ import {
   deleteReplicatesApi,
   createReplicatorDB
 } from './api';
-import 'whatwg-fetch';
 
 
-function initReplicator (localSource) {
-  if (localSource) {
-    FauxtonAPI.dispatch({
+export const initReplicator = (routeLocalSource, localSource) => dispatch => {
+  if (routeLocalSource && routeLocalSource !== localSource) {
+    dispatch({
       type: ActionTypes.INIT_REPLICATION,
       options: {
-        localSource: localSource
+        localSource: routeLocalSource
       }
     });
   }
+};
 
-  fetch('/_all_dbs', {
-    credentials: 'include',
-    headers: {
-      'Accept': 'application/json; charset=utf-8',
-      'Content-Type': 'application/json'
-    },
-  })
-    .then(resp => resp.json())
+export const getDatabasesList = () => dispatch => {
+  get('/_all_dbs')
     .then((databases) => {
-      FauxtonAPI.dispatch({
+      dispatch({
         type: ActionTypes.REPLICATION_DATABASES_LOADED,
         options: {
-          databases: databases
+          databases
         }
       });
     });
-}
+};
 
-export const replicate = (params) => {
+export const replicate = (params) => dispatch => {
   const replicationDoc = createReplicationDoc(params);
 
-  const promise = fetch('/_replicator', {
-    method: 'POST',
-    credentials: 'include',
-    headers: {
-      'Accept': 'application/json; charset=utf-8',
-      'Content-Type': 'application/json'
-    },
-    body: JSON.stringify(replicationDoc)
-  })
-    .then(res => res.json());
+  const promise = post('/_replicator', replicationDoc);
 
   const source = Helpers.getDatabaseLabel(replicationDoc.source);
   const target = Helpers.getDatabaseLabel(replicationDoc.target);
 
-  FauxtonAPI.dispatch({
+  dispatch({
     type: ActionTypes.REPLICATION_STARTING,
   });
 
@@ -82,18 +68,21 @@ export const replicate = (params) => {
     });
   };
 
-  promise.then(json => {
-    if (!json.ok) {
-      throw json;
-    }
+  promise
+    .then(json => {
+      if (!json.ok) {
+        throw json;
+      }
 
-    FauxtonAPI.addNotification({
-      msg: `Replication from <code>${decodeURIComponent(source)}</code> to 
<code>${decodeURIComponent(target)}</code> has been scheduled.`,
-      type: 'success',
-      escape: false,
-      clear: true
-    });
-  })
+      FauxtonAPI.addNotification({
+        msg: `Replication from <code>${decodeURIComponent(source)}</code> to 
<code>${decodeURIComponent(target)}</code> has been scheduled.`,
+        type: 'success',
+        escape: false,
+        clear: true
+      });
+
+      dispatch(getReplicationActivity());
+    })
     .catch(json => {
       if (json.error && json.error === "not_found") {
         return createReplicatorDB().then(() => {
@@ -106,47 +95,51 @@ export const replicate = (params) => {
     });
 };
 
-function updateFormField (fieldName, value) {
-  FauxtonAPI.dispatch({
+export const updateFormField = (fieldName, value) => {
+  return {
     type: ActionTypes.REPLICATION_UPDATE_FORM_FIELD,
     options: {
       fieldName: fieldName,
       value: value
     }
-  });
-}
+  };
+};
 
-function clearReplicationForm () {
-  FauxtonAPI.dispatch({ type: ActionTypes.REPLICATION_CLEAR_FORM });
-}
+export const clearReplicationForm = () => {
+  return { type: ActionTypes.REPLICATION_CLEAR_FORM };
+};
 
-const getReplicationActivity = (supportNewApi) => {
-  FauxtonAPI.dispatch({
+export const getReplicationActivity = () => dispatch => {
+  dispatch({
     type: ActionTypes.REPLICATION_FETCHING_STATUS,
   });
 
-  fetchReplicationDocs(supportNewApi).then(docs => {
-    FauxtonAPI.dispatch({
-      type: ActionTypes.REPLICATION_STATUS,
-      options: docs
+  supportNewApi()
+    .then(supportNewApi => {
+      return fetchReplicationDocs(supportNewApi);
+    })
+    .then(docs => {
+      dispatch({
+        type: ActionTypes.REPLICATION_STATUS,
+        options: docs
+      });
     });
-  });
 };
 
-const getReplicateActivity = () => {
+export const getReplicateActivity = () => dispatch => {
   supportNewApi()
     .then(newApi => {
       if (!newApi) {
         return;
       }
 
-      FauxtonAPI.dispatch({
+      dispatch({
         type: ActionTypes.REPLICATION_FETCHING_REPLICATE_STATUS,
       });
 
       fetchReplicateInfo()
         .then(replicateInfo => {
-          FauxtonAPI.dispatch({
+          dispatch({
             type: ActionTypes.REPLICATION_REPLICATE_STATUS,
             options: replicateInfo
           });
@@ -154,59 +147,59 @@ const getReplicateActivity = () => {
     });
 };
 
-const filterDocs = (filter) => {
-  FauxtonAPI.dispatch({
+export const filterDocs = (filter) => {
+  return {
     type: ActionTypes.REPLICATION_FILTER_DOCS,
     options: filter
-  });
+  };
 };
 
-const filterReplicate = (filter) => {
-  FauxtonAPI.dispatch({
+export const filterReplicate = (filter) => {
+  return {
     type: ActionTypes.REPLICATION_FILTER_REPLICATE,
     options: filter
-  });
+  };
 };
 
-const selectAllDocs = () => {
-  FauxtonAPI.dispatch({
+export const selectAllDocs = () => {
+  return {
     type: ActionTypes.REPLICATION_TOGGLE_ALL_DOCS
-  });
+  };
 };
 
-const selectDoc = (id) => {
-  FauxtonAPI.dispatch({
+export const selectDoc = (id) => {
+  return {
     type: ActionTypes.REPLICATION_TOGGLE_DOC,
     options: id
-  });
+  };
 };
 
-const selectAllReplicates = () => {
-  FauxtonAPI.dispatch({
+export const selectAllReplicates = () => {
+  return {
     type: ActionTypes.REPLICATION_TOGGLE_ALL_REPLICATE
-  });
+  };
 };
 
-const selectReplicate = (id) => {
-  FauxtonAPI.dispatch({
+export const selectReplicate = (id) => {
+  return {
     type: ActionTypes.REPLICATION_TOGGLE_REPLICATE,
     options: id
-  });
+  };
 };
 
-const clearSelectedDocs = () => {
-  FauxtonAPI.dispatch({
+export const clearSelectedDocs = () => {
+  return {
     type: ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS
-  });
+  };
 };
 
-const clearSelectedReplicates = () => {
-  FauxtonAPI.dispatch({
+export const clearSelectedReplicates = () => {
+  return {
     type: ActionTypes.REPLICATION_CLEAR_SELECTED_REPLICATES
-  });
+  };
 };
 
-export const deleteDocs = (docs) => {
+export const deleteDocs = (docs) => dispatch => {
   const bulkDocs = docs.map(({raw: doc}) => {
     doc._deleted = true;
     return doc;
@@ -219,20 +212,12 @@ export const deleteDocs = (docs) => {
     clear: true
   });
 
-  fetch('/_replicator/_bulk_docs', {
-    credentials: 'include',
-    headers: {
-      'Accept': 'application/json; charset=utf-8',
-      'Content-Type': 'application/json'
-    },
-    method: 'POST',
-    body: JSON.stringify({docs: bulkDocs})
-  })
+  post('/_replicator/_bulk_docs', {docs: bulkDocs}, {raw: true})
     .then(resp => {
       if (!resp.ok) {
         throw resp;
       }
-      return resp.json();
+      return resp;
     })
     .then(() => {
 
@@ -248,8 +233,8 @@ export const deleteDocs = (docs) => {
         clear: true
       });
 
-      clearSelectedDocs();
-      getReplicationActivity();
+      dispatch(clearSelectedDocs());
+      dispatch(getReplicationActivity());
     })
     .catch(resp => {
       resp.json()
@@ -264,7 +249,7 @@ export const deleteDocs = (docs) => {
     });
 };
 
-const deleteReplicates = (replicates) => {
+export const deleteReplicates = (replicates) => dispatch => {
   FauxtonAPI.addNotification({
     msg: `Deleting _replicate${replicates.length > 1 ? 's' : ''}.`,
     type: 'success',
@@ -279,8 +264,8 @@ const deleteReplicates = (replicates) => {
         msg = `Replication <code>${replicates[0]._id}</code> has been deleted`;
       }
 
-      clearSelectedReplicates();
-      getReplicateActivity();
+      dispatch(clearSelectedReplicates());
+      dispatch(getReplicateActivity());
 
       FauxtonAPI.addNotification({
         msg: msg,
@@ -298,19 +283,12 @@ const deleteReplicates = (replicates) => {
     });
 };
 
-export const getReplicationStateFrom = (id) => {
-  FauxtonAPI.dispatch({
+export const getReplicationStateFrom = (id) => dispatch => {
+  dispatch({
     type: ActionTypes.REPLICATION_FETCHING_FORM_STATE
   });
 
-  fetch(`/_replicator/${encodeURIComponent(id)}`, {
-    credentials: 'include',
-    headers: {
-      'Accept': 'application/json; charset=utf-8',
-    },
-    method: 'GET'
-  })
-    .then(resp => resp.json())
+  get(`/_replicator/${encodeURIComponent(id)}`)
     .then((doc) => {
       const stateDoc = {
         replicationDocName: doc._id,
@@ -338,7 +316,7 @@ export const getReplicationStateFrom = (id) => {
         stateDoc.remoteTarget = decodeFullUrl(targetUrl);
       }
 
-      FauxtonAPI.dispatch({
+      dispatch({
         type: ActionTypes.REPLICATION_SET_STATE_FROM_DOC,
         options: stateDoc
       });
@@ -353,65 +331,42 @@ export const getReplicationStateFrom = (id) => {
     });
 };
 
-const showConflictModal = () => {
-  FauxtonAPI.dispatch({
+export const showConflictModal = () => {
+  return {
     type: ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL
-  });
+  };
 };
 
-const hideConflictModal = () => {
-  FauxtonAPI.dispatch({
+export const hideConflictModal = () => {
+  return {
     type: ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL
-  });
+  };
 };
 
-const changeActivitySort = (sort) => {
-  FauxtonAPI.dispatch({
+export const changeActivitySort = (sort) => {
+  return {
     type: ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT,
     options: sort
-  });
-};
-
-const changeTabSection = (newSection, url) => {
-  FauxtonAPI.dispatch({
-    type: ActionTypes.REPLICATION_CHANGE_TAB_SECTION,
-    options: newSection
-  });
-
-  if (url) {
-    FauxtonAPI.navigate(url, {trigger: false});
-  }
+  };
 };
 
-const checkForNewApi = () => {
+export const checkForNewApi = () => dispatch => {
   supportNewApi().then(newApi => {
-    FauxtonAPI.dispatch({
+    dispatch({
       type: ActionTypes.REPLICATION_SUPPORT_NEW_API,
       options: newApi
     });
   });
 };
 
-export default {
-  checkForNewApi,
-  initReplicator,
-  replicate,
-  updateFormField,
-  clearReplicationForm,
-  getReplicationActivity,
-  filterDocs,
-  selectAllDocs,
-  selectDoc,
-  deleteDocs,
-  getReplicationStateFrom,
-  showConflictModal,
-  hideConflictModal,
-  changeActivitySort,
-  clearSelectedDocs,
-  changeTabSection,
-  getReplicateActivity,
-  filterReplicate,
-  selectReplicate,
-  selectAllReplicates,
-  deleteReplicates
+export const showPasswordModal = () => {
+  return {
+    type: ActionTypes.REPLICATION_SHOW_PASSWORD_MODAL
+  };
+};
+
+export const hidePasswordModal = () => {
+  return {
+    type: ActionTypes.REPLICATION_HIDE_PASSWORD_MODAL
+  };
 };
diff --git a/app/addons/replication/actiontypes.js 
b/app/addons/replication/actiontypes.js
index 5089ea4..7143868 100644
--- a/app/addons/replication/actiontypes.js
+++ b/app/addons/replication/actiontypes.js
@@ -36,5 +36,7 @@ export default {
   REPLICATION_TOGGLE_ALL_REPLICATE: 'REPLICATION_TOGGLE_ALL_REPLICATE',
   REPLICATION_TOGGLE_REPLICATE: 'REPLICATION_TOGGLE_REPLICATE',
   REPLICATION_CLEAR_SELECTED_REPLICATES: 
'REPLICATION_CLEAR_SELECTED_REPLICATES',
-  REPLICATION_FETCHING_FORM_STATE: 'REPLICATION_FETCHING_FORM_STATE'
+  REPLICATION_FETCHING_FORM_STATE: 'REPLICATION_FETCHING_FORM_STATE',
+  REPLICATION_HIDE_PASSWORD_MODAL: 'REPLICATION_HIDE_PASSWORD_MODAL',
+  REPLICATION_SHOW_PASSWORD_MODAL: 'REPLICATION_SHOW_PASSWORD_MODAL'
 };
diff --git a/app/addons/replication/base.js b/app/addons/replication/base.js
index 54c8f85..e4b966b 100644
--- a/app/addons/replication/base.js
+++ b/app/addons/replication/base.js
@@ -13,22 +13,23 @@
 import FauxtonAPI from '../../core/api';
 import replication from './route';
 import './assets/less/replication.less';
-import Actions from './actions';
+import { checkForNewApi } from './actions';
+import replicationReducer from './reducers';
 
 replication.initialize = function () {
   FauxtonAPI.addHeaderLink({ title: 'Replication', href: '#/replication', 
icon: 'fonticon-replicate' });
   FauxtonAPI.session.isAuthenticated().then(() => {
-    Actions.checkForNewApi();
+    checkForNewApi();
   });
 };
 
+FauxtonAPI.addReducers({
+  replication: replicationReducer
+});
+
 FauxtonAPI.registerUrls('replication', {
-  app: (db) => {
-    return '#/replication/_create/' + db;
-  },
-  api: () => {
-    return window.location.origin + '/_replicator';
-  }
+  app: (db) => '#/replication/_create/' + db,
+  api: () => window.location.origin + '/_replicator'
 });
 
 export default replication;
diff --git a/app/addons/replication/components/newreplication.js 
b/app/addons/replication/components/newreplication.js
index 1b64008..f1a82a0 100644
--- a/app/addons/replication/components/newreplication.js
+++ b/app/addons/replication/components/newreplication.js
@@ -156,6 +156,7 @@ export default class NewReplicationController extends 
React.Component {
       }
     }
 
+    this.props.hidePasswordModal();
     this.props.replicate({
       replicationTarget,
       replicationSource,
@@ -263,6 +264,7 @@ export default class NewReplicationController extends 
React.Component {
           
modalMessage={<p>{app.i18n.en_US['replication-password-modal-text']}</p>}
           submitBtnLabel="Start Replication"
           headerTitle={app.i18n.en_US['replication-password-modal-header']}
+          onClose={this.props.hidePasswordModal}
           onSuccess={this.submit} />
         <ConflictModal
           visible={conflictModalVisible}
diff --git a/app/addons/replication/container.js 
b/app/addons/replication/container.js
new file mode 100644
index 0000000..7320ae3
--- /dev/null
+++ b/app/addons/replication/container.js
@@ -0,0 +1,138 @@
+import { connect } from 'react-redux';
+import ReplicationController from './controller';
+
+import {
+  checkForNewApi,
+  updateFormField,
+  clearReplicationForm,
+  initReplicator,
+  getReplicationStateFrom,
+  getReplicateActivity,
+  getReplicationActivity,
+  getDatabasesList,
+  showPasswordModal,
+  hidePasswordModal,
+  showConflictModal,
+  hideConflictModal,
+  replicate,
+  filterReplicate,
+  filterDocs,
+  selectDoc,
+  deleteDocs,
+  selectAllDocs,
+  changeActivitySort,
+  deleteReplicates,
+  selectAllReplicates,
+  selectReplicate
+} from './actions';
+
+import {
+  isLoading,
+  isActivityLoading,
+  getDatabases,
+  isAuthenticated,
+  getReplicationSource,
+  getLocalSource,
+  isLocalSourceKnown,
+  getRemoteSource,
+  getReplicationTarget,
+  getLocalTarget,
+  isLocalTargetKnown,
+  getRemoteTarget,
+  isPasswordModalVisible,
+  isConflictModalVisible,
+  getReplicationType,
+  getReplicationDocName,
+  getSubmittedNoChange,
+  getFilteredReplicationStatus,
+  getStatusFilter,
+  getReplicateFilter,
+  getAllDocsSelected,
+  getSomeDocsSelected,
+  getUsername,
+  getPassword,
+  getActivitySort,
+  getCheckingApi,
+  supportNewApi,
+  isReplicateInfoLoading,
+  getAllReplicateSelected,
+  getReplicateInfo,
+  someReplicateSelected
+} from './reducers';
+
+const mapStateToProps = ({replication}, ownProps) => {
+  return {
+    routeLocalSource: ownProps.routeLocalSource,
+    replicationId: ownProps.replicationId,
+    tabSection: ownProps.section,
+    loading: isLoading(replication),
+    activityLoading: isActivityLoading(replication),
+    databases: getDatabases(replication),
+    authenticated: isAuthenticated(replication),
+
+    // source fields
+    replicationSource: getReplicationSource(replication),
+    localSource: getLocalSource(replication),
+    localSourceKnown: isLocalSourceKnown(replication),
+    remoteSource: getRemoteSource(replication),
+
+    // target fields
+    replicationTarget: getReplicationTarget(replication),
+    localTarget: getLocalTarget(replication),
+    localTargetKnown: isLocalTargetKnown(replication),
+    remoteTarget: getRemoteTarget(replication),
+
+    // other
+    passwordModalVisible: isPasswordModalVisible(replication),
+    isConflictModalVisible: isConflictModalVisible(replication),
+    replicationType: getReplicationType(replication),
+    replicationDocName: getReplicationDocName(replication),
+    submittedNoChange: getSubmittedNoChange(replication),
+    statusDocs: getFilteredReplicationStatus(replication),
+    statusFilter: getStatusFilter(replication),
+    replicateFilter: getReplicateFilter(replication),
+    allDocsSelected: getAllDocsSelected(replication),
+    someDocsSelected:  getSomeDocsSelected(replication),
+    username: getUsername(replication),
+    password: getPassword(replication),
+    activitySort: getActivitySort(replication),
+    checkingApi: getCheckingApi(replication),
+    supportNewApi: supportNewApi(replication),
+    replicateLoading: isReplicateInfoLoading(replication),
+    replicateInfo: getReplicateInfo(replication),
+    allReplicateSelected: getAllReplicateSelected(replication),
+    someReplicateSelected: someReplicateSelected(replication)
+  };
+};
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    checkForNewApi: () => dispatch(checkForNewApi()),
+    updateFormField: (fieldName) => (value) => {
+      dispatch(updateFormField(fieldName, value));
+    },
+    clearReplicationForm: () => dispatch(clearReplicationForm()),
+    initReplicator: (localSource) => dispatch(initReplicator(localSource)),
+    getReplicationActivity: () => dispatch(getReplicationActivity()),
+    getReplicateActivity: () => dispatch(getReplicateActivity()),
+    getReplicationStateFrom: (id) => dispatch(getReplicationStateFrom(id)),
+    getDatabasesList: () => dispatch(getDatabasesList()),
+    showPasswordModal: () => dispatch(showPasswordModal()),
+    hidePasswordModal: () => dispatch(hidePasswordModal()),
+    replicate: (params) => dispatch(replicate(params)),
+    showConflictModal: () => dispatch(showConflictModal()),
+    hideConflictModal: () => dispatch(hideConflictModal()),
+    filterReplicate: (filter) => dispatch(filterReplicate(filter)),
+    filterDocs: (filter) => dispatch(filterDocs(filter)),
+    selectDoc: (doc) => dispatch(selectDoc(doc)),
+    deleteDocs: (docs) => dispatch(deleteDocs(docs)),
+    selectAllDocs: () => dispatch(selectAllDocs()),
+    changeActivitySort: (sort) => dispatch(changeActivitySort(sort)),
+    selectAllReplicates: () => dispatch(selectAllReplicates()),
+    deleteReplicates: (replicates) => dispatch(deleteReplicates(replicates)),
+    selectReplicate: (replicate) => dispatch(selectReplicate(replicate))
+  };
+};
+
+
+export default connect(mapStateToProps, 
mapDispatchToProps)(ReplicationController);
diff --git a/app/addons/replication/controller.js 
b/app/addons/replication/controller.js
index dfefabc..117714a 100644
--- a/app/addons/replication/controller.js
+++ b/app/addons/replication/controller.js
@@ -9,11 +9,9 @@
 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 // License for the specific language governing permissions and limitations 
under
 // the License.
+import FauxtonAPI from '../../core/api';
 import React from 'react';
-import Stores from './stores';
-import Actions from './actions';
 import Helpers from '../../helpers';
-import {showPasswordModal} from '../auth/actions';
 import Components from '../components/react-components';
 import NewReplication from './components/newreplication';
 import Activity from './components/activity';
@@ -24,87 +22,41 @@ import ReplicateActivity from 
'./components/replicate-activity';
 
 const {LoadLines, Polling, RefreshBtn} = Components;
 
-const store = Stores.replicationStore;
-
 export default class ReplicationController extends React.Component {
-  constructor (props) {
-    super(props);
-    this.state = this.getStoreState();
-  }
-
-  getStoreState () {
-    return {
-      loading: store.isLoading(),
-      activityLoading: store.isActivityLoading(),
-      databases: store.getDatabases(),
-      authenticated: store.isAuthenticated(),
-
-      // source fields
-      replicationSource: store.getReplicationSource(),
-      localSource: store.getlocalSource(),
-      localSourceKnown: store.isLocalSourceKnown(),
-      remoteSource: store.getRemoteSource(),
-
-      // target fields
-      replicationTarget: store.getReplicationTarget(),
-      localTarget: store.getlocalTarget(),
-      localTargetKnown: store.isLocalTargetKnown(),
-      remoteTarget: store.getRemoteTarget(),
-
-      // other
-      passwordModalVisible: store.isPasswordModalVisible(),
-      showConflictModal: store.isConflictModalVisible(),
-      replicationType: store.getReplicationType(),
-      replicationDocName: store.getReplicationDocName(),
-      submittedNoChange: store.getSubmittedNoChange(),
-      statusDocs: store.getFilteredReplicationStatus(),
-      statusFilter: store.getStatusFilter(),
-      replicateFilter: store.getReplicateFilter(),
-      allDocsSelected: store.getAllDocsSelected(),
-      someDocsSelected:  store.someDocsSelected(),
-      username: store.getUsername(),
-      password: store.getPassword(),
-      activitySort: store.getActivitySort(),
-      tabSection: store.getTabSection(),
-      checkingApi: store.checkingAPI(),
-      supportNewApi: store.supportNewApi(),
-      replicateLoading: store.isReplicateInfoLoading(),
-      replicateInfo: store.getReplicateInfo(),
-      allReplicateSelected: store.getAllReplicateSelected(),
-      someReplicateSelected: store.someReplicateSelected()
-    };
-  }
 
   loadReplicationInfo (props, oldProps) {
-    Actions.initReplicator(props.localSource);
+    this.props.initReplicator(props.routeLocalSource, props.localSource);
     this.getAllActivity();
+    this.loadReplicationStateFrom(props, oldProps);
+  }
+
+  loadReplicationStateFrom (props, oldProps) {
     if (props.replicationId && props.replicationId !== oldProps.replicationId) 
{
-      Actions.clearReplicationForm();
-      Actions.getReplicationStateFrom(props.replicationId);
+      this.props.clearReplicationForm();
+      this.props.getReplicationStateFrom(props.replicationId);
     }
   }
 
   getAllActivity () {
-    Actions.getReplicationActivity();
-    Actions.getReplicateActivity();
+    this.props.getReplicationActivity();
+    this.props.getReplicateActivity();
   }
 
   componentDidMount () {
-    store.on('change', this.onChange, this);
+    this.props.checkForNewApi();
+    this.props.getDatabasesList();
     this.loadReplicationInfo(this.props, {});
   }
 
   componentWillReceiveProps (nextProps) {
-    this.loadReplicationInfo(nextProps, this.props);
+    this.loadReplicationStateFrom(nextProps, this.props);
+    if (this.props.tabSection !== 'new replication' && nextProps.tabSection 
=== 'new replication') {
+      this.props.clearReplicationForm();
+    }
   }
 
   componentWillUnmount () {
-    store.off('change', this.onChange);
-    Actions.clearReplicationForm();
-  }
-
-  onChange () {
-    this.setState(this.getStoreState());
+    this.props.clearReplicationForm();
   }
 
   showSection () {
@@ -112,28 +64,25 @@ export default class ReplicationController extends 
React.Component {
       replicationSource, replicationTarget, replicationType, 
replicationDocName,
       passwordModalVisible, databases, localSource, remoteSource, remoteTarget,
       localTarget, statusDocs, statusFilter, loading, allDocsSelected,
-      someDocsSelected, showConflictModal, localSourceKnown, localTargetKnown,
+      someDocsSelected, showConflictModal, localSourceKnown, localTargetKnown, 
updateFormField,
       username, password, authenticated, activityLoading, submittedNoChange, 
activitySort, tabSection,
-      replicateInfo, replicateLoading, replicateFilter, allReplicateSelected, 
someReplicateSelected
-    } = this.state;
+      replicateInfo, replicateLoading, replicateFilter, allReplicateSelected, 
someReplicateSelected,
+      showPasswordModal, hidePasswordModal, hideConflictModal, 
isConflictModalVisible, filterDocs,
+      filterReplicate, replicate, clearReplicationForm, selectAllDocs, 
changeActivitySort, selectDoc,
+      deleteDocs, deleteReplicates, selectAllReplicates, selectReplicate
+    } = this.props;
 
     if (tabSection === 'new replication') {
       if (loading) {
         return <LoadLines/>;
       }
 
-      const updateFormField = (field) => {
-        return (value) => {
-          Actions.updateFormField(field, value);
-        };
-      };
-
       return <NewReplication
         docs={statusDocs}
         localTargetKnown={localTargetKnown}
         localSourceKnown={localSourceKnown}
-        clearReplicationForm={Actions.clearReplicationForm}
-        replicate={Actions.replicate}
+        clearReplicationForm={clearReplicationForm}
+        replicate={replicate}
         showPasswordModal={showPasswordModal}
         replicationSource={replicationSource}
         replicationTarget={replicationTarget}
@@ -146,14 +95,15 @@ export default class ReplicationController extends 
React.Component {
         remoteTarget={remoteTarget}
         localTarget={localTarget}
         updateFormField={updateFormField}
-        conflictModalVisible={showConflictModal}
-        hideConflictModal={Actions.hideConflictModal}
-        showConflictModal={Actions.showConflictModal}
+        conflictModalVisible={isConflictModalVisible}
+        hideConflictModal={hideConflictModal}
+        showConflictModal={showConflictModal}
         checkReplicationDocID={checkReplicationDocID}
         authenticated={authenticated}
         username={username}
         password={password}
         submittedNoChange={submittedNoChange}
+        hidePasswordModal={hidePasswordModal}
       />;
     }
 
@@ -165,14 +115,14 @@ export default class ReplicationController extends 
React.Component {
       return <ReplicateActivity
         docs={replicateInfo}
         filter={replicateFilter}
-        onFilterChange={Actions.filterReplicate}
-        selectDoc={Actions.selectReplicate}
-        selectAllDocs={Actions.selectAllReplicates}
+        onFilterChange={filterReplicate}
+        selectDoc={selectReplicate}
+        selectAllDocs={selectAllReplicates}
         allDocsSelected={allReplicateSelected}
         someDocsSelected={someReplicateSelected}
         activitySort={activitySort}
-        changeActivitySort={Actions.changeActivitySort}
-        deleteDocs={Actions.deleteReplicates}
+        changeActivitySort={changeActivitySort}
+        deleteDocs={deleteReplicates}
       />;
     }
 
@@ -183,19 +133,19 @@ export default class ReplicationController extends 
React.Component {
     return <Activity
       docs={statusDocs}
       filter={statusFilter}
-      onFilterChange={Actions.filterDocs}
-      selectAllDocs={Actions.selectAllDocs}
-      selectDoc={Actions.selectDoc}
+      onFilterChange={filterDocs}
+      selectAllDocs={selectAllDocs}
+      selectDoc={selectDoc}
       allDocsSelected={allDocsSelected}
       someDocsSelected={someDocsSelected}
-      deleteDocs={Actions.deleteDocs}
+      deleteDocs={deleteDocs}
       activitySort={activitySort}
-      changeActivitySort={Actions.changeActivitySort}
+      changeActivitySort={changeActivitySort}
     />;
   }
 
   getHeaderComponents () {
-    if (this.state.tabSection === 'new replication') {
+    if (this.props.tabSection === 'new replication') {
       return null;
     }
     let rightHeaderclass = "right-header-flex";
@@ -220,7 +170,7 @@ export default class ReplicationController extends 
React.Component {
   }
 
   getTabElements () {
-    const {tabSection} = this.state;
+    const {tabSection} = this.props;
     const elements = [
       <TabElement
         key={1}
@@ -230,7 +180,7 @@ export default class ReplicationController extends 
React.Component {
       />
     ];
 
-    if (this.state.supportNewApi) {
+    if (this.props.supportNewApi) {
       elements.push(
         <TabElement
           key={2}
@@ -245,18 +195,19 @@ export default class ReplicationController extends 
React.Component {
   }
 
   onTabChange (section, url) {
-    Actions.changeTabSection(section, url);
+    // this.props.changeTabSection(section, url);
+    FauxtonAPI.navigate(url);
   }
 
   getCrumbs () {
-    if (this.state.tabSection === 'new replication') {
+    if (this.props.tabSection === 'new replication') {
       return [{'name': 'Job Configuration'}];
     }
     return [{'name': 'Replication'}];
   }
 
   getTabs () {
-    if (this.state.tabSection === 'new replication') {
+    if (this.props.tabSection === 'new replication') {
       return null;
     }
 
@@ -268,7 +219,7 @@ export default class ReplicationController extends 
React.Component {
   }
 
   render () {
-    const { checkingAPI } = this.state;
+    const { checkingAPI } = this.props;
 
     if (checkingAPI) {
       return <LoadLines />;
diff --git a/app/addons/replication/reducers.js 
b/app/addons/replication/reducers.js
new file mode 100644
index 0000000..545e98d
--- /dev/null
+++ b/app/addons/replication/reducers.js
@@ -0,0 +1,381 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy 
of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations 
under
+// the License.
+import ActionTypes from './actiontypes';
+import Constants from './constants';
+import app from "../../app";
+
+const setActivitySort = (sort) => {
+  app.utils.localStorageSet('replication-activity-sort', sort);
+};
+
+const loadActivitySort = () => {
+  const defaultSort = {
+    descending: false,
+    column: 'statusTime'
+
+  };
+  let sort = app.utils.localStorageGet('replication-activity-sort');
+
+  if (!sort) {
+    sort = defaultSort;
+  }
+
+  setActivitySort(sort);
+  return sort;
+};
+
+const validFieldMap = {
+  remoteSource: 'remoteSource',
+  remoteTarget: 'remoteTarget',
+  localTarget: 'localTarget',
+  replicationType: 'replicationType',
+  replicationDocName: 'replicationDocName',
+  replicationSource: 'replicationSource',
+  replicationTarget: 'replicationTarget',
+  localSource: 'localSource'
+};
+
+const initialState = {
+  loading: false,
+  databases: [],
+  authenticated: false,
+
+  // source fields
+  replicationSource: '',
+  localSource: '',
+  remoteSource: '',
+
+  // target fields
+  replicationTarget: '',
+  localTarget: '',
+  remoteTarget: '',
+
+  // other
+  isPasswordModalVisible: false,
+  isConflictModalVisible: false,
+  replicationType: Constants.REPLICATION_TYPE.ONE_TIME,
+  replicationDocName: '',
+  submittedNoChange: false,
+  statusDocs: [],
+  statusFilteredStatusDocs: [],
+  statusFilter: '',
+  replicateFilter: '',
+  allDocsSelected: false,
+  allReplicateSelected: false,
+  username: '',
+  password: '',
+  activityLoading: false,
+  tabSection: 'new replication',
+  supportNewApi: false,
+  fetchingReplicateInfo: false,
+  replicateInfo: [],
+
+  checkingAPI: true,
+  activitySort: loadActivitySort()
+};
+
+const clearForm = (state) => {
+  const newState = {
+    ...state
+  };
+  Object.values(validFieldMap).forEach(field => newState[field] = '');
+  return newState;
+};
+
+const updateFormField = (state, fieldName, value) => {
+  const updateState = {
+    ...state,
+    submittedNoChange: false,
+  };
+
+  updateState[validFieldMap[fieldName]] = value;
+
+  return updateState;
+};
+
+const toggleDoc = (state, id) => {
+  const doc = state.statusDocs.find(doc => doc._id === id);
+  if (!doc) {
+    return state;
+  }
+
+  doc.selected = !doc.selected;
+
+  return {
+    ...state,
+    allDocsSelected: false
+  };
+};
+
+const selectAllDocs = (state) => {
+  const newState = {
+    ...state,
+    allDocsSelected: !state.allDocsSelected
+  };
+
+  getFilteredReplicationStatus(newState)
+    .forEach(doc => doc.selected = newState.allDocsSelected);
+
+  return newState;
+};
+
+const selectReplicate = (state, id) => {
+  const newState = {
+    ...state
+  };
+
+  const doc = newState._replicateInfo.find(doc => doc._id === id);
+
+  if (!doc) {
+    return newState;
+  }
+
+  doc.selected = !doc.selected;
+  newState._allReplicateSelected = false;
+};
+
+const selectAllReplicate = (state) => {
+  const newState = {
+    ...state,
+    allReplicateSelected: !state.allReplicateSelected
+  };
+
+  getReplicateInfo(newState).forEach(doc => doc.selected = 
newState.allReplicateSelected);
+  return newState;
+};
+
+const setStateFromDoc = (state, doc) => {
+  let newState = {
+    ...state,
+    loading: false
+  };
+
+  return Object.keys(doc).reduce((state, key) => {
+    return updateFormField(state, key, doc[key]);
+  }, newState);
+};
+
+
+const replication = (state = initialState, {type, options}) => {
+  switch (type) {
+
+    case ActionTypes.INIT_REPLICATION:
+      const newState = {
+        ...state,
+        loading: true
+      };
+
+      if (options.localSource) {
+        newState.localSource = options.localSource;
+        newState.replicationSource = Constants.REPLICATION_SOURCE.LOCAL;
+        newState.remoteSource = '';
+        newState.replicationTarget = '';
+        newState.localTarget = '';
+        newState.remoteTarget = '';
+      }
+      return newState;
+
+    case ActionTypes.REPLICATION_DATABASES_LOADED:
+      return {
+        ...state,
+        loading: false,
+        databases: options.databases
+      };
+
+    case ActionTypes.REPLICATION_FETCHING_FORM_STATE:
+      return {
+        ...state,
+        loading: true
+      };
+
+    case ActionTypes.REPLICATION_UPDATE_FORM_FIELD:
+      return updateFormField(state, options.fieldName, options.value);
+
+    case ActionTypes.REPLICATION_CLEAR_FORM:
+      return clearForm(state);
+
+    case ActionTypes.REPLICATION_STARTING:
+      return {
+        ...state,
+        submittedNoChange: true
+      };
+
+    case ActionTypes.REPLICATION_FETCHING_STATUS:
+      return {
+        ...state,
+        activityLoading: true
+      };
+
+    case ActionTypes.REPLICATION_STATUS:
+      return {
+        ...state,
+        activityLoading: false,
+        statusDocs: options
+      };
+
+    case ActionTypes.REPLICATION_FILTER_DOCS:
+      return {
+        ...state,
+        statusFilter: options
+      };
+
+    case ActionTypes.REPLICATION_FILTER_REPLICATE:
+      return {
+        ...state,
+        replicateFilter: options
+      };
+
+    case ActionTypes.REPLICATION_TOGGLE_DOC:
+      return toggleDoc(state, options);
+
+    case ActionTypes.REPLICATION_TOGGLE_ALL_DOCS:
+      return selectAllDocs(state);
+
+    case ActionTypes.REPLICATION_TOGGLE_REPLICATE:
+      return selectReplicate(state, options);
+
+    case ActionTypes.REPLICATION_TOGGLE_ALL_REPLICATE:
+      return selectAllReplicate(state);
+
+    case ActionTypes.REPLICATION_SET_STATE_FROM_DOC:
+      return setStateFromDoc(state, options);
+
+    case ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL:
+      return {
+        ...state,
+        isConflictModalVisible: true
+      };
+
+    case ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL:
+      return {
+        ...state,
+        isConflictModalVisible: false
+      };
+
+    case ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT:
+      setActivitySort(options);
+      return {
+        ...state,
+        activitySort: loadActivitySort()
+      };
+
+    case ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS:
+      return {
+        ...state,
+        allDocsSelected: false
+      };
+
+    case ActionTypes.REPLICATION_CHANGE_TAB_SECTION:
+      return {
+        ...state,
+        tabSection: options
+      };
+
+    case ActionTypes.REPLICATION_SUPPORT_NEW_API:
+      return {
+        ...state,
+        checkingApi: false,
+        supportNewApi: options
+      };
+
+    case ActionTypes.REPLICATION_FETCHING_REPLICATE_STATUS:
+      return {
+        ...state,
+        fetchingReplicateInfo: true,
+      };
+
+    case ActionTypes.REPLICATION_REPLICATE_STATUS:
+      return {
+        ...state,
+        fetchingReplicateInfo: false,
+        replicateInfo: options
+      };
+
+    case ActionTypes.REPLICATION_CLEAR_SELECTED_REPLICATES:
+      return {
+        ...state,
+        allReplicateSelected: false
+      };
+
+    case ActionTypes.REPLICATION_SHOW_PASSWORD_MODAL:
+      return {
+        ...state,
+        isPasswordModalVisible: true
+      };
+
+    case ActionTypes.REPLICATION_HIDE_PASSWORD_MODAL:
+      return {
+        ...state,
+        isPasswordModalVisible: false
+      };
+
+    default:
+      return state;
+  }
+};
+
+
+export const isLoading = (state) => state.isLoading;
+export const isActivityLoading = (state) => state.activityLoading;
+export const getDatabases = (state) => state.databases;
+export const isAuthenticated = (state) => state.authenticated;
+
+export const getReplicationSource = (state) => state.replicationSource;
+export const getLocalSource = (state) => state.localSource;
+export const isLocalSourceKnown = (state) => _.includes(state.databases, 
state.localSource);
+export const getRemoteSource = (state) => state.remoteSource;
+
+
+export const getReplicationTarget = (state) => state.replicationTarget;
+export const getLocalTarget = (state) => state.localTarget;
+export const isLocalTargetKnown = (state) => _.includes(state.databases, 
state.localTarget);
+export const getRemoteTarget = (state) => state.remoteTarget;
+
+export const isPasswordModalVisible = (state) => state.isPasswordModalVisible;
+export const isConflictModalVisible = (state) => state.isConflictModalVisible;
+export const getReplicationType = (state) => state.replicationType;
+export const getReplicationDocName = (state) => state.replicationDocName;
+export const getSubmittedNoChange = (state) => state.submittedNoChange;
+
+export const getFilteredReplicationStatus = (state) => {
+  return state.statusDocs.filter(doc => {
+    return Object.values(doc).filter(item => {
+      if (!item) {return null;}
+      return item.toString().toLowerCase().match(state.statusFilter);
+    }).length > 0;
+  });
+};
+
+export const getStatusFilter = (state) => state.statusFilter;
+export const getReplicateFilter = (state) => state.replicateFilter;
+export const getAllDocsSelected = (state) => state.allDocsSelected;
+export const getSomeDocsSelected = (state) => 
getFilteredReplicationStatus(state).some(doc => doc.selected);
+export const getUsername = (state) => state.username;
+export const getPassword = (state) => state.password;
+export const getActivitySort = (state) => state.activitySort;
+export const getTabSection = (state) => state.tabSection;
+export const getCheckingApi = (state) => state.checkingAPI;
+export const supportNewApi = (state) => state.supportNewApi;
+export const isReplicateInfoLoading = (state) => state.fetchingReplicateInfo;
+export const getAllReplicateSelected = (state) => state.allReplicateSelected;
+export const someReplicateSelected = (state) => 
getReplicateInfo(state).some(doc => doc.selected);
+
+export const getReplicateInfo = (state) => {
+  return state.replicateInfo.filter(doc => {
+    return Object.values(doc).filter(item => {
+      if (!item) {return false;}
+      return item.toString().toLowerCase().match(state._replicateFilter);
+    }).length > 0;
+  });
+};
+
+export default replication;
diff --git a/app/addons/replication/route.js b/app/addons/replication/route.js
index 187c5e0..efcd01b 100644
--- a/app/addons/replication/route.js
+++ b/app/addons/replication/route.js
@@ -12,14 +12,13 @@
 
 import React from 'react';
 import FauxtonAPI from '../../core/api';
-import ReplicationController from './controller';
-import Actions from './actions';
+import ReplicationController from './container';
 
 const ReplicationRouteObject = FauxtonAPI.RouteObject.extend({
   routes: {
-    'replication/_create': 'defaultView',
-    'replication/_create/:dbname': 'defaultView',
-    'replication/id/:id': 'fromId',
+    'replication/_create': 'createView',
+    'replication/_create/:dbname': 'createView',
+    'replication/id/:id': 'createViewFromId',
     'replication': 'activityView',
     'replication/_replicate': 'replicateView'
   },
@@ -40,32 +39,32 @@ const ReplicationRouteObject = 
FauxtonAPI.RouteObject.extend({
     ];
   },
 
-  defaultView: function (databaseName) {
+  createView: function (databaseName) {
     const localSource = databaseName || '';
-    Actions.changeTabSection('new replication');
-    Actions.clearReplicationForm();
 
     return <ReplicationController
-      localSource={localSource}
+      routeLocalSource={localSource}
+      section={'new replication'}
     />;
   },
 
-  fromId: function (replicationId) {
-    Actions.clearReplicationForm();
-    Actions.changeTabSection('new replication');
+  createViewFromId: function (replicationId) {
     return <ReplicationController
       replicationId={replicationId}
+      section={'new replication'}
     />;
   },
 
   activityView: function () {
-    Actions.changeTabSection('activity');
-    return <ReplicationController/>;
+    return <ReplicationController
+      section={"activity"}
+    />;
   },
 
   replicateView: function () {
-    Actions.changeTabSection('_replicate');
-    return <ReplicationController/>;
+    return <ReplicationController
+      section={"_replicate"}
+    />;
   }
 });
 
diff --git a/app/addons/replication/stores.js b/app/addons/replication/stores.js
deleted file mode 100644
index 5286550..0000000
--- a/app/addons/replication/stores.js
+++ /dev/null
@@ -1,459 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy 
of
-// the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations 
under
-// the License.
-import app from "../../app";
-import FauxtonAPI from '../../core/api';
-import ActionTypes from './actiontypes';
-import Constants from './constants';
-import AccountActionTypes from '../auth/actiontypes';
-import _ from 'lodash';
-
-// I know this could be done by just adding the _ prefix to the passed field 
name, I just don't much like relying
-// on the var names like that...
-const validFieldMap = {
-  remoteSource: '_remoteSource',
-  remoteTarget: '_remoteTarget',
-  localTarget: '_localTarget',
-  replicationType: '_replicationType',
-  replicationDocName: '_replicationDocName',
-  replicationSource: '_replicationSource',
-  replicationTarget: '_replicationTarget',
-  localSource: '_localSource'
-};
-
-const ReplicationStore = FauxtonAPI.Store.extend({
-  initialize () {
-    this.reset();
-  },
-
-  reset () {
-    this._loading = false;
-    this._databases = [];
-    this._authenticated = false;
-    this._password = '';
-
-    // source fields
-    this._replicationSource = '';
-    this._localSource = '';
-    this._remoteSource = '';
-
-    // target fields
-    this._replicationTarget = '';
-    this._localTarget = '';
-    this._remoteTarget = '';
-
-    // other
-    this._isPasswordModalVisible = false;
-    this._isConflictModalVisible = false;
-    this._replicationType = Constants.REPLICATION_TYPE.ONE_TIME;
-    this._replicationDocName = '';
-    this._submittedNoChange = false;
-    this._statusDocs = [];
-    this._statusFilteredStatusDocs = [];
-    this._statusFilter = '';
-    this._replicateFilter = '';
-    this._allDocsSelected = false;
-    this._allReplicateSelected = false;
-    this._username = '';
-    this._password = '';
-    this._activityLoading = false;
-    this._tabSection = 'new replication';
-    this._supportNewApi = true;
-
-    this.loadActivitySort();
-
-    this._fetchingReplicateInfo = false;
-    this._replicateInfo = [];
-
-    this._checkingAPI = true;
-    this._supportNewApi = false;
-  },
-
-  supportNewApi () {
-    return this._supportNewApi;
-  },
-
-  checkingAPI () {
-    return this._checkingAPI;
-  },
-
-  getActivitySort () {
-    return this._activitySort;
-  },
-
-  loadActivitySort () {
-    const defaultSort = {
-      descending: false,
-      column: 'statusTime'
-    };
-    let sort = app.utils.localStorageGet('replication-activity-sort');
-
-    if (!sort) {
-      sort = defaultSort;
-      this.setActivitySort(sort);
-    }
-
-    this._activitySort = sort;
-  },
-
-  setActivitySort (sort) {
-    app.utils.localStorageSet('replication-activity-sort', sort);
-    this._activitySort = sort;
-  },
-
-  isReplicateInfoLoading () {
-    return this._fetchingReplicateInfo;
-  },
-
-  getReplicateInfo () {
-    return this._replicateInfo.filter(doc => {
-      return _.values(doc).filter(item => {
-        if (!item) {return false;}
-        return item.toString().toLowerCase().match(this._replicateFilter);
-      }).length > 0;
-    });
-  },
-
-  setReplicateInfo (info) {
-    this._replicateInfo = info;
-  },
-
-  setCredentials (username, password) {
-    this._username = username;
-    this._password = password;
-  },
-
-  getUsername () {
-    return this._username;
-  },
-
-  getPassword () {
-    return this._password;
-  },
-
-  getSubmittedNoChange () {
-    return this._submittedNoChange;
-  },
-
-  changeAfterSubmit () {
-    this._submittedNoChange = false;
-  },
-
-  isLoading () {
-    return this._loading;
-  },
-
-  isActivityLoading () {
-    return this._activityLoading;
-  },
-
-  isAuthenticated () {
-    return this._authenticated;
-  },
-
-  getReplicationSource () {
-    return this._replicationSource;
-  },
-
-  getlocalSource () {
-    return this._localSource;
-  },
-
-  isLocalSourceKnown () {
-    return _.includes(this._databases, this._localSource);
-  },
-
-  isLocalTargetKnown () {
-    return _.includes(this._databases, this._localTarget);
-  },
-
-  getReplicationTarget () {
-    return this._replicationTarget;
-  },
-
-  getDatabases () {
-    return this._databases;
-  },
-
-  setDatabases (databases) {
-    this._databases = databases;
-  },
-
-  getReplicationType () {
-    return this._replicationType;
-  },
-
-  getlocalTarget () {
-    return this._localTarget;
-  },
-
-  getReplicationDocName () {
-    return this._replicationDocName;
-  },
-
-  setReplicationStatus (docs) {
-    this._statusDocs = docs;
-  },
-
-  getReplicationStatus () {
-    return this._statusDocs;
-  },
-
-  getFilteredReplicationStatus () {
-    return this._statusDocs.filter(doc => {
-      return _.values(doc).filter(item => {
-        if (!item) {return null;}
-        return item.toString().toLowerCase().match(this._statusFilter);
-      }).length > 0;
-    });
-  },
-
-  selectDoc (id) {
-    const doc = this._statusDocs.find(doc => doc._id === id);
-    if (!doc) {
-      return;
-    }
-
-    doc.selected = !doc.selected;
-    this._allDocsSelected = false;
-  },
-
-  selectReplicate (id) {
-    const doc = this._replicateInfo.find(doc => doc._id === id);
-    if (!doc) {
-      return;
-    }
-
-    doc.selected = !doc.selected;
-    this._allReplicateSelected = false;
-  },
-
-  selectAllReplicate () {
-    this._allReplicateSelected = !this._allReplicateSelected;
-    this.getReplicateInfo().forEach(doc => doc.selected = 
this._allReplicateSelected);
-  },
-
-  someReplicateSelected () {
-    return this.getReplicateInfo().some(doc => doc.selected);
-  },
-
-  getAllReplicateSelected () {
-    return this._allReplicateSelected;
-  },
-
-  selectAllDocs () {
-    this._allDocsSelected = !this._allDocsSelected;
-    this.getFilteredReplicationStatus().forEach(doc => doc.selected = 
this._allDocsSelected);
-  },
-
-  someDocsSelected () {
-    return this.getFilteredReplicationStatus().some(doc => doc.selected);
-  },
-
-  getAllDocsSelected () {
-    return this._allDocsSelected;
-  },
-
-  setStatusFilter (filter) {
-    this._statusFilter = filter;
-  },
-
-  getStatusFilter () {
-    return this._statusFilter;
-  },
-
-  setReplicateFilter (filter) {
-    this._replicateFilter = filter;
-  },
-
-  getReplicateFilter () {
-    return this._replicateFilter;
-  },
-  // to cut down on boilerplate
-  updateFormField (fieldName, value) {
-    this[validFieldMap[fieldName]] = value;
-  },
-
-  clearReplicationForm () {
-    _.values(validFieldMap).forEach(fieldName => this[fieldName] = '');
-  },
-
-  getRemoteSource () {
-    return this._remoteSource;
-  },
-
-  getRemoteTarget () {
-    return this._remoteTarget;
-  },
-
-  isPasswordModalVisible () {
-    return this._isPasswordModalVisible;
-  },
-
-  isConflictModalVisible () {
-    return this._isConflictModalVisible;
-  },
-
-  setStateFromDoc (doc) {
-    Object.keys(doc).forEach(key => {
-      this.updateFormField(key, doc[key]);
-    });
-  },
-
-  getTabSection () {
-    return this._tabSection;
-  },
-
-  dispatch ({type, options}) {
-    switch (type) {
-
-      case ActionTypes.INIT_REPLICATION:
-        this._loading = true;
-        this._localSource = options.localSource;
-
-        if (this._localSource) {
-          this._replicationSource = Constants.REPLICATION_SOURCE.LOCAL;
-          this._remoteSource = '';
-          this._replicationTarget = '';
-          this._localTarget = '';
-          this._remoteTarget = '';
-        }
-        break;
-
-      case ActionTypes.REPLICATION_DATABASES_LOADED:
-        this.setDatabases(options.databases);
-        this._loading = false;
-        break;
-
-      case ActionTypes.REPLICATION_FETCHING_FORM_STATE:
-        this._loading = true;
-        break;
-
-      case ActionTypes.REPLICATION_UPDATE_FORM_FIELD:
-        this.changeAfterSubmit();
-        this.updateFormField(options.fieldName, options.value);
-        break;
-
-      case ActionTypes.REPLICATION_CLEAR_FORM:
-        this.clearReplicationForm();
-        break;
-
-      case ActionTypes.REPLICATION_STARTING:
-        this._submittedNoChange = true;
-        break;
-
-      case ActionTypes.REPLICATION_FETCHING_STATUS:
-        this._activityLoading = true;
-        break;
-
-      case ActionTypes.REPLICATION_STATUS:
-        this._activityLoading = false;
-        this.setReplicationStatus(options);
-        break;
-
-      case ActionTypes.REPLICATION_FILTER_DOCS:
-        this.setStatusFilter(options);
-        break;
-
-      case ActionTypes.REPLICATION_FILTER_REPLICATE:
-        this.setReplicateFilter(options);
-        break;
-
-      case ActionTypes.REPLICATION_TOGGLE_DOC:
-        this.selectDoc(options);
-        break;
-
-      case ActionTypes.REPLICATION_TOGGLE_ALL_DOCS:
-        this.selectAllDocs();
-        break;
-
-      case ActionTypes.REPLICATION_TOGGLE_REPLICATE:
-        this.selectReplicate(options);
-        break;
-
-      case ActionTypes.REPLICATION_TOGGLE_ALL_REPLICATE:
-        this.selectAllReplicate();
-        break;
-
-      case ActionTypes.REPLICATION_SET_STATE_FROM_DOC:
-        this._loading = false;
-        this.setStateFromDoc(options);
-        break;
-
-      case ActionTypes.REPLICATION_SHOW_CONFLICT_MODAL:
-        this._isConflictModalVisible = true;
-        break;
-
-      case ActionTypes.REPLICATION_HIDE_CONFLICT_MODAL:
-        this._isConflictModalVisible = false;
-        break;
-
-      case ActionTypes.REPLICATION_CHANGE_ACTIVITY_SORT:
-        this.setActivitySort(options);
-        break;
-
-      case ActionTypes.REPLICATION_CLEAR_SELECTED_DOCS:
-        this._allDocsSelected = false;
-        break;
-
-      case ActionTypes.REPLICATION_CHANGE_TAB_SECTION:
-        this._tabSection = options;
-        break;
-
-      case ActionTypes.REPLICATION_SUPPORT_NEW_API:
-        this._checkingAPI = false;
-        this._supportNewApi = options;
-        break;
-
-      case ActionTypes.REPLICATION_FETCHING_REPLICATE_STATUS:
-        this._fetchingReplicateInfo = true;
-        break;
-
-      case ActionTypes.REPLICATION_REPLICATE_STATUS:
-        this._fetchingReplicateInfo = false;
-        this.setReplicateInfo(options);
-        break;
-
-      case ActionTypes.REPLICATION_CLEAR_SELECTED_REPLICATES:
-        this._allReplicateSelected = false;
-        break;
-
-      case AccountActionTypes.AUTH_SHOW_PASSWORD_MODAL:
-        this._isPasswordModalVisible = true;
-        break;
-
-      case AccountActionTypes.AUTH_HIDE_PASSWORD_MODAL:
-        this._isPasswordModalVisible = false;
-        break;
-
-      case AccountActionTypes.AUTH_CREDS_VALID:
-        this._authenticated = true;
-        this.setCredentials(options.username, options.password);
-        break;
-
-      case AccountActionTypes.AUTH_CREDS_INVALID:
-        this._authenticated = false;
-        break;
-
-      default:
-        return;
-    }
-
-    this.triggerChange();
-  }
-});
-
-const replicationStore = new ReplicationStore();
-replicationStore.dispatchToken = 
FauxtonAPI.dispatcher.register(replicationStore.dispatch);
-
-export default {
-  replicationStore
-};

-- 
To stop receiving notification emails like this one, please contact
amaran...@apache.org.

Reply via email to