diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js index a1b8035e9..1e06472d7 100644 --- a/client/app/scripts/actions/app-actions.js +++ b/client/app/scripts/actions/app-actions.js @@ -339,14 +339,15 @@ export function clickNode(nodeId, label, origin, topologyId = null) { export function pauseTimeAtNow() { return (dispatch, getState) => { + const getScopeState = () => getState().scope || getState(); dispatch({ type: ActionTypes.PAUSE_TIME_AT_NOW }); - updateRoute(getState); - if (!getState().get('nodesLoaded')) { - getNodes(getState, dispatch); - if (isResourceViewModeSelector(getState())) { - getResourceViewNodesSnapshot(getState(), dispatch); + updateRoute(getScopeState); + if (!getScopeState().get('nodesLoaded')) { + getNodes(getScopeState, dispatch); + if (isResourceViewModeSelector(getScopeState())) { + getResourceViewNodesSnapshot(getScopeState(), dispatch); } } }; @@ -549,7 +550,8 @@ export function receiveNodeDetails(details, requestTimestamp) { export function receiveNodesDelta(delta) { return (dispatch, getState) => { - if (!isPausedSelector(getState())) { + const getScopeState = () => getState().scope || getState(); + if (!isPausedSelector(getScopeState())) { // Allow css-animation to run smoothly by scheduling it to run on the // next tick after any potentially expensive canvas re-draws have been // completed. @@ -559,7 +561,7 @@ export function receiveNodesDelta(delta) { // only when the first batch of nodes delta has been received. We // do that because we want to keep the previous state blurred instead // of transitioning over an empty state like when switching topologies. - if (getState().get('timeTravelTransitioning')) { + if (getScopeState().get('timeTravelTransitioning')) { dispatch({ type: ActionTypes.FINISH_TIME_TRAVEL_TRANSITION }); } @@ -576,16 +578,17 @@ export function receiveNodesDelta(delta) { export function resumeTime() { return (dispatch, getState) => { - if (isPausedSelector(getState())) { + const getScopeState = () => getState().scope || getState(); + if (isPausedSelector(getScopeState())) { dispatch({ type: ActionTypes.RESUME_TIME }); - updateRoute(getState); + updateRoute(getScopeState); // After unpausing, all of the following calls will re-activate polling. - getTopologies(getState, dispatch); - getNodes(getState, dispatch, true); - if (isResourceViewModeSelector(getState())) { - getResourceViewNodesSnapshot(getState(), dispatch); + getTopologies(getScopeState, dispatch); + getNodes(getScopeState, dispatch, true); + if (isResourceViewModeSelector(getScopeState())) { + getResourceViewNodesSnapshot(getScopeState(), dispatch); } } }; diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index 7ce06f7f7..20826a6e9 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -44,6 +44,7 @@ import DebugToolbar, { showingDebugToolbar, toggleDebugToolbar } from './debug-t import { getRouter, getUrlState } from '../utils/router-utils'; import { trackAnalyticsEvent } from '../utils/tracking-utils'; import { availableNetworksSelector } from '../selectors/node-networks'; +import { timeTravelSupportedSelector } from '../selectors/time-travel'; import { isResourceViewModeSelector, isTableViewModeSelector, @@ -177,10 +178,10 @@ class App extends React.Component { const { isTableViewMode, isGraphViewMode, isResourceViewMode, showingDetails, showingHelp, showingNetworkSelector, showingTroubleshootingMenu, - timeTravelTransitioning, showingTimeTravel + timeTravelTransitioning, timeTravelSupported } = this.props; - const className = classNames('scope-app', { 'time-travel-open': showingTimeTravel }); + const className = classNames('scope-app', { 'time-travel-open': timeTravelSupported }); const isIframe = window !== window.top; return ( @@ -195,9 +196,11 @@ class App extends React.Component { {showingDetails &&
}
- - - + {timeTravelSupported && ( + + + + )}
@@ -244,11 +247,11 @@ function mapStateToProps(state) { searchQuery: state.get('searchQuery'), showingDetails: state.get('nodeDetails').size > 0, showingHelp: state.get('showingHelp'), - showingTimeTravel: state.get('showingTimeTravel'), showingTroubleshootingMenu: state.get('showingTroubleshootingMenu'), showingNetworkSelector: availableNetworksSelector(state).count() > 0, showingTerminal: state.get('controlPipes').size > 0, topologyViewMode: state.get('topologyViewMode'), + timeTravelSupported: timeTravelSupportedSelector(state), timeTravelTransitioning: state.get('timeTravelTransitioning'), urlState: getUrlState(state) }; diff --git a/client/app/scripts/components/nodes.js b/client/app/scripts/components/nodes.js index e5933858c..88e9f8db6 100644 --- a/client/app/scripts/components/nodes.js +++ b/client/app/scripts/components/nodes.js @@ -86,7 +86,6 @@ function mapStateToProps(state) { topologyNodeCountZero: isTopologyNodeCountZero(state), nodesDisplayEmpty: isNodesDisplayEmpty(state), nodesLoaded: nodesLoadedSelector(state), - timeTravelTransitioning: state.get('timeTravelTransitioning'), currentTopology: state.get('currentTopology'), topologies: state.get('topologies'), topologiesLoaded: state.get('topologiesLoaded'), diff --git a/client/app/scripts/components/time-control.js b/client/app/scripts/components/time-control.js index f835701b8..96a83b653 100644 --- a/client/app/scripts/components/time-control.js +++ b/client/app/scripts/components/time-control.js @@ -4,7 +4,8 @@ import classNames from 'classnames'; import { connect } from 'react-redux'; import { trackAnalyticsEvent } from '../utils/tracking-utils'; -import { pauseTimeAtNow, resumeTime, startTimeTravel } from '../actions/app-actions'; +import { pauseTimeAtNow, resumeTime } from '../actions/app-actions'; +import { isPausedSelector, timeTravelSupportedSelector } from '../selectors/time-travel'; const className = isSelected => ( @@ -17,7 +18,6 @@ class TimeControl extends React.Component { this.handleNowClick = this.handleNowClick.bind(this); this.handlePauseClick = this.handlePauseClick.bind(this); - this.handleTravelClick = this.handleTravelClick.bind(this); this.getTrackingMetadata = this.getTrackingMetadata.bind(this); } @@ -50,71 +50,44 @@ class TimeControl extends React.Component { this.props.pauseTimeAtNow(); } - handleTravelClick() { - if (!this.props.showingTimeTravel) { - trackAnalyticsEvent('scope.time.travel.click', this.getTrackingMetadata({ open: true })); - this.props.startTimeTravel(); - } else { - trackAnalyticsEvent('scope.time.travel.click', this.getTrackingMetadata({ open: false })); - this.props.resumeTime(); - } - } - render() { - const { - showingTimeTravel, pausedAt, timeTravelTransitioning, topologiesLoaded, - hasHistoricReports - } = this.props; + const { isPaused, pausedAt, topologiesLoaded } = this.props; - const isPausedNow = pausedAt && !showingTimeTravel; - const isTimeTravelling = showingTimeTravel; - const isRunningNow = !pausedAt; - - if (!topologiesLoaded) return null; + // If Time Travel is supported, show an empty placeholder div instead + // of this control, since time will be controlled through the timeline. + // We return
instead of null so that selector controls would + // be aligned the same way between WC Explore and Scope standalone. + if (this.props.timeTravelSupported) return
; return (
-
- {timeTravelTransitioning && } -
- {isRunningNow && } + {!isPaused && } Live - {isPausedNow && } - {isPausedNow ? 'Paused' : 'Pause'} + {isPaused && } + {isPaused ? 'Paused' : 'Pause'} - {hasHistoricReports && - - {isTimeTravelling && } - Time Travel - - }
- {(isPausedNow || isTimeTravelling) && + {isPaused && Showing state from {moment(pausedAt).fromNow()} } - {isRunningNow && timeTravelTransitioning && - Resuming the live state - }
); } @@ -122,12 +95,11 @@ class TimeControl extends React.Component { function mapStateToProps(state) { return { - hasHistoricReports: state.getIn(['capabilities', 'historic_reports']), + isPaused: isPausedSelector(state), + timeTravelSupported: timeTravelSupportedSelector(state), topologyViewMode: state.get('topologyViewMode'), topologiesLoaded: state.get('topologiesLoaded'), currentTopology: state.get('currentTopology'), - showingTimeTravel: state.get('showingTimeTravel'), - timeTravelTransitioning: state.get('timeTravelTransitioning'), pausedAt: state.get('pausedAt'), }; } @@ -137,6 +109,5 @@ export default connect( { resumeTime, pauseTimeAtNow, - startTimeTravel, } )(TimeControl); diff --git a/client/app/scripts/components/time-travel-wrapper.js b/client/app/scripts/components/time-travel-wrapper.js index 5795d1dc9..f8c5e687d 100644 --- a/client/app/scripts/components/time-travel-wrapper.js +++ b/client/app/scripts/components/time-travel-wrapper.js @@ -1,28 +1,18 @@ import React from 'react'; import moment from 'moment'; -import styled from 'styled-components'; import { connect } from 'react-redux'; import { TimeTravel } from 'weaveworks-ui-components'; import { trackAnalyticsEvent } from '../utils/tracking-utils'; -import { jumpToTime } from '../actions/app-actions'; +import { jumpToTime, resumeTime, pauseTimeAtNow } from '../actions/app-actions'; -const TimeTravelContainer = styled.div` - transition: all .15s ease-in-out; - position: relative; - overflow: hidden; - height: 0; - - ${props => props.visible && ` - height: 105px; - `} -`; - class TimeTravelWrapper extends React.Component { constructor(props, context) { super(props, context); + this.handleLiveModeChange = this.handleLiveModeChange.bind(this); + this.trackTimestampEdit = this.trackTimestampEdit.bind(this); this.trackTimelinePanButtonClick = this.trackTimelinePanButtonClick.bind(this); this.trackTimelineLabelClick = this.trackTimelineLabelClick.bind(this); @@ -71,20 +61,29 @@ class TimeTravelWrapper extends React.Component { }); } + handleLiveModeChange(showingLive) { + if (showingLive) { + this.props.resumeTime(); + } else { + this.props.pauseTimeAtNow(); + } + } + render() { return ( - - - + ); } } @@ -102,7 +101,7 @@ function mapStateToProps(state, { params }) { } return { - visible: scopeState.get('showingTimeTravel'), + showingLive: !scopeState.get('pausedAt'), topologyViewMode: scopeState.get('topologyViewMode'), currentTopology: scopeState.get('currentTopology'), earliestTimestamp: firstSeenConnectedAt, @@ -112,5 +111,5 @@ function mapStateToProps(state, { params }) { export default connect( mapStateToProps, - { jumpToTime }, + { jumpToTime, resumeTime, pauseTimeAtNow }, )(TimeTravelWrapper); diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js index 8e04ed00d..d9d346506 100644 --- a/client/app/scripts/reducers/root.js +++ b/client/app/scripts/reducers/root.js @@ -75,7 +75,6 @@ export const initialState = makeMap({ selectedNetwork: null, selectedNodeId: null, showingHelp: false, - showingTimeTravel: false, showingTroubleshootingMenu: false, showingNetworks: false, timeTravelTransitioning: false, @@ -369,18 +368,15 @@ export function rootReducer(state = initialState, action) { case ActionTypes.RESUME_TIME: { state = state.set('timeTravelTransitioning', true); - state = state.set('showingTimeTravel', false); return state.set('pausedAt', null); } case ActionTypes.PAUSE_TIME_AT_NOW: { - state = state.set('showingTimeTravel', false); state = state.set('timeTravelTransitioning', false); return state.set('pausedAt', moment().utc().format()); } case ActionTypes.START_TIME_TRAVEL: { - state = state.set('showingTimeTravel', true); state = state.set('timeTravelTransitioning', false); return state.set('pausedAt', action.timestamp || moment().utc().format()); } diff --git a/client/app/scripts/selectors/time-travel.js b/client/app/scripts/selectors/time-travel.js index 40a70772a..c3bfa556a 100644 --- a/client/app/scripts/selectors/time-travel.js +++ b/client/app/scripts/selectors/time-travel.js @@ -7,3 +7,5 @@ export const isPausedSelector = createSelector( ], pausedAt => !!pausedAt ); + +export const timeTravelSupportedSelector = state => state.getIn(['capabilities', 'historic_reports']); diff --git a/client/app/styles/_base.scss b/client/app/styles/_base.scss index 4addf32c2..6d83b7022 100644 --- a/client/app/styles/_base.scss +++ b/client/app/styles/_base.scss @@ -211,6 +211,7 @@ a { .selectors { display: flex; position: relative; + > * { z-index: 20; flex: 1 1;