Skip to content

Commit

Permalink
Better request triggers.
Browse files Browse the repository at this point in the history
  • Loading branch information
fbarl committed Jul 25, 2017
1 parent 5544990 commit 3cfe6df
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 90 deletions.
74 changes: 31 additions & 43 deletions client/app/scripts/components/time-travel-timeline.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import React from 'react';
import moment from 'moment';
import classNames from 'classnames';
import { debounce, map, clamp, find, last } from 'lodash';
import { map, clamp, find, last } from 'lodash';
import { connect } from 'react-redux';
import { drag } from 'd3-drag';
import { scaleUtc } from 'd3-scale';
import { event as d3Event, select } from 'd3-selection';

import { nowInSecondsPrecision } from '../utils/time-utils';
import {
jumpToTime,
} from '../actions/app-actions';
nowInSecondsPrecision,
clampToNowInSecondsPrecision,
scaleDuration,
} from '../utils/time-utils';

import {
TIMELINE_TICK_INTERVAL,
TIMELINE_DEBOUNCE_INTERVAL,
} from '../constants/timer';
import { TIMELINE_TICK_INTERVAL } from '../constants/timer';


const TICK_ROWS = {
Expand Down Expand Up @@ -52,17 +50,14 @@ const TICK_ROWS = {
},
};

const FADE_OUT_FACTOR = 1.4;
const ZOOM_SENSITIVITY = 1.0015;
const MIN_TICK_SPACING_PX = 80;
const MAX_TICK_SPACING_PX = 415;
const MIN_DURATION_PER_PX = moment.duration(250, 'milliseconds');
const INIT_DURATION_PER_PX = moment.duration(1, 'minute');
const MAX_DURATION_PER_PX = moment.duration(3, 'days');
const MIN_TICK_SPACING_PX = 80;
const MAX_TICK_SPACING_PX = 415;
const ZOOM_SENSITIVITY = 1.0015;
const FADE_OUT_FACTOR = 1.4;

function scaleDuration(duration, scale) {
return moment.duration(duration.asMilliseconds() * scale);
}

