Skip to content

Commit

Permalink
More polishing.
Browse files Browse the repository at this point in the history
  • Loading branch information
fbarl committed Jun 7, 2017
1 parent dc4855b commit 5c4c4e7
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 148 deletions.
77 changes: 26 additions & 51 deletions client/app/scripts/actions/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,10 @@ export function sortOrderChanged(sortedBy, sortedDesc) {
}

function resetNodesDeltaBuffer() {
clearTimeout(nodesDeltaBufferUpdateTimer);
clearInterval(nodesDeltaBufferUpdateTimer);
return { type: ActionTypes.CLEAR_NODES_DELTA_BUFFER };
}

function bufferDeltaUpdate(delta) {
return (dispatch, getState) => {
if (delta.add === null && delta.update === null && delta.remove === null) {
log('Discarding empty nodes delta');
return;
}

if (getState().get('nodesDeltaBuffer').size >= NODES_DELTA_BUFFER_SIZE_LIMIT) {
dispatch({ type: ActionTypes.CONSOLIDATE_NODES_DELTA_BUFFER });
}

dispatch({
type: ActionTypes.ADD_TO_NODES_DELTA_BUFFER,
delta,
});
log('Buffering node delta, new size', getState().get('nodesDeltaBuffer').size);
};
}

//
// Networks
Expand Down Expand Up @@ -443,23 +425,17 @@ export function clickTopology(topologyId) {
};
}

