You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@couchdb.apache.org by GitBox <gi...@apache.org> on 2018/10/10 09:34:31 UTC

[GitHub] garrensmith closed pull request #1135: Update documents/designdocinfo to use redux

garrensmith closed pull request #1135: Update documents/designdocinfo to use redux
URL: https://github.com/apache/couchdb-fauxton/pull/1135
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/app/addons/documents/__tests__/designdocinfo-action.test.js b/app/addons/documents/__tests__/designdocinfo-action.test.js
index cb64564b8..f94df9f53 100644
--- a/app/addons/documents/__tests__/designdocinfo-action.test.js
+++ b/app/addons/documents/__tests__/designdocinfo-action.test.js
@@ -10,10 +10,11 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import FauxtonAPI from "../../../core/api";
-import Actions from "../designdocinfo/actions";
-import testUtils from "../../../../test/mocha/testUtils";
-import sinon from "sinon";
+import sinon from 'sinon';
+import FauxtonAPI from '../../../core/api';
+import testUtils from '../../../../test/mocha/testUtils';
+import Actions from '../designdocinfo/actions';
+
 const {assert, restore} = testUtils;
 
 describe('DesignDocInfo Actions', () => {
@@ -21,10 +22,10 @@ describe('DesignDocInfo Actions', () => {
   describe('fetchDesignDocInfo', () => {
 
     afterEach(() => {
-      restore(Actions.monitorDesignDoc);
+      restore(window.setInterval);
     });
 
-    it('calls monitorDesignDoc on successful fetch', () => {
+    it('schedules regular updates on successful fetch', () => {
       const promise = FauxtonAPI.Deferred();
       promise.resolve();
       const fakeDesignDocInfo = {
@@ -33,12 +34,13 @@ describe('DesignDocInfo Actions', () => {
         }
       };
 
-      const spy = sinon.spy(Actions, 'monitorDesignDoc');
+      const spy = sinon.spy(window, 'setInterval');
+      const dispatch = sinon.stub();
 
       return Actions.fetchDesignDocInfo({
         ddocName: 'test-designdoc-info',
         designDocInfo: fakeDesignDocInfo
-      }).then(() => {
+      })(dispatch).then(() => {
         assert.ok(spy.calledOnce);
       });
     });
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index 6809d5ffb..090a45099 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -14,6 +14,7 @@ import app from "../../app";
 import Helpers from "../../helpers";
 import FauxtonAPI from "../../core/api";
 import Documents from "./routes";
+import designDocInfoReducers from "./designdocinfo/reducers";
 import reducers from "./index-results/reducers";
 import mangoReducers from "./mango/mango.reducers";
 import sidebarReducers from "./sidebar/reducers";
@@ -26,7 +27,8 @@ FauxtonAPI.addReducers({
   mangoQuery: mangoReducers,
   sidebar: sidebarReducers,
   revisionBrowser: revisionBrowserReducers,
-  partitionKey: partitionKeyReducers
+  partitionKey: partitionKeyReducers,
+  designDocInfo: designDocInfoReducers
 });
 
 function getQueryParam (query) {
diff --git a/app/addons/documents/designdocinfo/actions.js b/app/addons/documents/designdocinfo/actions.js
index dc3920cdd..79004e696 100644
--- a/app/addons/documents/designdocinfo/actions.js
+++ b/app/addons/documents/designdocinfo/actions.js
@@ -10,42 +10,51 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import FauxtonAPI from "../../../core/api";
 import ActionTypes from "./actiontypes";
-import Stores from "./stores";
-var store = Stores.designDocInfoStore;
 
-export default {
-  fetchDesignDocInfo ({ddocName, designDocInfo}) {
-    FauxtonAPI.dispatch({
-      type: ActionTypes.DESIGN_FETCHING
-    });
+const fetchDesignDocInfo = ({designDocName, designDocInfo}) => (dispatch) => {
+  dispatch({
+    type: ActionTypes.DESIGN_FETCHING
+  });
 
-    return designDocInfo.fetch().then(() => {
-      this.monitorDesignDoc({
-        ddocName,
-        designDocInfo
-      });
-    });
-  },
+  return designDocInfo.fetch().then(() => {
+    monitorDesignDoc({
+      designDocName,
+      designDocInfo
+    }, dispatch);
+  });
+};
 
-  monitorDesignDoc (options) {
-    options.intervalId = window.setInterval(_.bind(this.refresh, this), 5000);
-    FauxtonAPI.dispatch({
-      type: ActionTypes.DESIGN_DOC_MONITOR,
-      options: options
-    });
-  },
+let intervalId;
+const monitorDesignDoc = (options, dispatch) => {
+  const refreshDDoc = () => {
+    refresh(options.designDocInfo, dispatch);
+  };
+  stopRefresh();
+  intervalId = window.setInterval(refreshDDoc, 5000);
+  dispatch({
+    type: ActionTypes.DESIGN_DOC_MONITOR,
+    options: options
+  });
+};
 
-  refresh () {
-    store.getDesignDocInfo().fetch().then(() => {
-      FauxtonAPI.dispatch({
-        type: ActionTypes.DESIGN_REFRESH
-      });
+const refresh = (designDocInfo, dispatch) => {
+  designDocInfo.fetch().then(() => {
+    dispatch({
+      type: ActionTypes.DESIGN_REFRESH,
+      designDocInfo
     });
-  },
+  });
+};
 
-  stopRefresh () {
-    window.clearInterval(store.getIntervalId());
+const stopRefresh = () => {
+  if (intervalId) {
+    window.clearInterval(intervalId);
+    intervalId = undefined;
   }
 };
+
+export default {
+  fetchDesignDocInfo,
+  stopRefresh
+};
diff --git a/app/addons/documents/designdocinfo/components.js b/app/addons/documents/designdocinfo/components/DesignDocInfo.js
similarity index 69%
rename from app/addons/documents/designdocinfo/components.js
rename to app/addons/documents/designdocinfo/components/DesignDocInfo.js
index 726bc93d9..70d60501e 100644
--- a/app/addons/documents/designdocinfo/components.js
+++ b/app/addons/documents/designdocinfo/components/DesignDocInfo.js
@@ -10,38 +10,38 @@
 // 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 ReactComponents from "../../components/react-components";
-var designDocInfoStore = Stores.designDocInfoStore;
-var LoadLines = ReactComponents.LoadLines;
-var Copy = ReactComponents.Copy;
+import PropTypes from 'prop-types';
+import React from 'react';
 import uuid from 'uuid';
+import FauxtonAPI from '../../../../core/api';
+import ReactComponents from '../../../components/react-components';
 
+const LoadLines = ReactComponents.LoadLines;
+const Copy = ReactComponents.Copy;
 
-class DesignDocInfo extends React.Component {
-  getStoreState = () => {
-    return {
-      viewIndex: designDocInfoStore.getViewIndex(),
-      isLoading: designDocInfoStore.isLoading(),
-      ddocName: designDocInfoStore.getDdocName()
-    };
-  };
+export default class DesignDocInfo extends React.Component {
+
+  constructor(props) {
+    super(props);
+    this.fetchDDocInfo();
+  }
 
-  componentDidMount() {
-    designDocInfoStore.on('change', this.onChange, this);
+  componentDidUpdate(prevProps) {
+    if (this.props.designDocInfo !== prevProps.designDocInfo) {
+      this.fetchDDocInfo();
+    }
   }
 
   componentWillUnmount() {
-    designDocInfoStore.off('change', this.onChange);
-    Actions.stopRefresh();
+    this.props.stopRefresh();
   }
 
-  onChange = () => {
-    this.setState(this.getStoreState());
-  };
+  fetchDDocInfo() {
+    this.props.fetchDesignDocInfo({
+      designDocName: this.props.designDocName,
+      designDocInfo: this.props.designDocInfo
+    });
+  }
 
   showCopiedMessage = () => {
     FauxtonAPI.addNotification({
@@ -51,22 +51,18 @@ class DesignDocInfo extends React.Component {
     });
   };
 
-  state = this.getStoreState();
-
   render() {
-    var viewIndex = this.state.viewIndex;
-
-    if (this.state.isLoading) {
+    if (this.props.isLoading) {
       return <LoadLines />;
     }
-
-    var actualSize = (viewIndex.data_size) ? viewIndex.data_size.toLocaleString('en') : 0;
-    var dataSize = (viewIndex.disk_size) ? viewIndex.disk_size.toLocaleString('en') : 0;
+    const viewIndex = this.props.viewIndex;
+    const actualSize = (viewIndex.data_size) ? viewIndex.data_size.toLocaleString('en') : 0;
+    const dataSize = (viewIndex.disk_size) ? viewIndex.disk_size.toLocaleString('en') : 0;
 
     return (
       <div className="metadata-page">
         <header>
-          <h2>_design/{this.state.ddocName} Metadata</h2>
+          <h2>_design/{this.props.designDocName} Metadata</h2>
         </header>
 
         <section className="container">
@@ -131,7 +127,10 @@ class DesignDocInfo extends React.Component {
   }
 }
 
-
-export default {
-  DesignDocInfo: DesignDocInfo
+DesignDocInfo.propTypes = {
+  isLoading: PropTypes.bool.isRequired,
+  viewIndex: PropTypes.object,
+  designDocName: PropTypes.string.isRequired,
+  stopRefresh: PropTypes.func.isRequired,
+  fetchDesignDocInfo: PropTypes.func.isRequired
 };
diff --git a/app/addons/documents/designdocinfo/components/DesignDocInfoContainer.js b/app/addons/documents/designdocinfo/components/DesignDocInfoContainer.js
new file mode 100644
index 000000000..2984b8635
--- /dev/null
+++ b/app/addons/documents/designdocinfo/components/DesignDocInfoContainer.js
@@ -0,0 +1,31 @@
+import { connect } from 'react-redux';
+import DesignDocInfo from './DesignDocInfo';
+import Actions from '../actions';
+
+const mapStateToProps = ({ designDocInfo }, ownProps) => {
+  return {
+    isLoading: designDocInfo.isLoading,
+    viewIndex: designDocInfo.viewIndex,
+    designDocInfo: ownProps.designDocInfo,
+    designDocName: ownProps.designDocName
+  };
+};
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    fetchDesignDocInfo: (options) => {
+      dispatch(Actions.fetchDesignDocInfo(options));
+    },
+
+    stopRefresh: () => {
+      Actions.stopRefresh();
+    }
+  };
+};
+
+const DesignDocInfoContainer = connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(DesignDocInfo);
+
+export default DesignDocInfoContainer;
diff --git a/app/addons/documents/designdocinfo/reducers.js b/app/addons/documents/designdocinfo/reducers.js
new file mode 100644
index 000000000..9b62b91b9
--- /dev/null
+++ b/app/addons/documents/designdocinfo/reducers.js
@@ -0,0 +1,47 @@
+// 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';
+
+const initialState = {
+  isLoading: true,
+  viewIndex: undefined
+};
+
+export default function designDocInfo (state = initialState, action) {
+  const { options } = action;
+
+  switch (action.type) {
+
+    case ActionTypes.DESIGN_FETCHING:
+      return {
+        ...state,
+        isLoading: true
+      };
+
+    case ActionTypes.DESIGN_DOC_MONITOR:
+      return {
+        ...state,
+        isLoading: false,
+        viewIndex: options.designDocInfo.get('view_index')
+      };
+
+    case ActionTypes.DESIGN_DOC_REFRESH:
+      return {
+        ...state,
+        viewIndex: options.designDocInfo.get('view_index')
+      };
+
+    default:
+      return state;
+  }
+}
diff --git a/app/addons/documents/designdocinfo/stores.js b/app/addons/documents/designdocinfo/stores.js
deleted file mode 100644
index d07f11db5..000000000
--- a/app/addons/documents/designdocinfo/stores.js
+++ /dev/null
@@ -1,82 +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 "./actiontypes";
-var Stores = {};
-
-Stores.DesignDocInfoStore = FauxtonAPI.Store.extend({
-
-  initialize: function () {
-    this._isLoading = true;
-  },
-
-  isLoading: function () {
-    return this._isLoading;
-  },
-
-  getDdocName: function () {
-    return this._ddocName;
-  },
-
-  getDesignDocInfo: function () {
-    return this._designDocInfo;
-  },
-
-  monitorDesignDoc: function (options) {
-    this._isLoading = false;
-    this._designDocInfo = options.designDocInfo;
-    this._ddocName = options.ddocName;
-    this._intervalId = options.intervalId;
-  },
-
-  getIntervalId: function () {
-    return this._intervalId;
-  },
-
-  getViewIndex: function () {
-    if (this._isLoading) {
-      return {};
-    }
-
-    return this._designDocInfo.get('view_index');
-  },
-
-  dispatch: function (action) {
-    switch (action.type) {
-      case ActionTypes.DESIGN_FETCHING:
-        this._isLoading = true;
-        this.triggerChange();
-        break;
-
-      case ActionTypes.DESIGN_DOC_MONITOR:
-        this.monitorDesignDoc(action.options);
-        this.triggerChange();
-        break;
-
-      case ActionTypes.DESIGN_DOC_REFRESH:
-        this.triggerChange();
-        break;
-
-      default:
-        return;
-      // do nothing
-    }
-  }
-
-});
-
-Stores.designDocInfoStore = new Stores.DesignDocInfoStore();
-
-Stores.designDocInfoStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.designDocInfoStore.dispatch.bind(Stores.designDocInfoStore));
-
-export default Stores;
diff --git a/app/addons/documents/layouts.js b/app/addons/documents/layouts.js
index bd6f680a6..bb11d66b0 100644
--- a/app/addons/documents/layouts.js
+++ b/app/addons/documents/layouts.js
@@ -18,7 +18,7 @@ import SidebarControllerContainer from "./sidebar/SidebarControllerContainer";
 import HeaderDocsLeft from './components/header-docs-left';
 import Changes from './changes/components';
 import IndexEditorComponents from "./index-editor/components";
-import DesignDocInfoComponents from './designdocinfo/components';
+import DesignDocInfoContainer from './designdocinfo/components/DesignDocInfoContainer';
 import RightAllDocsHeader from './components/header-docs-right';
 import IndexResultsContainer from './index-results/containers/IndexResultsContainer';
 import PaginationContainer from './index-results/containers/PaginationContainer';
@@ -47,13 +47,16 @@ export const TabsSidebarHeader = ({
   onGlobalModeSelected,
   globalMode
 }) => {
-  const partKeySelector = (<PartitionKeySelectorContainer
-    databaseName={dbName}
-    partitionKey={partitionKey}
-    onPartitionKeySelected={onPartitionKeySelected}
-    onGlobalModeSelected={onGlobalModeSelected}
-    globalMode={globalMode}/>
-  );
+  let partKeySelector = null;
+  if (showPartitionKeySelector) {
+    partKeySelector = (<PartitionKeySelectorContainer
+      databaseName={dbName}
+      partitionKey={partitionKey}
+      onPartitionKeySelected={onPartitionKeySelected}
+      onGlobalModeSelected={onGlobalModeSelected}
+      globalMode={globalMode}/>
+    );
+  }
 
   return (
     <header className="two-panel-header">
@@ -66,7 +69,7 @@ export const TabsSidebarHeader = ({
         </div>
         <div className="right-header-wrapper flex-layout flex-row flex-body">
           <div style={{flex:1, padding: '18px 6px 12px 12px'}}>
-            {showPartitionKeySelector ? partKeySelector : null}
+            {partKeySelector}
           </div>
           <div id="right-header" className="flex-fill">
             <RightAllDocsHeader
@@ -99,9 +102,9 @@ TabsSidebarHeader.propTypes = {
   database: PropTypes.object.isRequired,
   queryDocs: PropTypes.func,
   selectedNavItem: PropTypes.object,
-  showPartitionKeySelector: PropTypes.bool,
+  showPartitionKeySelector: PropTypes.bool.isRequired,
   partitionKey: PropTypes.string,
-  onPartitionKeySelected: PropTypes.func.isRequired,
+  onPartitionKeySelected: PropTypes.func,
   onGlobalModeSelected: PropTypes.bool,
   globalMode: PropTypes.bool
 };
@@ -235,9 +238,13 @@ export const ChangesSidebarLayout = ({ docURL, database, endpoint, dbName, dropD
 };
 
 export const ViewsTabsSidebarLayout = ({showEditView, database, docURL, endpoint,
-  dbName, dropDownLinks, selectedNavItem }) => {
+  dbName, dropDownLinks, selectedNavItem, designDocInfo }) => {
 
-  const content = showEditView ? <IndexEditorComponents.EditorController /> : <DesignDocInfoComponents.DesignDocInfo />;
+  const content = showEditView ?
+    <IndexEditorComponents.EditorController /> :
+    <DesignDocInfoContainer
+      designDocInfo={designDocInfo}
+      designDocName={selectedNavItem.designDocName}/>;
   return (
     <div id="dashboard" className="with-sidebar">
       <TabsSidebarHeader
@@ -268,5 +275,6 @@ ViewsTabsSidebarLayout.propTypes = {
   docURL: PropTypes.string.isRequired,
   endpoint: PropTypes.string,
   dbName: PropTypes.string.isRequired,
-  dropDownLinks: PropTypes.array.isRequired
+  dropDownLinks: PropTypes.array.isRequired,
+  designDocInfo: PropTypes.object
 };
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index 41c7493a3..e61fab789 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -17,7 +17,6 @@ import ChangesActions from './changes/actions';
 import Databases from '../databases/base';
 import Resources from './resources';
 import {SidebarItemSelection} from './sidebar/helpers';
-import DesignDocInfoActions from './designdocinfo/actions';
 import ComponentsActions from '../components/actions';
 import {DocsTabsSidebarLayout, ViewsTabsSidebarLayout, ChangesSidebarLayout} from './layouts';
 
@@ -52,11 +51,7 @@ var DocumentsRouteObject = BaseRoute.extend({
   },
 
   designDocMetadata: function (database, ddoc) {
-    var designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, { database: this.database });
-    DesignDocInfoActions.fetchDesignDocInfo({
-      ddocName: ddoc,
-      designDocInfo: designDocInfo
-    });
+    const designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, { database: this.database });
     const selectedNavItem = new SidebarItemSelection('designDoc', {
       designDocName: ddoc,
       designDocSection: 'metadata'
@@ -71,6 +66,7 @@ var DocumentsRouteObject = BaseRoute.extend({
       dropDownLinks={dropDownLinks}
       database={this.database}
       selectedNavItem={selectedNavItem}
+      designDocInfo={designDocInfo}
     />;
   },
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services