function yFunc(currentDuration, fadedInDuration) {
const durationLog = d => Math.log(d.asMilliseconds());
Expand Down Expand Up @@ -104,18 +99,17 @@ class TimeTravelTimeline extends React.Component {
};

this.saveSvgRef = this.saveSvgRef.bind(this);
this.jumpTo = this.jumpTo.bind(this);
this.jumpRelativePixels = this.jumpRelativePixels.bind(this);
this.jumpForward = this.jumpForward.bind(this);
this.jumpBackward = this.jumpBackward.bind(this);
this.jumpTo = this.jumpTo.bind(this);

this.findOptimalDuration = this.findOptimalDuration.bind(this);

this.handleZoom = this.handleZoom.bind(this);
this.handlePanStart = this.handlePanStart.bind(this);
this.handlePanEnd = this.handlePanEnd.bind(this);
this.handlePan = this.handlePan.bind(this);
this.handleZoom = this.handleZoom.bind(this);

this.debouncedJumpTo = debounce(this.jumpTo.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
}

componentDidMount() {
Expand Down Expand Up @@ -152,14 +146,16 @@ class TimeTravelTimeline extends React.Component {
}

handlePanEnd() {
this.props.onTimelinePanEnd(this.state.focusedTimestamp);
this.setState({ isPanning: false });
}

handlePan() {
const { focusedTimestamp, durationPerPixel } = this.state;
const dragDuration = scaleDuration(durationPerPixel, d3Event.dx);
const newTimestamp = moment(focusedTimestamp).subtract(dragDuration);
this.jumpTo(newTimestamp);
const dragDuration = scaleDuration(this.state.durationPerPixel, -d3Event.dx);
const timestamp = moment(this.state.focusedTimestamp).add(dragDuration);
const focusedTimestamp = clampToNowInSecondsPrecision(timestamp);
this.props.onTimelinePan(focusedTimestamp);
this.setState({ focusedTimestamp });
}

handleZoom(e) {
Expand All @@ -171,25 +167,23 @@ class TimeTravelTimeline extends React.Component {
}

jumpTo(timestamp) {
const { timestampNow } = this.state;
const focusedTimestamp = timestamp > timestampNow ? timestampNow : timestamp;
this.props.onTimelinePan(focusedTimestamp);
const focusedTimestamp = clampToNowInSecondsPrecision(timestamp);
this.props.onInstantJump(focusedTimestamp);
this.setState({ focusedTimestamp });
this.debouncedJumpTo.cancel();
}

jumpRelativePixels(pixels) {
const duration = scaleDuration(this.state.durationPerPixel, pixels);
const timestamp = moment(this.state.focusedTimestamp).add(duration);
this.jumpTo(timestamp);
}

jumpForward() {
const { focusedTimestamp, durationPerPixel, boundingRect } = this.state;
const duration = scaleDuration(durationPerPixel, boundingRect.width / 4);
const newTimestamp = moment(focusedTimestamp).add(duration);
this.jumpTo(newTimestamp);
this.jumpRelativePixels(this.state.boundingRect.width / 4);
}

jumpBackward() {
const { focusedTimestamp, durationPerPixel, boundingRect } = this.state;
const duration = scaleDuration(durationPerPixel, boundingRect.width / 4);
const newTimestamp = moment(focusedTimestamp).subtract(duration);
this.jumpTo(newTimestamp);
this.jumpRelativePixels(-this.state.boundingRect.width / 4);
}

getTimeScale() {
Expand Down Expand Up @@ -325,7 +319,7 @@ class TimeTravelTimeline extends React.Component {
}

render() {
const className = classNames({ dragging: this.state.isPanning });
const className = classNames({ panning: this.state.isPanning });
const halfWidth = this.state.boundingRect.width / 2;

return (
Expand Down Expand Up @@ -354,10 +348,4 @@ function mapStateToProps(state) {
};
}


export default connect(
mapStateToProps,
{
jumpToTime,
}
)(TimeTravelTimeline);
export default connect(mapStateToProps)(TimeTravelTimeline);
93 changes: 51 additions & 42 deletions client/app/scripts/components/time-travel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ import React from 'react';
import moment from 'moment';
import classNames from 'classnames';
import { connect } from 'react-redux';
import { debounce, map } from 'lodash';
import { debounce } from 'lodash';

import TimeTravelTimeline from './time-travel-timeline';
import { trackMixpanelEvent } from '../utils/tracking-utils';
import { clampToNowInSecondsPrecision } from '../utils/time-utils';
import {
jumpToTime,
resumeTime,
timeTravelStartTransition,
} from '../actions/app-actions';

import {
TIMELINE_DEBOUNCE_INTERVAL,
} from '../constants/timer';
import { TIMELINE_DEBOUNCE_INTERVAL } from '../constants/timer';


const getTimestampStates = (timestamp) => {
Expand All @@ -33,13 +32,16 @@ class TimeTravel extends React.Component {

this.handleInputChange = this.handleInputChange.bind(this);
this.handleTimelinePan = this.handleTimelinePan.bind(this);
this.handleJumpClick = this.handleJumpClick.bind(this);
this.travelTo = this.travelTo.bind(this);
this.handleTimelinePanEnd = this.handleTimelinePanEnd.bind(this);
this.handleInstantJump = this.handleInstantJump.bind(this);

this.trackTimestampEdit = this.trackTimestampEdit.bind(this);
this.trackTimelineClick = this.trackTimelineClick.bind(this);
this.trackTimelinePan = this.trackTimelinePan.bind(this);

this.instantUpdateTimestamp = this.instantUpdateTimestamp.bind(this);
this.debouncedUpdateTimestamp = debounce(
this.updateTimestamp.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
this.debouncedTrackSliderChange = debounce(
this.trackSliderChange.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
this.instantUpdateTimestamp.bind(this), TIMELINE_DEBOUNCE_INTERVAL);
}

componentWillReceiveProps(props) {
Expand All @@ -51,53 +53,59 @@ class TimeTravel extends React.Component {
this.props.resumeTime();
}

handleSliderChange(timestamp) {
if (!timestamp.isSame(this.props.pausedAt)) {
this.travelTo(timestamp, true);
this.debouncedTrackSliderChange();
}
}

handleInputChange(ev) {
let timestamp = moment(ev.target.value);
const timestamp = moment(ev.target.value);
this.setState({ inputValue: ev.target.value });

if (timestamp.isValid()) {
timestamp = Math.min(timestamp, moment().valueOf());
this.travelTo(timestamp);

trackMixpanelEvent('scope.time.timestamp.edit', {
layout: this.props.topologyViewMode,
topologyId: this.props.currentTopology.get('id'),
parentTopologyId: this.props.currentTopology.get('parentId'),
});
const clampedTimestamp = clampToNowInSecondsPrecision(timestamp);
this.instantUpdateTimestamp(clampedTimestamp, this.trackTimestampEdit);
}
}

// TODO: Redo
handleJumpClick(millisecondsDelta) {
let timestamp = this.state.sliderValue + millisecondsDelta;
timestamp = Math.min(timestamp, moment().valueOf());
this.travelTo(timestamp, true);
handleTimelinePan(timestamp) {
this.setState(getTimestampStates(timestamp));
this.debouncedUpdateTimestamp(timestamp);
}

updateTimestamp(timestamp) {
this.props.jumpToTime(moment(timestamp));
handleTimelinePanEnd(timestamp) {
this.instantUpdateTimestamp(timestamp, this.trackTimelinePan);
}

travelTo(timestamp, debounced = false) {
this.props.timeTravelStartTransition();
this.setState(getTimestampStates(timestamp));
if (debounced) {
this.debouncedUpdateTimestamp(timestamp);
} else {
handleInstantJump(timestamp) {
this.instantUpdateTimestamp(timestamp, this.trackTimelineClick);
}

instantUpdateTimestamp(timestamp, callback) {
if (!timestamp.isSame(this.props.pausedAt)) {
this.debouncedUpdateTimestamp.cancel();
this.updateTimestamp(timestamp);
this.setState(getTimestampStates(timestamp));
this.props.timeTravelStartTransition();
this.props.jumpToTime(moment(timestamp));

// Used for tracking.
if (callback) callback();
}
}

trackSliderChange() {
trackMixpanelEvent('scope.time.slider.change', {
trackTimestampEdit() {
trackMixpanelEvent('scope.time.timestamp.edit', {
layout: this.props.topologyViewMode,
topologyId: this.props.currentTopology.get('id'),
parentTopologyId: this.props.currentTopology.get('parentId'),
});
}

trackTimelineClick() {
trackMixpanelEvent('scope.time.timeline.click', {
layout: this.props.topologyViewMode,
topologyId: this.props.currentTopology.get('id'),
parentTopologyId: this.props.currentTopology.get('parentId'),
});
}

trackTimelinePan() {
trackMixpanelEvent('scope.time.timeline.pan', {
layout: this.props.topologyViewMode,
topologyId: this.props.currentTopology.get('id'),
parentTopologyId: this.props.currentTopology.get('parentId'),
Expand All @@ -109,7 +117,8 @@ class TimeTravel extends React.Component {
<div className={classNames('time-travel', { visible: this.props.showingTimeTravel })}>
<TimeTravelTimeline
onTimelinePan={this.handleTimelinePan}
onJumpClick={this.handleJumpClick}
onTimelinePanEnd={this.handleTimelinePanEnd}
onInstantJump={this.handleInstantJump}
/>
<div className="time-travel-timestamp">
<input
Expand Down
18 changes: 18 additions & 0 deletions client/app/scripts/utils/__tests__/time-utils-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import expect from 'expect';
import { timer } from '../time-utils';

describe('timer', () => {
it('records how long a function takes to execute', () => {
const add100k = (number) => {
for (let i = 0; i < 100000; i += 1) {
number += 1;
}
return number;
};

const timedFn = timer(add100k);
const result = timedFn(70);
expect(result).toEqual(100070);
expect(timedFn.time).toBeA('number');
});
});
26 changes: 26 additions & 0 deletions client/app/scripts/utils/time-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import moment from 'moment';

// Replacement for timely dependency
export function timer(fn) {
const timedFn = (...args) => {
const start = new Date();
const result = fn.apply(fn, args);
timedFn.time = new Date() - start;
return result;
};
return timedFn;
}

export function nowInSecondsPrecision() {
return moment().startOf('second');
}

export function clampToNowInSecondsPrecision(timestamp) {
const now = nowInSecondsPrecision();
return timestamp.isAfter(now) ? now : timestamp;
}

// This is unfortunately not there in moment.js
export function scaleDuration(duration, scale) {
return moment.duration(duration.asMilliseconds() * scale);
}
3 changes: 2 additions & 1 deletion client/app/scripts/utils/tracking-utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import debug from 'debug';

const log = debug('service:tracking');
const log = debug('scope:tracking');

// Track mixpanel events only if Scope is running inside of Weave Cloud.
export function trackMixpanelEvent(name, props) {
log('trackMixpanelEvent', name, props);
if (window.mixpanel && process.env.WEAVE_CLOUD) {
window.mixpanel.track(name, props);
} else {
Expand Down
6 changes: 2 additions & 4 deletions client/app/styles/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,6 @@
}

.time-travel {
// align-items: center;
// display: flex;
position: relative;
margin-bottom: 15px;
z-index: 2001;
Expand Down Expand Up @@ -290,7 +288,7 @@
width: 100%;
height: 100%;

&.dragging { @extend .grabbing; }
&.panning { @extend .grabbing; }

.available-range {
fill: #888;
Expand Down Expand Up @@ -318,6 +316,7 @@
background-color: red;
height: 70px;
width: 3px;
margin-left: -1px;
}
}

Expand Down Expand Up @@ -346,7 +345,6 @@
background-color: #ccc;
height: 9px;
width: 1px;
left: 1px
}
}
}
Expand Down

0 comments on commit 3cfe6df

Please sign in to comment.