export function startMovingInTime() {
export function startWebsocketTransition() {
return {
type: ActionTypes.START_MOVING_IN_TIME,
type: ActionTypes.START_WEBSOCKET_TRANSITION,
};
}

export function websocketQueryTimestamp(timestampSinceNow) {
// If the timestamp stands for a time less than one second ago,
// assume we are actually interested in the current time.
if (timestampSinceNow < 1000) {
timestampSinceNow = null;
}

export function websocketQueryInPast(millisecondsInPast) {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.WEBSOCKET_QUERY_TIMESTAMP,
timestampSinceNow,
type: ActionTypes.WEBSOCKET_QUERY_MILLISECONDS_IN_PAST,
millisecondsInPast,
});
updateWebsocketChannel(getState(), dispatch);
dispatch(resetNodesDeltaBuffer());
Expand Down Expand Up @@ -621,12 +597,15 @@ export function receiveNodesDelta(delta) {
setTimeout(() => dispatch({ type: ActionTypes.SET_RECEIVED_NODES_DELTA }), 0);

const state = getState();
const movingInTime = state.get('websocketMovingInTime');
const movingInTime = state.get('websocketTransitioning');
const hasChanges = delta.add || delta.update || delta.remove;

if (hasChanges || movingInTime) {
if (isPausedSelector(state)) {
dispatch(bufferDeltaUpdate(delta));
if (state.get('nodesDeltaBuffer').size >= NODES_DELTA_BUFFER_SIZE_LIMIT) {
dispatch({ type: ActionTypes.CONSOLIDATE_NODES_DELTA_BUFFER });
}
dispatch({ type: ActionTypes.BUFFER_NODES_DELTA, delta });
} else {
dispatch({
type: ActionTypes.RECEIVE_NODES_DELTA,
Expand All @@ -637,32 +616,28 @@ export function receiveNodesDelta(delta) {
};
}

function maybeUpdateFromNodesDeltaBuffer() {
return (dispatch, getState) => {
if (isPausedSelector(getState())) {
dispatch(resetNodesDeltaBuffer());
} else {
if (!getState().get('nodesDeltaBuffer').isEmpty()) {
const delta = getState().get('nodesDeltaBuffer').first();
dispatch({ type: ActionTypes.POP_NODES_DELTA_BUFFER });
dispatch(receiveNodesDelta(delta));
}
if (!getState().get('nodesDeltaBuffer').isEmpty()) {
nodesDeltaBufferUpdateTimer = setTimeout(
() => dispatch(maybeUpdateFromNodesDeltaBuffer()),
NODES_DELTA_BUFFER_FEED_INTERVAL,
);
}
}
};
function updateFromNodesDeltaBuffer(dispatch, state) {
const isPaused = isPausedSelector(state);
const isBufferEmpty = state.get('nodesDeltaBuffer').isEmpty();

if (isPaused || isBufferEmpty) {
dispatch(resetNodesDeltaBuffer());
} else {
dispatch(receiveNodesDelta(state.get('nodesDeltaBuffer').first()));
dispatch({ type: ActionTypes.POP_NODES_DELTA_BUFFER });
}
}

export function clickResumeUpdate() {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.CLICK_RESUME_UPDATE
});
dispatch(maybeUpdateFromNodesDeltaBuffer(getState));
// Periodically merge buffered nodes deltas until the buffer is emptied.
nodesDeltaBufferUpdateTimer = setInterval(
() => updateFromNodesDeltaBuffer(dispatch, getState()),
NODES_DELTA_BUFFER_FEED_INTERVAL,
);
};
}

Expand Down
14 changes: 5 additions & 9 deletions client/app/scripts/components/nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@ const NODES_DISPLAY_EMPTY_CAUSES = [
];

const renderCauses = causes => (
<ul>
{causes.map(cause => (
<li>{cause}</li>
))}
</ul>
<ul>{causes.map(cause => <li>{cause}</li>)}</ul>
);

class Nodes extends React.Component {
Expand All @@ -56,9 +52,9 @@ class Nodes extends React.Component {

render() {
const { topologiesLoaded, nodesLoaded, topologies, currentTopology, isGraphViewMode,
isTableViewMode, isResourceViewMode, movingInTime } = this.props;
isTableViewMode, isResourceViewMode, websocketTransitioning } = this.props;

const className = classNames('nodes-wrapper', { blurred: movingInTime });
const className = classNames('nodes-wrapper', { blurred: websocketTransitioning });

// TODO: Rename view mode components.
return (
Expand Down Expand Up @@ -89,9 +85,9 @@ function mapStateToProps(state) {
topologyNodeCountZero: isTopologyNodeCountZero(state),
nodesDisplayEmpty: isNodesDisplayEmpty(state),
topologyEmpty: isTopologyEmpty(state),
movingInTime: state.get('websocketMovingInTime'),
websocketTransitioning: state.get('websocketTransitioning'),
currentTopology: state.get('currentTopology'),
nodesLoaded: state.get('nodesLoaded') || state.get('websocketMovingInTime'),
nodesLoaded: state.get('nodesLoaded'),
topologies: state.get('topologies'),
topologiesLoaded: state.get('topologiesLoaded'),
};
Expand Down
76 changes: 38 additions & 38 deletions client/app/scripts/components/timeline-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { debounce } from 'lodash';
import PauseButton from './pause-button';
import TopologyTimestampButton from './topology-timestamp-button';
import {
websocketQueryTimestamp,
websocketQueryInPast,
startWebsocketTransition,
clickResumeUpdate,
startMovingInTime,
} from '../actions/app-actions';

import { TIMELINE_DEBOUNCE_INTERVAL } from '../constants/timer';
Expand Down Expand Up @@ -72,62 +72,62 @@ class TimelineControl extends React.Component {
super(props, context);

this.state = {
showTimelinePanel: false,
showSliderPanel: false,
millisecondsInPast: 0,
rangeOptionSelected: sliderRanges.last1Hour,
};

this.jumpToNow = this.jumpToNow.bind(this);
this.toggleTimelinePanel = this.toggleTimelinePanel.bind(this);
this.handleSliderChange = this.handleSliderChange.bind(this);
this.renderRangeOption = this.renderRangeOption.bind(this);
this.handleTimestampClick = this.handleTimestampClick.bind(this);
this.handleJumpToNowClick = this.handleJumpToNowClick.bind(this);
this.handleSliderChange = this.handleSliderChange.bind(this);
this.debouncedUpdateTimestamp = debounce(
this.updateTimestamp.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
}

componentWillUnmount() {
this.updateTimestamp(null);
}

updateTimestamp(timestampSinceNow) {
this.props.websocketQueryTimestamp(timestampSinceNow);
this.props.clickResumeUpdate();
}

toggleTimelinePanel() {
this.setState({ showTimelinePanel: !this.state.showTimelinePanel });
this.updateTimestamp();
}

handleSliderChange(sliderValue) {
const millisecondsInPast = this.getRangeMilliseconds() - sliderValue;
this.props.startMovingInTime();
this.debouncedUpdateTimestamp(millisecondsInPast);
this.setState({ millisecondsInPast });
this.debouncedUpdateTimestamp(millisecondsInPast);
this.props.startWebsocketTransition();
}

handleRangeOptionClick(rangeOption) {
this.setState({ rangeOptionSelected: rangeOption });

const rangeMilliseconds = this.getRangeMilliseconds(rangeOption);
if (this.state.millisecondsInPast > rangeMilliseconds) {
this.updateTimestamp(rangeMilliseconds);
this.setState({ millisecondsInPast: rangeMilliseconds });
this.updateTimestamp(rangeMilliseconds);
this.props.startWebsocketTransition();
}
}

getRangeMilliseconds(rangeOption) {
rangeOption = rangeOption || this.state.rangeOptionSelected;
return moment().diff(rangeOption.getStart());
}

jumpToNow() {
handleJumpToNowClick() {
this.setState({
showTimelinePanel: false,
showSliderPanel: false,
millisecondsInPast: 0,
rangeOptionSelected: sliderRanges.last1Hour,
});
this.props.startMovingInTime();
this.updateTimestamp(null);
this.updateTimestamp();
this.props.startWebsocketTransition();
}

handleTimestampClick() {
this.setState({ showSliderPanel: !this.state.showSliderPanel });
}

updateTimestamp(millisecondsInPast = 0) {
this.props.websocketQueryInPast(millisecondsInPast);
this.props.clickResumeUpdate();
}

getRangeMilliseconds(rangeOption = this.state.rangeOptionSelected) {
return moment().diff(rangeOption.getStart());
}

renderRangeOption(rangeOption) {
Expand All @@ -144,7 +144,7 @@ class TimelineControl extends React.Component {

renderJumpToNowButton() {
return (
<a className="button jump-to-now" title="Jump to now" onClick={this.jumpToNow}>
<a className="button jump-to-now" title="Jump to now" onClick={this.handleJumpToNowClick}>
<span className="fa fa-step-forward" />
</a>
);
Expand All @@ -164,13 +164,13 @@ class TimelineControl extends React.Component {
}

render() {
const { movingInTime } = this.props;
const { showTimelinePanel, millisecondsInPast } = this.state;
const { websocketTransitioning } = this.props;
const { showSliderPanel, millisecondsInPast } = this.state;
const isCurrent = (millisecondsInPast === 0);

return (
<div className="timeline-control">
{showTimelinePanel && <div className="timeline-panel">
{showSliderPanel && <div className="slider-panel">
<span className="caption">Explore</span>
<div className="options">
<div className="column">
Expand All @@ -196,13 +196,13 @@ class TimelineControl extends React.Component {
{this.renderTimelineSlider()}
</div>}
<div className="time-status">
{movingInTime && <div className="timeline-jump-loader">
{websocketTransitioning && <div className="timeline-jump-loader">
<span className="fa fa-circle-o-notch fa-spin" />
</div>}
<TopologyTimestampButton
onClick={this.toggleTimelinePanel}
onClick={this.handleTimestampClick}
millisecondsInPast={millisecondsInPast}
selected={showTimelinePanel}
selected={showSliderPanel}
/>
{!isCurrent && this.renderJumpToNowButton()}
<PauseButton />
Expand All @@ -214,15 +214,15 @@ class TimelineControl extends React.Component {

function mapStateToProps(state) {
return {
movingInTime: state.get('websocketMovingInTime'),
websocketTransitioning: state.get('websocketTransitioning'),
};
}

export default connect(
mapStateToProps,
{
websocketQueryTimestamp,
websocketQueryInPast,
startWebsocketTransition,
clickResumeUpdate,
startMovingInTime,
}
)(TimelineControl);
Loading

0 comments on commit 5c4c4e7

Please sign in to comment.