You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2018/01/03 07:15:38 UTC

[couchdb-fauxton] branch master updated: move revision browser to use redux (#1036)

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

garren 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 74e2bb9  move revision browser to use redux (#1036)
74e2bb9 is described below

commit 74e2bb9054ed7b22176f709589851988cd78ad61
Author: garren smith <ga...@gmail.com>
AuthorDate: Wed Jan 3 09:15:36 2018 +0200

    move revision browser to use redux (#1036)
    
    move revision browser to use redux
---
 app/addons/documents/base.js                       |   4 +-
 .../index-results/components/results/TableRow.js   |   2 +-
 .../index-results/containers/ApiBarContainer.js    |   4 +-
 .../{rev-browser.actions.js => actions.js}         | 112 +++---
 .../{rev-browser.actiontypes.js => actiontypes.js} |  13 +-
 .../rev-browser/components/confirmmodal.js         |  86 +++++
 .../components/conflictingrevisiondropdown.js      |  53 +++
 .../documents/rev-browser/components/controller.js |  79 ++++
 .../components/revisionbrowsercontrols.js          | 146 +++++++
 .../rev-browser/components/revisiondiffarea.js     |  44 +++
 .../rev-browser/components/splitscreenarea.js      |  74 ++++
 app/addons/documents/rev-browser/container.js      |  85 ++++
 app/addons/documents/rev-browser/reducers.js       |  92 +++++
 .../rev-browser/rev-browser.components.js          | 430 ---------------------
 .../documents/rev-browser/rev-browser.stores.js    | 120 ------
 app/addons/documents/routes-doc-editor.js          |   7 +-
 16 files changed, 720 insertions(+), 631 deletions(-)

diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index 46433e1..053b9b4 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -16,12 +16,14 @@ import Documents from "./routes";
 import reducers from "./index-results/reducers";
 import mangoReducers from "./mango/mango.reducers";
 import sidebarReducers from "./sidebar/reducers";
+import revisionBrowserReducers from './rev-browser/reducers';
 import "./assets/less/documents.less";
 
 FauxtonAPI.addReducers({
   indexResults: reducers,
   mangoQuery: mangoReducers,
-  sidebar: sidebarReducers
+  sidebar: sidebarReducers,
+  revisionBrowser: revisionBrowserReducers
 });
 
 function getQueryParam (query) {
diff --git a/app/addons/documents/index-results/components/results/TableRow.js b/app/addons/documents/index-results/components/results/TableRow.js
index b983d86..c9da4ac 100644
--- a/app/addons/documents/index-results/components/results/TableRow.js
+++ b/app/addons/documents/index-results/components/results/TableRow.js
@@ -93,7 +93,7 @@ export default class TableRow extends React.Component {
     }
 
     return (
-      <td className="tableview-el-last" onClick={this.onClick}>
+      <td className="tableview-el-last" onClick={this.onClick.bind(this)}>
         {conflictIndicator}
         {attachmentIndicator}
       </td>
diff --git a/app/addons/documents/index-results/containers/ApiBarContainer.js b/app/addons/documents/index-results/containers/ApiBarContainer.js
index a65e063..72e3065 100644
--- a/app/addons/documents/index-results/containers/ApiBarContainer.js
+++ b/app/addons/documents/index-results/containers/ApiBarContainer.js
@@ -1,5 +1,3 @@
-import { connect } from 'react-redux';
-
 // 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
@@ -13,7 +11,7 @@ import { connect } from 'react-redux';
 // the License.
 
 import PropTypes from 'prop-types';
-
+import { connect } from 'react-redux';
 import app from '../../../../app';
 import { ApiBarWrapper } from '../../../components/layouts';
 import { getQueryOptionsParams } from '../reducers';
diff --git a/app/addons/documents/rev-browser/rev-browser.actions.js b/app/addons/documents/rev-browser/actions.js
similarity index 56%
rename from app/addons/documents/rev-browser/rev-browser.actions.js
rename to app/addons/documents/rev-browser/actions.js
index 1668799..ec080bc 100644
--- a/app/addons/documents/rev-browser/rev-browser.actions.js
+++ b/app/addons/documents/rev-browser/actions.js
@@ -13,7 +13,8 @@
 /* global FormData */
 
 import FauxtonAPI from "../../../core/api";
-import ActionTypes from "./rev-browser.actiontypes";
+import {post} from "../../../core/ajax";
+import ActionTypes from "./actiontypes";
 import getTree from "visualizeRevTree/lib/getTree";
 import PouchDB from "pouchdb-core";
 import PouchHttpAdapter from 'pouchdb-adapter-http';
@@ -21,33 +22,26 @@ PouchDB.plugin(PouchHttpAdapter);
 
 let db;
 
-function initDiffEditor (dbName, docId) {
+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"
-  const d1 = $.Deferred();
-  const d2 = $.Deferred();
-  $.when(d1, d2).done((tree, doc) => {
+  Promise.all([db.get(docId), getTree(db, docId)])
+  .then(([doc, tree]) => {
     const conflictingRevs = getConflictingRevs(tree.paths, tree.winner, Object.keys(tree.deleted));
     const initialRev = conflictingRevs[0];
 
     if (!initialRev) {
-      return dispatchData(tree, doc, conflictingRevs, null, dbName);
+      return dispatch(treeLoaded(tree, doc, conflictingRevs, null, dbName));
     }
 
     db.get(doc._id, {rev: initialRev})
       .then((conflictDoc) => {
-        dispatchData(tree, doc, conflictingRevs, conflictDoc, dbName);
+        dispatch(treeLoaded(tree, doc, conflictingRevs, conflictDoc, dbName));
       });
   });
-
-  db.get(docId)
-    .then(d2.resolve);
-
-  getTree(db, docId)
-    .then(d1.resolve);
-}
+};
 
 function getConflictingRevs (paths, winner, deleted) {
 
@@ -63,81 +57,77 @@ function getConflictingRevs (paths, winner, deleted) {
   });
 }
 
-function dispatchData (tree, doc, conflictingRevs, conflictDoc, databaseName) {
-  FauxtonAPI.dispatch({
+const treeLoaded = (tree, doc, conflictingRevs, conflictDoc, databaseName) => {
+  return {
     type: ActionTypes.REV_BROWSER_REV_TREE_LOADED,
     options: {
-      tree: tree,
-      doc: doc,
-      conflictDoc: conflictDoc,
-      conflictingRevs: conflictingRevs,
-      databaseName: databaseName
+      tree,
+      doc,
+      conflictDoc,
+      conflictingRevs,
+      databaseName
     }
-  });
-}
+  };
+};
 
-function toggleDiffView (enableDiff) {
-  FauxtonAPI.dispatch({
+export const toggleDiffView = (enableDiff) => {
+  return {
     type: ActionTypes.REV_BROWSER_DIFF_ENABLE_DIFF_VIEW,
     options: {
       enableDiff: enableDiff
     }
-  });
-}
+  };
+};
 
-function chooseLeaves (doc, revTheirs) {
+export const chooseLeaves = (doc, revTheirs) => dispatch => {
   db.get(doc._id, {rev: revTheirs})
     .then((res) => {
-      dispatchDocsToDiff(doc, res);
+      dispatch(docsToDiff(doc, res));
     });
-}
+};
 
-function dispatchDocsToDiff (doc, theirs) {
-  FauxtonAPI.dispatch({
+const docsToDiff = (doc, theirs) => {
+  return {
     type: ActionTypes.REV_BROWSER_DIFF_DOCS_READY,
     options: {
       theirs: theirs,
       ours: doc
     }
-  });
-}
+  };
+};
 
-function showConfirmModal (show, docToWin) {
-  FauxtonAPI.dispatch({
+export const toggleConfirmModal = (show, docToWin) => {
+  return {
     type: ActionTypes.REV_BROWSER_SHOW_CONFIRM_MODAL,
     options: {
       show: show,
       docToWin: docToWin
     }
-  });
-}
+  };
+};
 
-function selectRevAsWinner (databaseName, docId, paths, revToWin) {
+export const selectRevAsWinner = (databaseName, docId, paths, revToWin) => dispatch => {
   const revsToDelete = getConflictingRevs(paths, revToWin, []);
   const payload = buildBulkDeletePayload(docId, revsToDelete);
 
-  $.ajax({
-    url: FauxtonAPI.urls('bulk_docs', 'server', databaseName, ''),
-    type: 'POST',
-    contentType: 'application/json; charset=UTF-8',
-    data: JSON.stringify(payload),
-    success: () => {
-      FauxtonAPI.addNotification({
-        msg: 'Conflicts successfully solved.',
-        clear: true
-      });
-      showConfirmModal(false, null);
-      FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', databaseName, ''));
-    },
-    error: () => {
-      FauxtonAPI.addNotification({
+  post(FauxtonAPI.urls('bulk_docs', 'server', databaseName, ''), payload)
+  .then((resp) => {
+    if (resp.error) {
+      return FauxtonAPI.addNotification({
         msg: 'Failed to delete clean up conflicts!',
         type: 'error',
         clear: true
       });
     }
+
+    FauxtonAPI.addNotification({
+      msg: 'Conflicts successfully solved.',
+      clear: true
+    });
+    dispatch(toggleConfirmModal(false, null));
+    FauxtonAPI.navigate(FauxtonAPI.urls('allDocs', 'app', databaseName, ''));
   });
-}
+};
 
 function buildBulkDeletePayload (docId, revs) {
   const list = revs.map((rev) => {
@@ -150,15 +140,3 @@ function buildBulkDeletePayload (docId, revs) {
 
   return { "docs": list };
 }
-
-export default {
-  getConflictingRevs: getConflictingRevs,
-  selectRevAsWinner: selectRevAsWinner,
-  buildBulkDeletePayload: buildBulkDeletePayload,
-  chooseLeaves: chooseLeaves,
-  dispatchDocsToDiff: dispatchDocsToDiff,
-  initDiffEditor: initDiffEditor,
-  dispatchData: dispatchData,
-  toggleDiffView: toggleDiffView,
-  showConfirmModal: showConfirmModal
-};
diff --git a/app/addons/documents/rev-browser/rev-browser.actiontypes.js b/app/addons/documents/rev-browser/actiontypes.js
similarity index 56%
rename from app/addons/documents/rev-browser/rev-browser.actiontypes.js
rename to app/addons/documents/rev-browser/actiontypes.js
index b54a609..0604d4f 100644
--- a/app/addons/documents/rev-browser/rev-browser.actiontypes.js
+++ b/app/addons/documents/rev-browser/actiontypes.js
@@ -10,9 +10,14 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+export const REV_BROWSER_REV_TREE_LOADED = 'REV_TREE_LOADED';
+export const REV_BROWSER_DIFF_DOCS_READY = 'REV_BROWSER_DIFF_DOCS_READY';
+export const REV_BROWSER_DIFF_ENABLE_DIFF_VIEW = 'REV_BROWSER_DIFF_ENABLE_DIFF_VIEW';
+export const REV_BROWSER_SHOW_CONFIRM_MODAL = 'REV_BROWSER_SHOW_CONFIRM_MODAL';
+
 export default {
-  REV_BROWSER_REV_TREE_LOADED: 'REV_TREE_LOADED',
-  REV_BROWSER_DIFF_DOCS_READY: 'REV_BROWSER_DIFF_DOCS_READY',
-  REV_BROWSER_DIFF_ENABLE_DIFF_VIEW: 'REV_BROWSER_DIFF_ENABLE_DIFF_VIEW',
-  REV_BROWSER_SHOW_CONFIRM_MODAL: 'REV_BROWSER_SHOW_CONFIRM_MODAL'
+  REV_BROWSER_REV_TREE_LOADED,
+  REV_BROWSER_DIFF_DOCS_READY,
+  REV_BROWSER_DIFF_ENABLE_DIFF_VIEW,
+  REV_BROWSER_SHOW_CONFIRM_MODAL
 };
diff --git a/app/addons/documents/rev-browser/components/confirmmodal.js b/app/addons/documents/rev-browser/components/confirmmodal.js
new file mode 100644
index 0000000..77fc8a7
--- /dev/null
+++ b/app/addons/documents/rev-browser/components/confirmmodal.js
@@ -0,0 +1,86 @@
+// 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 PropTypes from 'prop-types';
+import React from "react";
+import ReactDOM from "react-dom";
+import ReactComponents from "../../../components/react-components";
+import { Modal } from "react-bootstrap";
+
+const ConfirmButton = ReactComponents.ConfirmButton;
+
+export default class ConfirmModal extends React.Component {
+
+  constructor (props) {
+    super(props);
+
+    this.state = {
+      checked: false
+    };
+
+    this.close = this.close.bind(this);
+    this.onDeleteConflicts = this.onDeleteConflicts.bind(this);
+  }
+
+  close () {
+    this.props.toggleConfirmModal(false, null);
+  }
+
+  onDeleteConflicts () {
+    const hideModal = this.state.checked;
+    this.props.onConfirm(this.props.docToWin, hideModal);
+  }
+
+  render () {
+    return (
+      <Modal dialogClassName="delete-conflicts-modal" show={this.props.show} onHide={this.close}>
+        <Modal.Header closeButton={false}>
+          <Modal.Title>Solve Conflicts</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <p>
+          <i className="icon-warning-sign"></i> Do you want to delete all conflicting revisions for this document?
+          </p>
+
+
+        </Modal.Body>
+        <Modal.Footer>
+          <div style={{float: 'left', marginTop: '10px'}}>
+            <label>
+              <input
+                style={{margin: '0 5px 3px 0'}}
+                onChange={() => { this.setState({checked: !this.state.checked }); }}
+                type="checkbox" />
+                Do not show this warning message again
+            </label>
+          </div>
+          <a
+            style={{marginRight: '10px', cursor: 'pointer'}}
+            onClick={this.close}
+            data-bypass="true"
+          >
+            Cancel
+          </a>
+
+          <ConfirmButton
+            onClick={this.onDeleteConflicts}
+            text="Delete Revisions"
+            buttonType="btn-danger" />
+        </Modal.Footer>
+      </Modal>
+    );
+  }
+}
+ConfirmModal.propTypes = {
+  onConfirm: PropTypes.func.isRequired,
+  toggleConfirmModal: PropTypes.func.isRequired
+};
diff --git a/app/addons/documents/rev-browser/components/conflictingrevisiondropdown.js b/app/addons/documents/rev-browser/components/conflictingrevisiondropdown.js
new file mode 100644
index 0000000..78c6a6d
--- /dev/null
+++ b/app/addons/documents/rev-browser/components/conflictingrevisiondropdown.js
@@ -0,0 +1,53 @@
+// 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 React from 'react';
+import ReactDOM from "react-dom";
+import PropTypes from 'prop-types';
+import ReactSelect from "react-select";
+
+const BackForwardControls = ({onClick, forward}) => {
+  const icon = forward ? 'fonticon-right-open' : 'fonticon-left-open';
+  const style = {height: '20px', width: '11px', marginTop: '7px'};
+
+  return <div style={style} className={icon} onClick={onClick}></div>;
+};
+
+BackForwardControls.propTypes = {
+  onClick: PropTypes.func.isRequired,
+};
+
+const ConflictingRevisionsDropDown = ({options, selected, onRevisionClick, onBackwardClick, onForwardClick}) => {
+  return (
+    <div className="conflicting-revs-dropdown">
+      <BackForwardControls backward onClick={onBackwardClick} />
+      <div style={{width: '345px', margin: '0 5px'}}>
+        <ReactSelect
+          name="form-field-name"
+          value={selected}
+          options={options}
+          clearable={false}
+          onChange={onRevisionClick} />
+      </div>
+      <BackForwardControls forward onClick={onForwardClick} />
+    </div>
+  );
+};
+ConflictingRevisionsDropDown.propTypes = {
+  options: PropTypes.array.isRequired,
+  selected: PropTypes.string.isRequired,
+  onRevisionClick: PropTypes.func.isRequired,
+  onBackwardClick: PropTypes.func.isRequired,
+  onForwardClick: PropTypes.func.isRequired,
+};
+
+export default ConflictingRevisionsDropDown;
diff --git a/app/addons/documents/rev-browser/components/controller.js b/app/addons/documents/rev-browser/components/controller.js
new file mode 100644
index 0000000..11b5472
--- /dev/null
+++ b/app/addons/documents/rev-browser/components/controller.js
@@ -0,0 +1,79 @@
+// 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 React from 'react';
+import ReactDOM from "react-dom";
+import RevisionBrowserControls from './revisionbrowsercontrols';
+import SplitScreenArea from './splitscreenarea';
+import RevisionDiffArea from './revisiondiffarea';
+import { ButtonGroup, Button } from "react-bootstrap";
+import {LoadLines} from '../../../components/components/loadlines';
+
+
+export default class DiffyController extends React.Component {
+
+  constructor (props) {
+    super(props);
+    this.toggleDiffViewFalse = this.toggleDiffView.bind(this, false);
+    this.toggleDiffViewTrue = this.toggleDiffView.bind(this, true);
+  }
+
+  componentDidMount () {
+    this.props.toggleConfirmModal(false, null);
+    this.props.initDiffEditor();
+  }
+
+  toggleDiffView (enableDiff) {
+    this.props.toggleDiffView(enableDiff);
+  }
+
+  render () {
+    const {tree, ours, theirs, conflictingRevs, isDiffViewEnabled} = this.props;
+
+    if (!tree.length && !theirs) {
+      return <LoadLines />;
+    }
+
+    // no conflicts happened for this doc
+    if (!theirs || !conflictingRevs.length) {
+      return <div style={{textAlign: 'center', color: '#fff'}}><h2>No conflicts</h2></div>;
+    }
+
+    return (
+      <div className="revision-wrapper scrollable">
+        <RevisionBrowserControls {...this.props} />
+        <div className="revision-view-controls">
+          <ButtonGroup className="two-sides-toggle-button">
+            <Button
+              style={{width: '120px'}}
+              className={isDiffViewEnabled ? 'active' : ''}
+              onClick={this.toggleDiffViewTrue}
+            >
+              <i className="icon-columns" /> Diff
+            </Button>
+            <Button
+              style={{width: '120px'}}
+              className={isDiffViewEnabled ? '' : 'active'}
+              onClick={this.toggleDiffViewFalse}
+            >
+              <i className="icon-file-text" /> Document
+            </Button>
+          </ButtonGroup>
+        </div>
+
+        {isDiffViewEnabled ?
+          <RevisionDiffArea ours={ours} theirs={theirs} /> :
+          <SplitScreenArea ours={ours} theirs={theirs} /> }
+      </div>
+    );
+  }
+}
diff --git a/app/addons/documents/rev-browser/components/revisionbrowsercontrols.js b/app/addons/documents/rev-browser/components/revisionbrowsercontrols.js
new file mode 100644
index 0000000..7cc7dec
--- /dev/null
+++ b/app/addons/documents/rev-browser/components/revisionbrowsercontrols.js
@@ -0,0 +1,146 @@
+// 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 React from 'react';
+import ReactDOM from "react-dom";
+import PropTypes from 'prop-types';
+import app from '../../../../app';
+import ReactComponents from "../../../components/react-components";
+import ConflictingRevisionsDropDown from './conflictingrevisiondropdown';
+import ConfirmModal from './confirmmodal';
+
+const ConfirmButton = ReactComponents.ConfirmButton;
+const storageKeyDeleteConflictsModal = 'deleteConflictsHideModal';
+
+export default class RevisionBrowserControls extends React.Component {
+
+  constructor (props) {
+    super(props);
+
+    this.state = {showModal: false};
+    this.selectAsWinner = this.selectAsWinner.bind(this);
+    this.onSelectAsWinnerClickOurs = this.onSelectAsWinnerClick.bind(this, this.props.ours);
+    this.onSelectAsWinnerClickTheirs = this.onSelectAsWinnerClick.bind(this, this.props.theirs);
+    this.onRevisionClick = this.onRevisionClick.bind(this);
+    this.onForwardClick = this.onForwardClick.bind(this);
+    this.onBackwardClick = this.onBackwardClick.bind(this);
+  }
+
+  onRevisionClick (revTheirs) {
+    this.props.chooseLeaves(this.props.ours, revTheirs.value);
+  }
+
+  onForwardClick () {
+    const conflictingRevs = this.props.conflictingRevs;
+    const index = conflictingRevs.indexOf(this.props.theirs._rev);
+
+    const next = conflictingRevs[index + 1];
+
+    if (!next) {
+      return;
+    }
+
+    this.props.chooseLeaves(this.props.ours, next);
+  }
+
+  onBackwardClick () {
+    const conflictingRevs = this.props.conflictingRevs;
+    const index = conflictingRevs.indexOf(this.props.theirs._rev);
+
+    const next = conflictingRevs[index - 1];
+
+    if (!next) {
+      return;
+    }
+
+    this.props.chooseLeaves(this.props.ours, next);
+  }
+
+  selectAsWinner (docToWin, doNotShowModalAgain) {
+    if (doNotShowModalAgain) {
+      app.utils.localStorageSet(storageKeyDeleteConflictsModal, true);
+    }
+
+    this.props.selectRevAsWinner(docToWin._id, docToWin._rev, this.props.tree);
+  }
+
+  onSelectAsWinnerClick (docToWin) {
+    if (app.utils.localStorageGet(storageKeyDeleteConflictsModal) !== true) {
+      this.props.toggleConfirmModal(true, docToWin);
+      return;
+    }
+
+    this.selectAsWinner(docToWin);
+  }
+
+  render () {
+    const {tree} = this.props;
+    const cellStyle = {paddingRight: '30px'};
+
+    return (
+      <div className="revision-browser-controls">
+        <ConfirmModal
+          toggleConfirmModal={this.props.toggleConfirmModal}
+          onConfirm={this.selectAsWinner}
+          docToWin={this.props.docToWin}
+          show={this.props.showConfirmModal}
+        />
+        <table style={{margin: '10px 60px', width: '100%'}}>
+          <tbody>
+            <tr style={{height: '60px'}}>
+              <td style={cellStyle}>Server-Selected Rev: </td>
+              <td style={cellStyle}>
+                <div style={{lineHeight: '36px', height: '36px', width: '337px', color: '#000', backgroundColor: '#ffbbbb'}}>
+                  <b style={{paddingLeft: '10px'}}>{tree.winner}</b>
+                </div>
+              </td>
+              <td>
+                <ConfirmButton
+                  onClick={this.onSelectAsWinnerClickOurs}
+                  style={{marginRight: '10px', width: '220px'}}
+                  text="Delete Other Conflicts"
+                  buttonType="btn-secondary"
+                  customIcon="icon-trophy" />
+              </td>
+            </tr>
+            <tr style={{height: '60px'}}>
+              <td style={cellStyle}>Conflicting Revisions: </td>
+              <td style={cellStyle}>
+                <ConflictingRevisionsDropDown
+                  onRevisionClick={this.onRevisionClick}
+                  onForwardClick={this.onForwardClick}
+                  onBackwardClick={this.onBackwardClick}
+                  options={this.props.dropdownData}
+                  selected={this.props.theirs._rev} />
+              </td>
+              <td>
+                <ConfirmButton
+                  data-id="button-select-theirs"
+                  onClick={this.onSelectAsWinnerClickTheirs}
+                  style={{marginRight: '10px', width: '220px'}}
+                  text="Select as Winner"
+                  buttonType="btn-secondary"
+                  customIcon="icon-trophy" />
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+
+    );
+  }
+}
+RevisionBrowserControls.propTypes = {
+  tree: PropTypes.object.isRequired,
+  ours: PropTypes.object.isRequired,
+  conflictingRevs: PropTypes.array.isRequired,
+};
diff --git a/app/addons/documents/rev-browser/components/revisiondiffarea.js b/app/addons/documents/rev-browser/components/revisiondiffarea.js
new file mode 100644
index 0000000..b86eb07
--- /dev/null
+++ b/app/addons/documents/rev-browser/components/revisiondiffarea.js
@@ -0,0 +1,44 @@
+// 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 React from 'react';
+import ReactDOM from "react-dom";
+import PropTypes from 'prop-types';
+import jdp from "jsondiffpatch";
+import jdpformatters from "jsondiffpatch/src/formatters/html";
+import "jsondiffpatch/public/formatters-styles/html.css";
+
+const RevisionDiffArea = ({ours, theirs}) => {
+  if (!ours || !theirs) {
+    return <div></div>;
+  }
+
+  const delta = jdp.diff(ours, theirs);
+  const html = jdpformatters.format(delta, ours);
+
+  return (
+    <div className="revision-diff-area">
+      <div
+        style={{marginTop: '30px'}}
+        dangerouslySetInnerHTML={{__html: html}}
+      >
+      </div>
+    </div>
+  );
+};
+RevisionDiffArea.propTypes = {
+  ours: PropTypes.object,
+  theirs: PropTypes.object,
+  currentRev: PropTypes.string
+};
+
+export default RevisionDiffArea;
diff --git a/app/addons/documents/rev-browser/components/splitscreenarea.js b/app/addons/documents/rev-browser/components/splitscreenarea.js
new file mode 100644
index 0000000..49aa300
--- /dev/null
+++ b/app/addons/documents/rev-browser/components/splitscreenarea.js
@@ -0,0 +1,74 @@
+// 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 React from 'react';
+import ReactDOM from "react-dom";
+import ace from "brace";
+
+require('brace/ext/static_highlight');
+const highlight = ace.acequire('ace/ext/static_highlight');
+
+require('brace/mode/json');
+const JavaScriptMode = ace.acequire('ace/mode/json').Mode;
+
+require('brace/theme/idle_fingers');
+const theme = ace.acequire('ace/theme/idle_fingers');
+
+export default class SplitScreenArea extends React.Component {
+
+  constructor (props) {
+    super(props);
+  }
+
+  componentDidUpdate () {
+    this.hightlightAfterRender();
+  }
+
+  componentDidMount () {
+    this.hightlightAfterRender();
+  }
+
+  hightlightAfterRender () {
+    const format = (input) => { return JSON.stringify(input, null, '  '); };
+
+    const jsmode = new JavaScriptMode();
+    const left = ReactDOM.findDOMNode(this.refs.revLeftOurs);
+    const right = ReactDOM.findDOMNode(this.refs.revRightTheirs);
+
+    const leftRes = highlight.render(format(this.props.ours), jsmode, theme, 0, true);
+    left.innerHTML = leftRes.html;
+    const rightRes = highlight.render(format(this.props.theirs), jsmode, theme, 0, true);
+    right.innerHTML = rightRes.html;
+  }
+
+  render () {
+    const {ours, theirs} = this.props;
+
+    if (!ours || !theirs) {
+      return <div></div>;
+    }
+
+    return (
+      <div className="revision-split-area">
+        <div data-id="ours" style={{width: '50%'}}>
+          <div style={{marginBottom: '20px'}}>{ours._rev} (Server-Selected Rev)</div>
+          <pre ref="revLeftOurs"></pre>
+        </div>
+
+        <div data-id="theirs" style={{width: '50%'}}>
+          <div style={{marginBottom: '20px'}}>{theirs._rev}</div>
+          <pre ref="revRightTheirs"></pre>
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/app/addons/documents/rev-browser/container.js b/app/addons/documents/rev-browser/container.js
new file mode 100644
index 0000000..345b05d
--- /dev/null
+++ b/app/addons/documents/rev-browser/container.js
@@ -0,0 +1,85 @@
+// 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 { connect } from 'react-redux';
+import RevisionBrowserController from './components/controller';
+
+import {
+  getTree,
+  getDoc,
+  getConflictDoc,
+  getConflictingRev,
+  getDatabaseName,
+  getOurs,
+  getTheirs,
+  getShowConfirmModal,
+  getDropdownData,
+  getIsDiffViewEnabled,
+  getDocToWin
+} from './reducers';
+
+import {
+  toggleConfirmModal,
+  initDiffEditor,
+  chooseLeaves,
+  selectRevAsWinner,
+  toggleDiffView
+} from './actions';
+
+const mapStateToProps = ({revisionBrowser}, ownProps) => {
+  return {
+    databaseName: ownProps.databaseName,
+    docId: ownProps.docId,
+    tree: getTree(revisionBrowser),
+    doc: getDoc(revisionBrowser),
+    conflictDoc: getConflictDoc(revisionBrowser),
+    conflictingRevs: getConflictingRev(revisionBrowser),
+    databaseName: getDatabaseName(revisionBrowser),
+    ours: getOurs(revisionBrowser),
+    theirs: getTheirs(revisionBrowser),
+    showConfirmModal: getShowConfirmModal(revisionBrowser),
+    dropdownData: getDropdownData(revisionBrowser),
+    isDiffViewEnabled: getIsDiffViewEnabled(revisionBrowser),
+    docToWin: getDocToWin(revisionBrowser)
+  };
+};
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+  return {
+    toggleConfirmModal (show, docToWin) {
+      dispatch(toggleConfirmModal(show, docToWin));
+    },
+
+    initDiffEditor () {
+      dispatch(initDiffEditor(ownProps.databaseName, ownProps.docId));
+    },
+
+    chooseLeaves (doc, revTheirs) {
+      dispatch(chooseLeaves(doc, revTheirs));
+    },
+
+    selectRevAsWinner (id, rev, tree) {
+      dispatch(selectRevAsWinner(ownProps.databaseName, id, tree.paths, rev));
+    },
+
+    toggleDiffView (enableDiff) {
+      dispatch(toggleDiffView(enableDiff));
+    }
+  };
+};
+
+const RevisionBrowserContainer = connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(RevisionBrowserController);
+
+export default RevisionBrowserContainer;
diff --git a/app/addons/documents/rev-browser/reducers.js b/app/addons/documents/rev-browser/reducers.js
new file mode 100644
index 0000000..4ce2ed9
--- /dev/null
+++ b/app/addons/documents/rev-browser/reducers.js
@@ -0,0 +1,92 @@
+// 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 {
+  REV_BROWSER_REV_TREE_LOADED,
+  REV_BROWSER_DIFF_DOCS_READY,
+  REV_BROWSER_DIFF_ENABLE_DIFF_VIEW,
+  REV_BROWSER_SHOW_CONFIRM_MODAL
+} from './actiontypes';
+
+const initialState = {
+  tree: [],
+  doc: {},
+  conflictDoc: false,
+  conflictingRevs: [],
+  databaseName: '',
+  ours: null,
+  theirs: null,
+  isDiffViewEnabled: true,
+  showConfirmModal: false,
+  docToWin: null
+};
+
+const revisionConflicts = (state = initialState, {type, options}) => {
+
+  switch (type) {
+    case REV_BROWSER_REV_TREE_LOADED:
+      const {
+        tree,
+        doc,
+        conflictDoc,
+        conflictingRevs,
+        databaseName,
+      } = options;
+      return {
+        ...state,
+        tree,
+        doc,
+        conflictDoc,
+        conflictingRevs,
+        databaseName,
+        theirs: conflictDoc,
+        ours: doc
+      };
+
+    case REV_BROWSER_DIFF_DOCS_READY:
+      return {
+        ...state,
+        theirs: options.theirs
+      };
+
+    case REV_BROWSER_DIFF_ENABLE_DIFF_VIEW:
+      return {
+        ...state,
+        isDiffViewEnabled: options.enableDiff
+      };
+
+    case REV_BROWSER_SHOW_CONFIRM_MODAL:
+      return {
+        ...state,
+        showConfirmModal: options.show,
+        docToWin: options.docToWin
+      };
+
+    default:
+      return state;
+  }
+
+};
+
+export default revisionConflicts;
+
+export const getTree = state => state.tree;
+export const getDoc = state => state.doc;
+export const getConflictDoc = state => state.conflictDoc;
+export const getConflictingRev = state => state.conflictingRevs;
+export const getDatabaseName = state => state.databaseName;
+export const getOurs = state => state.ours;
+export const getTheirs = state => state.theirs;
+export const getShowConfirmModal = state => state.showConfirmModal;
+export const getDropdownData = state => state.conflictingRevs.map(rev => { return {value: rev, label: rev}; });
+export const getIsDiffViewEnabled = state => state.isDiffViewEnabled;
+export const getDocToWin = state => state.docToWin;
diff --git a/app/addons/documents/rev-browser/rev-browser.components.js b/app/addons/documents/rev-browser/rev-browser.components.js
deleted file mode 100644
index 7edf906..0000000
--- a/app/addons/documents/rev-browser/rev-browser.components.js
+++ /dev/null
@@ -1,430 +0,0 @@
-import app from "../../../app";
-
-// 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 PropTypes from 'prop-types';
-
-import React from "react";
-import ReactDOM from "react-dom";
-import RevActions from "./rev-browser.actions";
-import RevStores from "./rev-browser.stores";
-import ReactComponents from "../../components/react-components";
-import { ButtonGroup, Button, Modal } from "react-bootstrap";
-import ReactSelect from "react-select";
-import jdp from "jsondiffpatch";
-import jdpformatters from "jsondiffpatch/src/formatters/html";
-import ace from "brace";
-import "jsondiffpatch/public/formatters-styles/html.css";
-
-const storageKeyDeleteConflictsModal = 'deleteConflictsHideModal';
-
-const store = RevStores.revBrowserStore;
-const ConfirmButton = ReactComponents.ConfirmButton;
-
-require('brace/ext/static_highlight');
-const highlight = ace.acequire('ace/ext/static_highlight');
-
-require('brace/mode/json');
-const JavaScriptMode = ace.acequire('ace/mode/json').Mode;
-
-require('brace/theme/idle_fingers');
-const theme = ace.acequire('ace/theme/idle_fingers');
-
-
-class DiffyController extends React.Component {
-
-  constructor (props) {
-    super(props);
-
-    this.state = this.getStoreState();
-  }
-
-  getStoreState () {
-
-    return {
-      tree: store.getRevTree(),
-      ours: store.getOurs(),
-      theirs: store.getTheirs(),
-      conflictingRevs: store.getConflictingRevs(),
-      dropdownData: store.getDropdownData(),
-      isDiffViewEnabled: store.getIsDiffViewEnabled(),
-      databaseName: store.getDatabaseName()
-    };
-  }
-
-  componentDidMount () {
-    store.on('change', this.onChange, this);
-  }
-
-  componentWillUnmount () {
-    store.off('change', this.onChange);
-  }
-
-  onChange () {
-    this.setState(this.getStoreState());
-  }
-
-  toggleDiffView (enableDiff) {
-    RevActions.toggleDiffView(enableDiff);
-  }
-
-  render () {
-    const {tree, ours, theirs, conflictingRevs, isDiffViewEnabled} = this.state;
-
-    if (!tree) {
-      return null;
-    }
-
-    // no conflicts happened for this doc
-    if (!theirs || !conflictingRevs.length) {
-      return <div style={{textAlign: 'center', color: '#fff'}}><h2>No conflicts</h2></div>;
-    }
-
-    return (
-      <div className="revision-wrapper scrollable">
-        <RevisionBrowserControls {...this.state} />
-        <div className="revision-view-controls">
-          <ButtonGroup className="two-sides-toggle-button">
-            <Button
-              style={{width: '120px'}}
-              className={isDiffViewEnabled ? 'active' : ''}
-              onClick={this.toggleDiffView.bind(this, true)}
-            >
-              <i className="icon-columns" /> Diff
-            </Button>
-            <Button
-              style={{width: '120px'}}
-              className={isDiffViewEnabled ? '' : 'active'}
-              onClick={this.toggleDiffView.bind(this, false)}
-            >
-              <i className="icon-file-text" /> Document
-            </Button>
-          </ButtonGroup>
-        </div>
-
-        {isDiffViewEnabled ?
-          <RevisionDiffArea ours={ours} theirs={theirs} /> :
-          <SplitScreenArea ours={ours} theirs={theirs} /> }
-      </div>
-    );
-  }
-}
-
-
-class SplitScreenArea extends React.Component {
-
-  constructor (props) {
-    super(props);
-  }
-
-  componentDidUpdate () {
-    this.hightlightAfterRender();
-  }
-
-  componentDidMount () {
-    this.hightlightAfterRender();
-  }
-
-  hightlightAfterRender () {
-    const format = (input) => { return JSON.stringify(input, null, '  '); };
-
-    const jsmode = new JavaScriptMode();
-    const left = ReactDOM.findDOMNode(this.refs.revLeftOurs);
-    const right = ReactDOM.findDOMNode(this.refs.revRightTheirs);
-
-    const leftRes = highlight.render(format(this.props.ours), jsmode, theme, 0, true);
-    left.innerHTML = leftRes.html;
-    const rightRes = highlight.render(format(this.props.theirs), jsmode, theme, 0, true);
-    right.innerHTML = rightRes.html;
-  }
-
-  render () {
-    const {ours, theirs} = this.props;
-
-    if (!ours || !theirs) {
-      return <div></div>;
-    }
-
-    return (
-      <div className="revision-split-area">
-        <div data-id="ours" style={{width: '50%'}}>
-          <div style={{marginBottom: '20px'}}>{ours._rev} (Server-Selected Rev)</div>
-          <pre ref="revLeftOurs"></pre>
-        </div>
-
-        <div data-id="theirs" style={{width: '50%'}}>
-          <div style={{marginBottom: '20px'}}>{theirs._rev}</div>
-          <pre ref="revRightTheirs"></pre>
-        </div>
-      </div>
-    );
-  }
-}
-
-const RevisionDiffArea = ({ours, theirs}) => {
-  if (!ours || !theirs) {
-    return <div></div>;
-  }
-
-  const delta = jdp.diff(ours, theirs);
-  const html = jdpformatters.format(delta, ours);
-
-  return (
-    <div className="revision-diff-area">
-      <div
-        style={{marginTop: '30px'}}
-        dangerouslySetInnerHTML={{__html: html}}
-      >
-      </div>
-    </div>
-  );
-};
-RevisionDiffArea.propTypes = {
-  ours: PropTypes.object,
-  theirs: PropTypes.object,
-  currentRev: PropTypes.string
-};
-
-
-const ConflictingRevisionsDropDown = ({options, selected, onRevisionClick, onBackwardClick, onForwardClick}) => {
-  return (
-    <div className="conflicting-revs-dropdown">
-      <BackForwardControls backward onClick={onBackwardClick} />
-      <div style={{width: '345px', margin: '0 5px'}}>
-        <ReactSelect
-          name="form-field-name"
-          value={selected}
-          options={options}
-          clearable={false}
-          onChange={onRevisionClick} />
-      </div>
-      <BackForwardControls forward onClick={onForwardClick} />
-    </div>
-  );
-};
-ConflictingRevisionsDropDown.propTypes = {
-  options: PropTypes.array.isRequired,
-  selected: PropTypes.string.isRequired,
-  onRevisionClick: PropTypes.func.isRequired,
-  onBackwardClick: PropTypes.func.isRequired,
-  onForwardClick: PropTypes.func.isRequired,
-};
-
-class RevisionBrowserControls extends React.Component {
-
-  constructor (props) {
-    super(props);
-
-    this.state = {showModal: false};
-  }
-
-  onRevisionClick (revTheirs) {
-
-    RevActions.chooseLeaves(this.props.ours, revTheirs.value);
-  }
-
-  onForwardClick () {
-    const conflictingRevs = this.props.conflictingRevs;
-    const index = conflictingRevs.indexOf(this.props.theirs._rev);
-
-    const next = conflictingRevs[index + 1];
-
-    if (!next) {
-      return;
-    }
-
-    RevActions.chooseLeaves(this.props.ours, next);
-  }
-
-  onBackwardClick () {
-    const conflictingRevs = this.props.conflictingRevs;
-    const index = conflictingRevs.indexOf(this.props.theirs._rev);
-
-    const next = conflictingRevs[index - 1];
-
-    if (!next) {
-      return;
-    }
-
-    RevActions.chooseLeaves(this.props.ours, next);
-  }
-
-  selectAsWinner (docToWin, doNotShowModalAgain) {
-    if (doNotShowModalAgain) {
-      app.utils.localStorageSet(storageKeyDeleteConflictsModal, true);
-    }
-
-    RevActions.selectRevAsWinner(this.props.databaseName, docToWin._id, this.props.tree.paths, docToWin._rev);
-  }
-
-  onSelectAsWinnerClick (docToWin) {
-    if (app.utils.localStorageGet(storageKeyDeleteConflictsModal) !== true) {
-      RevActions.showConfirmModal(true, docToWin);
-      return;
-    }
-
-    this.selectAsWinner(docToWin);
-  }
-
-  render () {
-    const {tree} = this.props;
-    const cellStyle = {paddingRight: '30px'};
-
-    return (
-      <div className="revision-browser-controls">
-        <ConfirmModal onConfirm={this.selectAsWinner.bind(this)} />
-        <table style={{margin: '10px 60px', width: '100%'}}>
-          <tbody>
-            <tr style={{height: '60px'}}>
-              <td style={cellStyle}>Server-Selected Rev: </td>
-              <td style={cellStyle}>
-                <div style={{lineHeight: '36px', height: '36px', width: '337px', color: '#000', backgroundColor: '#ffbbbb'}}>
-                  <b style={{paddingLeft: '10px'}}>{tree.winner}</b>
-                </div>
-              </td>
-              <td>
-                <ConfirmButton
-                  onClick={this.onSelectAsWinnerClick.bind(this, this.props.ours)}
-                  style={{marginRight: '10px', width: '220px'}}
-                  text="Delete Other Conflicts"
-                  buttonType="btn-secondary"
-                  customIcon="icon-trophy" />
-              </td>
-            </tr>
-            <tr style={{height: '60px'}}>
-              <td style={cellStyle}>Conflicting Revisions: </td>
-              <td style={cellStyle}>
-                <ConflictingRevisionsDropDown
-                  onRevisionClick={this.onRevisionClick.bind(this)}
-                  onForwardClick={this.onForwardClick.bind(this)}
-                  onBackwardClick={this.onBackwardClick.bind(this)}
-                  options={this.props.dropdownData}
-                  selected={this.props.theirs._rev} />
-              </td>
-              <td>
-                <ConfirmButton
-                  data-id="button-select-theirs"
-                  onClick={this.onSelectAsWinnerClick.bind(this, this.props.theirs)}
-                  style={{marginRight: '10px', width: '220px'}}
-                  text="Select as Winner"
-                  buttonType="btn-secondary"
-                  customIcon="icon-trophy" />
-              </td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-
-    );
-  }
-}
-RevisionBrowserControls.propTypes = {
-  tree: PropTypes.object.isRequired,
-  ours: PropTypes.object.isRequired,
-  conflictingRevs: PropTypes.array.isRequired,
-};
-
-class ConfirmModal extends React.Component {
-
-  constructor (props) {
-    super(props);
-
-    this.state = this.getStoreState();
-  }
-
-  getStoreState () {
-    return {
-      show: store.getShowConfirmModal(),
-      docToWin: store.getDocToWin(),
-      checked: false
-    };
-  }
-
-  componentDidMount () {
-    store.on('change', this.onChange, this);
-  }
-
-  componentWillUnmount () {
-    store.off('change', this.onChange);
-  }
-
-  onChange () {
-    this.setState(this.getStoreState());
-  }
-
-  close () {
-    RevActions.showConfirmModal(false, null);
-  }
-
-  onDeleteConflicts () {
-    const hideModal = this.state.checked;
-    this.props.onConfirm(this.state.docToWin, hideModal);
-  }
-
-  render () {
-    return (
-      <Modal dialogClassName="delete-conflicts-modal" show={this.state.show} onHide={this.close}>
-        <Modal.Header closeButton={false}>
-          <Modal.Title>Solve Conflicts</Modal.Title>
-        </Modal.Header>
-        <Modal.Body>
-          <p>
-          <i className="icon-warning-sign"></i> Do you want to delete all conflicting revisions for this document?
-          </p>
-
-
-        </Modal.Body>
-        <Modal.Footer>
-          <div style={{float: 'left', marginTop: '10px'}}>
-            <label>
-              <input
-                style={{margin: '0 5px 3px 0'}}
-                onChange={() => { this.setState({checked: !this.state.checked }); }}
-                type="checkbox" />
-                Do not show this warning message again
-            </label>
-          </div>
-          <a
-            style={{marginRight: '10px', cursor: 'pointer'}}
-            onClick={this.close}
-            data-bypass="true"
-          >
-            Cancel
-          </a>
-
-          <ConfirmButton
-            onClick={this.onDeleteConflicts.bind(this)}
-            text="Delete Revisions"
-            buttonType="btn-danger" />
-        </Modal.Footer>
-      </Modal>
-    );
-  }
-}
-ConfirmModal.propTypes = {
-  onConfirm: PropTypes.func.isRequired,
-};
-
-const BackForwardControls = ({onClick, forward}) => {
-  const icon = forward ? 'fonticon-right-open' : 'fonticon-left-open';
-  const style = {height: '20px', width: '11px', marginTop: '7px'};
-
-  return <div style={style} className={icon} onClick={onClick}></div>;
-};
-BackForwardControls.propTypes = {
-  onClick: PropTypes.func.isRequired,
-};
-
-export default {
-  DiffyController: DiffyController
-};
diff --git a/app/addons/documents/rev-browser/rev-browser.stores.js b/app/addons/documents/rev-browser/rev-browser.stores.js
deleted file mode 100644
index c4b4afa..0000000
--- a/app/addons/documents/rev-browser/rev-browser.stores.js
+++ /dev/null
@@ -1,120 +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 FauxtonAPI from "../../../core/api";
-import ActionTypes from "./rev-browser.actiontypes";
-
-const Stores = {};
-
-Stores.RevBrowserStore = FauxtonAPI.Store.extend({
-  initialize: function () {
-    this.reset();
-  },
-
-  reset: function () {
-    this._revTree = null;
-
-    this._ours = null;
-    this._theirs = null;
-
-    this._dropDownData = null;
-    this._isDiffViewEnabled = true;
-
-    this._databaseName = null;
-
-    this._showConfirmModal = false;
-    this._docToWin = null;
-  },
-
-  prepareDropdownData: function (revs) {
-    return revs.map((el) => {
-
-      return { value: el, label: el };
-    });
-  },
-
-  getRevTree: function () {
-    return this._revTree;
-  },
-
-  getDatabaseName: function () {
-    return this._databaseName;
-  },
-
-  getOurs: function () {
-    return this._ours;
-  },
-
-  getTheirs: function () {
-    return this._theirs;
-  },
-
-  getConflictingRevs: function () {
-    return this._conflictingRevs;
-  },
-
-  getDropdownData: function () {
-    return this._dropDownData;
-  },
-
-  getIsDiffViewEnabled: function () {
-    return this._isDiffViewEnabled;
-  },
-
-  getShowConfirmModal: function () {
-    return this._showConfirmModal;
-  },
-
-  getDocToWin: function () {
-    return this._docToWin;
-  },
-
-  dispatch: function (action) {
-    switch (action.type) {
-      case ActionTypes.REV_BROWSER_REV_TREE_LOADED:
-        this._revTree = action.options.tree;
-        this._ours = action.options.doc;
-        this._conflictingRevs = action.options.conflictingRevs;
-        this._theirs = action.options.conflictDoc;
-
-        this._dropDownData = this.prepareDropdownData(this._conflictingRevs);
-
-        this._databaseName = action.options.databaseName;
-      break;
-
-      case ActionTypes.REV_BROWSER_DIFF_DOCS_READY:
-        this._theirs = action.options.theirs;
-      break;
-
-      case ActionTypes.REV_BROWSER_DIFF_ENABLE_DIFF_VIEW:
-        this._isDiffViewEnabled = action.options.enableDiff;
-      break;
-
-      case ActionTypes.REV_BROWSER_SHOW_CONFIRM_MODAL:
-        this._showConfirmModal = action.options.show;
-        this._docToWin = action.options.docToWin;
-      break;
-
-      default:
-      return;
-      // do nothing
-    }
-
-    this.triggerChange();
-  }
-
-});
-
-Stores.revBrowserStore = new Stores.RevBrowserStore();
-Stores.revBrowserStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.revBrowserStore.dispatch);
-
-export default Stores;
diff --git a/app/addons/documents/routes-doc-editor.js b/app/addons/documents/routes-doc-editor.js
index 89342c5..a5f49ce 100644
--- a/app/addons/documents/routes-doc-editor.js
+++ b/app/addons/documents/routes-doc-editor.js
@@ -16,8 +16,7 @@ import Documents from "./resources";
 import Databases from "../databases/base";
 import Actions from "./doc-editor/actions";
 import ReactComponents from "./doc-editor/components";
-import RevBrowserActions from "./rev-browser/rev-browser.actions";
-import RevBrowserComponents from "./rev-browser/rev-browser.components";
+import RevBrowserContainer from './rev-browser/container';
 import {DocEditorLayout} from '../components/layouts';
 
 
@@ -51,13 +50,11 @@ const DocEditorRouteObject = FauxtonAPI.RouteObject.extend({
       { name: this.docId + ' > Conflicts' }
     ];
 
-    RevBrowserActions.showConfirmModal(false, null);
-    RevBrowserActions.initDiffEditor(databaseName, docId);
     return <DocEditorLayout
       crumbs={crumbs}
       endpoint={this.doc.url('apiurl')}
       docURL={docURL}
-      component={<RevBrowserComponents.DiffyController />}
+      component={<RevBrowserContainer docId={docId} databaseName={databaseName} />}
       />;
   },
 

-- 
To stop receiving notification emails like this one, please contact
['"commits@couchdb.apache.org" <co...@couchdb.apache.org>'].