diff --git a/client/app/scripts/components/nodes.js b/client/app/scripts/components/nodes.js index c41254d8fa..78b4945345 100644 --- a/client/app/scripts/components/nodes.js +++ b/client/app/scripts/components/nodes.js @@ -11,6 +11,7 @@ import { isTopologyNodeCountZero, isNodesDisplayEmpty, } from '../utils/topology-utils'; +import { nodesLoadedSelector } from '../selectors/node-filters'; import { isGraphViewModeSelector, isTableViewModeSelector, @@ -82,9 +83,9 @@ function mapStateToProps(state) { isResourceViewMode: isResourceViewModeSelector(state), topologyNodeCountZero: isTopologyNodeCountZero(state), nodesDisplayEmpty: isNodesDisplayEmpty(state), + nodesLoaded: nodesLoadedSelector(state), timeTravelTransitioning: state.get('timeTravelTransitioning'), currentTopology: state.get('currentTopology'), - nodesLoaded: state.get('nodesLoaded'), topologies: state.get('topologies'), topologiesLoaded: state.get('topologiesLoaded'), }; diff --git a/client/app/scripts/selectors/node-filters.js b/client/app/scripts/selectors/node-filters.js index 0d1d0c7d3b..b8b517ecf9 100644 --- a/client/app/scripts/selectors/node-filters.js +++ b/client/app/scripts/selectors/node-filters.js @@ -1,4 +1,9 @@ import { createSelector } from 'reselect'; +import { fromJS } from 'immutable'; + +import { isResourceViewModeSelector } from './topology'; +import { layoutNodesByTopologyIdSelector } from './resource-view/layout'; +import { RESOURCE_VIEW_LAYERS } from '../constants/resources'; export const shownNodesSelector = createSelector( @@ -7,3 +12,35 @@ export const shownNodesSelector = createSelector( ], nodes => nodes.filter(node => !node.get('filtered')) ); + +export const shownResourceTopologyIdsSelector = createSelector( + [ + layoutNodesByTopologyIdSelector, + ], + layoutNodesByTopologyId => layoutNodesByTopologyId.filter(nodes => !nodes.isEmpty()).keySeq() +); + +// TODO: Get rid of this logic by unifying `nodes` and `nodesByTopology` global states. +const loadedAllResourceLayersSelector = createSelector( + [ + state => state.get('nodesByTopology').keySeq(), + ], + resourceViewLoadedTopologyIds => fromJS(RESOURCE_VIEW_LAYERS).keySeq() + .every(topId => resourceViewLoadedTopologyIds.contains(topId)) +); + +export const nodesLoadedSelector = createSelector( + [ + state => state.get('nodesLoaded'), + loadedAllResourceLayersSelector, + isResourceViewModeSelector, + ], + (nodesLoaded, loadedAllResourceLayers, isResourceViewMode) => ( + // Since `nodesLoaded` is set when we receive nodes delta over websockets, + // it's a completely wrong criterion for determining if resource view is + // in the loading state - instead we look at the 'static' topologies whose + // nodes were loaded into 'nodesByTopology' and say resource view has been + // loaded if nodes for all the resource layer topologies have been loaded once. + isResourceViewMode ? loadedAllResourceLayers : nodesLoaded + ) +); diff --git a/client/app/scripts/utils/topology-utils.js b/client/app/scripts/utils/topology-utils.js index d1103d6f16..f92c15088d 100644 --- a/client/app/scripts/utils/topology-utils.js +++ b/client/app/scripts/utils/topology-utils.js @@ -4,7 +4,7 @@ import { Set as makeSet, List as makeList } from 'immutable'; import { isNowSelector } from '../selectors/time-travel'; import { isResourceViewModeSelector } from '../selectors/topology'; import { pinnedMetricSelector } from '../selectors/node-metric'; -import { shownNodesSelector } from '../selectors/node-filters'; +import { shownNodesSelector, shownResourceTopologyIdsSelector } from '../selectors/node-filters'; // // top priority first @@ -137,14 +137,15 @@ export function getCurrentTopologyOptions(state) { export function isTopologyNodeCountZero(state) { const nodeCount = state.getIn(['currentTopology', 'stats', 'node_count'], 0); - return nodeCount === 0 && isNowSelector(state); + // If we are browsing the past, assume there would normally be some nodes at different times. + // If we are in the resource view, don't rely on these stats at all (for now). + return nodeCount === 0 && isNowSelector(state) && !isResourceViewModeSelector(state); } export function isNodesDisplayEmpty(state) { // Consider a topology in the resource view empty if it has no pinned metric. if (isResourceViewModeSelector(state)) { - // TODO: Check for empty displays here after Scope v1.5.0 has been released. - return !pinnedMetricSelector(state); + return !pinnedMetricSelector(state) || shownResourceTopologyIdsSelector(state).isEmpty(); } // Otherwise (in graph and table view), we only look at the nodes content. return shownNodesSelector(state).isEmpty();