import React from 'react'; import { connect } from 'react-redux'; import cn from 'classnames'; import { connectPlayer, Controls } from 'Player'; import { TimelinePointer, Icon } from 'UI'; import TimeTracker from './TimeTracker'; import stl from './timeline.module.css'; import { TYPES } from 'Types/session/event'; import { setTimelinePointer } from 'Duck/sessions'; import DraggableCircle from './DraggableCircle'; import CustomDragLayer from './CustomDragLayer'; import { debounce } from 'App/utils'; import { Tooltip } from 'react-tippy'; const BOUNDRY = 15 const getPointerIcon = (type) => { // exception, switch(type) { case 'fetch': return 'funnel/file-earmark-minus-fill'; case 'exception': return 'funnel/exclamation-circle-fill'; case 'log': return 'funnel/exclamation-circle-fill'; case 'stack': return 'funnel/patch-exclamation-fill'; case 'resource': return 'funnel/file-earmark-minus-fill'; case 'dead_click': return 'funnel/dizzy'; case 'click_rage': return 'funnel/dizzy'; case 'excessive_scrolling': return 'funnel/mouse'; case 'bad_request': return 'funnel/file-medical-alt'; case 'missing_resource': return 'funnel/file-earmark-minus-fill'; case 'memory': return 'funnel/sd-card'; case 'cpu': return 'funnel/microchip'; case 'slow_resource': return 'funnel/hourglass-top'; case 'slow_page_load': return 'funnel/hourglass-top'; case 'crash': return 'funnel/file-exclamation'; case 'js_exception': return 'funnel/exclamation-circle-fill'; } return 'info'; } let deboucneJump = () => null; @connectPlayer(state => ({ playing: state.playing, time: state.time, skipIntervals: state.skipIntervals, events: state.eventList, skip: state.skip, // not updating properly rn // skipToIssue: state.skipToIssue, disabled: state.cssLoading || state.messagesLoading || state.markedTargets, endTime: state.endTime, live: state.live, logList: state.logList, exceptionsList: state.exceptionsList, resourceList: state.resourceList, stackList: state.stackList, fetchList: state.fetchList, })) @connect(state => ({ issues: state.getIn([ 'sessions', 'current', 'issues' ]), clickRageTime: state.getIn([ 'sessions', 'current', 'clickRage' ]) && state.getIn([ 'sessions', 'current', 'clickRageTime' ]), returningLocationTime: state.getIn([ 'sessions', 'current', 'returningLocation' ]) && state.getIn([ 'sessions', 'current', 'returningLocationTime' ]), }), { setTimelinePointer }) export default class Timeline extends React.PureComponent { progressRef = React.createRef() wasPlaying = false seekProgress = (e) => { const { endTime } = this.props; const p = e.nativeEvent.offsetX / e.target.offsetWidth; const time = Math.max(Math.round(p * endTime), 0); console.log(p, time, e, endTime) this.props.jump(time); } createEventClickHandler = pointer => (e) => { e.stopPropagation(); this.props.jump(pointer.time); this.props.setTimelinePointer(pointer); } componentDidMount() { const { issues } = this.props; const skipToIssue = Controls.updateSkipToIssue(); const firstIssue = issues.get(0); deboucneJump = debounce(this.props.jump, 500); if (firstIssue && skipToIssue) { this.props.jump(firstIssue.time); } } onDragEnd = () => { if (this.wasPlaying) { this.props.togglePlay(); } } onDrag = (offset) => { const { endTime } = this.props; const p = (offset.x - BOUNDRY) / this.progressRef.current.offsetWidth; const time = Math.max(Math.round(p * endTime), 0); deboucneJump(time); if (this.props.playing) { this.wasPlaying = true; this.props.pause(); } } render() { const { events, skip, skipIntervals, disabled, endTime, live, logList, exceptionsList, resourceList, clickRageTime, stackList, fetchList, issues, } = this.props; const scale = 100 / endTime; return (
{ skip && skipIntervals.map(interval => (
)) }
{ events.map(e => (
)) } { issues.map(iss => (
{ iss.name }
} >
)) } { events.filter(e => e.type === TYPES.CLICKRAGE).map(e => (
{ "Click Rage" }
} >
))} {typeof clickRageTime === 'number' &&
{ "Click Rage" }
} >
} { exceptionsList .map(e => (
{ "Exception" }
{ e.message }
} >
)) } { resourceList .filter(r => r.isRed() || r.isYellow()) .map(r => (
{ r.success ? "Slow resource: " : "Missing resource:" }
{ r.name }
} >
)) } { fetchList .filter(e => e.isRed()) .map(e => (
{ "Failed Fetch" }
{ e.name }
} /> )) } { stackList .filter(e => e.isRed()) .map(e => (
{ "Stack Event" }
{ e.name }
} /> )) } ); } }