Bug 1368100 - Adapt console to the new messages state; r=bgrins draft
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Fri, 17 Nov 2017 15:18:25 +0100
changeset 699716 44dd9e83493734ed4d3b19bb783a7c86ddc28654
parent 699706 cb1f611219b3aea41db110e05084c4732e717430
child 699717 cf7ae5bca38a42e26a036faa0e2e031370dbfa21
push id89639
push userbmo:nchevobbe@mozilla.com
push dateFri, 17 Nov 2017 14:42:48 +0000
reviewersbgrins
bugs1368100
milestone59.0a1
Bug 1368100 - Adapt console to the new messages state; r=bgrins This fixes and renames messages selectors. This also introduce some changes in the ConsoleOutput component so we can see the messages delta in shouldComponentUpdate. MozReview-Commit-ID: EjOM5ptLBog
devtools/client/webconsole/new-console-output/actions/messages.js
devtools/client/webconsole/new-console-output/components/ConsoleOutput.js
devtools/client/webconsole/new-console-output/constants.js
devtools/client/webconsole/new-console-output/selectors/messages.js
devtools/client/webconsole/new-console-output/store.js
--- a/devtools/client/webconsole/new-console-output/actions/messages.js
+++ b/devtools/client/webconsole/new-console-output/actions/messages.js
@@ -8,17 +8,16 @@
 
 const {
   prepareMessage
 } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
 const { batchActions } = require("devtools/client/shared/redux/middleware/debounce");
 
 const {
-  MESSAGE_ADD,
   MESSAGES_ADD,
   NETWORK_MESSAGE_UPDATE,
   NETWORK_UPDATE_REQUEST,
   MESSAGES_CLEAR,
   MESSAGE_OPEN,
   MESSAGE_CLOSE,
   MESSAGE_TYPE,
   MESSAGE_TABLE_RECEIVE,
@@ -46,35 +45,16 @@ function messagesAdd(packets, idGenerato
   // When this is used for non-cached messages then handle clear message and
   // split up into batches
   return {
     type: MESSAGES_ADD,
     messages
   };
 }
 
-function messageAdd(packet, idGenerator = null) {
-  if (idGenerator == null) {
-    idGenerator = defaultIdGenerator;
-  }
-  let message = prepareMessage(packet, idGenerator);
-  const addMessageAction = {
-    type: MESSAGE_ADD,
-    message
-  };
-
-  if (message.type === MESSAGE_TYPE.CLEAR) {
-    return batchActions([
-      messagesClear(),
-      addMessageAction,
-    ]);
-  }
-  return addMessageAction;
-}
-
 function messagesClear() {
   return {
     type: MESSAGES_CLEAR
   };
 }
 
 function messageOpen(id) {
   return {
@@ -137,17 +117,16 @@ function networkUpdateRequest(id, data) 
   return {
     type: NETWORK_UPDATE_REQUEST,
     id,
     data,
   };
 }
 
 module.exports = {
-  messageAdd,
   messagesAdd,
   messagesClear,
   messageOpen,
   messageClose,
   messageTableDataGet,
   networkMessageUpdate,
   networkUpdateRequest,
   // for test purpose only.
--- a/devtools/client/webconsole/new-console-output/components/ConsoleOutput.js
+++ b/devtools/client/webconsole/new-console-output/components/ConsoleOutput.js
@@ -5,18 +5,18 @@
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const {initialize} = require("devtools/client/webconsole/new-console-output/actions/ui");
 
 const {
-  getAllMessagesById,
-  getAllMessagesUiById,
+  getMutableMessagesById,
+  getAllExpandedMessageIds,
   getAllMessagesTableDataById,
   getAllNetworkMessagesUpdateById,
   getVisibleMessages,
   getAllRepeatById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/MessageContainer").MessageContainer);
 const {
   MESSAGE_TYPE,
@@ -25,30 +25,31 @@ const {
   getInitialMessageCountForViewport
 } = require("devtools/client/webconsole/new-console-output/utils/messages.js");
 
 class ConsoleOutput extends Component {
   static get propTypes() {
     return {
       initialized: PropTypes.bool.isRequired,
       messages: PropTypes.object.isRequired,
-      messagesUi: PropTypes.object.isRequired,
+      expandedMessageIds: PropTypes.array.isRequired,
       serviceContainer: PropTypes.shape({
         attachRefToHud: PropTypes.func.isRequired,
         openContextMenu: PropTypes.func.isRequired,
         sourceMapService: PropTypes.object,
       }),
       dispatch: PropTypes.func.isRequired,
       timestampsVisible: PropTypes.bool,
       messagesTableData: PropTypes.object.isRequired,
       messagesRepeat: PropTypes.object.isRequired,
       networkMessagesUpdate: PropTypes.object.isRequired,
       visibleMessages: PropTypes.array.isRequired,
       networkMessageActiveTabId: PropTypes.string.isRequired,
       onFirstMeaningfulPaint: PropTypes.func.isRequired,
+      messagesCount: PropTypes.number.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.onContextMenu = this.onContextMenu.bind(this);
   }
 
@@ -78,33 +79,35 @@ class ConsoleOutput extends Component {
       // are added after a page refresh (Bug 1402237).
       this.shouldScrollBottom = true;
       return;
     }
 
     const lastChild = outputNode.lastChild;
     const visibleMessagesDelta =
       nextProps.visibleMessages.length - this.props.visibleMessages.length;
-    const messagesDelta =
-      nextProps.messages.size - this.props.messages.size;
+    const messagesDelta = nextProps.messagesCount - this.props.messagesCount;
+    const lastVisibleMessageIndex = nextProps.visibleMessages.length - 1;
+    const lastVisibleMessageId = nextProps.visibleMessages[lastVisibleMessageIndex];
+    const lastMessage = nextProps.messages.get(lastVisibleMessageId);
 
     // We need to scroll to the bottom if:
     // - we are reacting to the "initialize" action,
     //   and we are already scrolled to the bottom
     // - the number of messages displayed changed
     //   and we are already scrolled to the bottom
     // - the number of messages in the store changed
     //   and the new message is an evaluation result.
     this.shouldScrollBottom =
       (
         !this.props.initialized &&
         nextProps.initialized &&
         isScrolledToBottom(lastChild, outputNode)
       ) ||
-      (messagesDelta > 0 && nextProps.messages.last().type === MESSAGE_TYPE.RESULT) ||
+      (messagesDelta > 0 && lastMessage.type === MESSAGE_TYPE.RESULT) ||
       (visibleMessagesDelta > 0 && isScrolledToBottom(lastChild, outputNode));
   }
 
   componentDidUpdate() {
     if (this.shouldScrollBottom) {
       scrollToBottom(this.outputNode);
     }
   }
@@ -115,17 +118,17 @@ class ConsoleOutput extends Component {
     e.preventDefault();
   }
 
   render() {
     let {
       dispatch,
       visibleMessages,
       messages,
-      messagesUi,
+      expandedMessageIds,
       messagesTableData,
       messagesRepeat,
       networkMessagesUpdate,
       networkMessageActiveTabId,
       serviceContainer,
       timestampsVisible,
       initialized,
     } = this.props;
@@ -138,17 +141,17 @@ class ConsoleOutput extends Component {
       }
     }
 
     let messageNodes = visibleMessages.map((messageId) => MessageContainer({
       dispatch,
       key: messageId,
       messageId,
       serviceContainer,
-      open: messagesUi.includes(messageId),
+      open: expandedMessageIds.includes(messageId),
       tableData: messagesTableData.get(messageId),
       timestampsVisible,
       repeat: messagesRepeat[messageId],
       networkMessageUpdate: networkMessagesUpdate[messageId],
       networkMessageActiveTabId,
       getMessage: () => messages.get(messageId),
     }));
 
@@ -172,22 +175,24 @@ function scrollToBottom(node) {
 function isScrolledToBottom(outputNode, scrollNode) {
   let lastNodeHeight = outputNode.lastChild ?
                        outputNode.lastChild.clientHeight : 0;
   return scrollNode.scrollTop + scrollNode.clientHeight >=
          scrollNode.scrollHeight - lastNodeHeight / 2;
 }
 
 function mapStateToProps(state, props) {
+  const messages = getMutableMessagesById(state);
   return {
     initialized: state.ui.initialized,
-    messages: getAllMessagesById(state),
+    messages,
     visibleMessages: getVisibleMessages(state),
-    messagesUi: getAllMessagesUiById(state),
+    expandedMessageIds: getAllExpandedMessageIds(state),
     messagesTableData: getAllMessagesTableDataById(state),
     messagesRepeat: getAllRepeatById(state),
     networkMessagesUpdate: getAllNetworkMessagesUpdateById(state),
     timestampsVisible: state.ui.timestampsVisible,
     networkMessageActiveTabId: state.ui.networkMessageActiveTabId,
+    messagesCount: messages.size
   };
 }
 
 module.exports = connect(mapStateToProps)(ConsoleOutput);
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -8,17 +8,16 @@
 const actionTypes = {
   BATCH_ACTIONS: "BATCH_ACTIONS",
   DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
   FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
   FILTER_TEXT_SET: "FILTER_TEXT_SET",
   FILTER_TOGGLE: "FILTER_TOGGLE",
   FILTERS_CLEAR: "FILTERS_CLEAR",
   INITIALIZE: "INITIALIZE",
-  MESSAGE_ADD: "MESSAGE_ADD",
   MESSAGE_CLOSE: "MESSAGE_CLOSE",
   MESSAGE_OPEN: "MESSAGE_OPEN",
   MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
   MESSAGES_ADD: "MESSAGES_ADD",
   MESSAGES_CLEAR: "MESSAGES_CLEAR",
   NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
   NETWORK_UPDATE_REQUEST: "NETWORK_UPDATE_REQUEST",
   PERSIST_TOGGLE: "PERSIST_TOGGLE",
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -1,59 +1,68 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-function getAllMessagesById(state) {
-  return state.messages.messagesById;
+function getMessage(state, id) {
+  return getMutableMessagesById(state).get(id);
 }
 
-function getMessage(state, id) {
-  return getAllMessagesById(state).get(id);
-}
-
-function getAllMessagesUiById(state) {
-  return state.messages.messagesUiById;
+function getAllExpandedMessageIds(state) {
+  return state.messages.expandedMessageIds;
 }
 
 function getAllMessagesTableDataById(state) {
   return state.messages.messagesTableDataById;
 }
 
-function getAllGroupsById(state) {
-  return state.messages.groupsById;
-}
-
-function getCurrentGroup(state) {
-  return state.messages.currentGroup;
-}
-
 function getVisibleMessages(state) {
   return state.messages.visibleMessages;
 }
 
 function getFilteredMessagesCount(state) {
   return state.messages.filteredMessagesCount;
 }
 
 function getAllRepeatById(state) {
   return state.messages.repeatById;
 }
 
 function getAllNetworkMessagesUpdateById(state) {
   return state.messages.networkMessagesUpdateById;
 }
 
+function getMutable(state) {
+  return state.messages.mutable;
+}
+
+function getMutableCurrentGroup(state) {
+  return getMutable(state).currentGroup;
+}
+
+function getMutableGroupsById(state) {
+  return getMutable(state).groupsById;
+}
+
+function getMutableMessagesById(state) {
+  return getMutable(state).messagesById;
+}
+
+function getMutableRemovedActors(state) {
+  return getMutable(state).removedActors;
+}
+
 module.exports = {
-  getMessage,
-  getAllMessagesById,
-  getAllMessagesUiById,
+  getAllExpandedMessageIds,
   getAllMessagesTableDataById,
-  getAllGroupsById,
-  getCurrentGroup,
-  getVisibleMessages,
+  getAllNetworkMessagesUpdateById,
+  getAllRepeatById,
   getFilteredMessagesCount,
-  getAllRepeatById,
-  getAllNetworkMessagesUpdateById,
+  getMessage,
+  getMutableCurrentGroup,
+  getMutableGroupsById,
+  getMutableMessagesById,
+  getMutableRemovedActors,
+  getVisibleMessages,
 };
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -21,23 +21,22 @@ const {
   MESSAGES_CLEAR,
   REMOVED_ACTORS_CLEAR,
   NETWORK_MESSAGE_UPDATE,
   PREFS,
 } = require("devtools/client/webconsole/new-console-output/constants");
 const { reducers } = require("./reducers/index");
 const Services = require("Services");
 const {
+  getAllExpandedMessageIds,
+  getAllNetworkMessagesUpdateById,
   getMessage,
-  getAllMessagesUiById,
+  getMutableRemovedActors,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const DataProvider = require("devtools/client/netmonitor/src/connector/firefox-data-provider");
-const {
-  getAllNetworkMessagesUpdateById,
-} = require("devtools/client/webconsole/new-console-output/selectors/messages");
 
 /**
  * Create and configure store for the Console panel. This is the place
  * where various enhancers and middleware can be registered.
  */
 function configureStore(hud, options = {}) {
   const logLimit = options.logLimit
     || Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1);
@@ -126,17 +125,17 @@ function enableBatching() {
 function enableActorReleaser(hud) {
   return next => (reducer, initialState, enhancer) => {
     function releaseActorsEnhancer(state, action) {
       state = reducer(state, action);
 
       let type = action.type;
       let proxy = hud ? hud.proxy : null;
       if (proxy && (type == MESSAGE_ADD || type == MESSAGES_CLEAR)) {
-        releaseActors(state.messages.removedActors, proxy);
+        releaseActors(getMutableRemovedActors(state), proxy);
 
         // Reset `removedActors` in message reducer.
         state = reducer(state, {
           type: REMOVED_ACTORS_CLEAR
         });
       }
 
       return state;
@@ -222,17 +221,17 @@ function enableNetProvider(hud) {
             });
           });
         }
       }
 
       // Process all incoming HTTP details packets.
       if (type == NETWORK_MESSAGE_UPDATE) {
         let actor = action.response.networkInfo.actor;
-        let open = getAllMessagesUiById(state).includes(actor);
+        let open = getAllExpandedMessageIds(state).includes(actor);
         if (open) {
           dataProvider.onNetworkEventUpdate(null, action.response);
         }
       }
 
       return newState;
     }