From f3bf7be8cc6ecc01d45707c6976337af247e6cd0 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 10 Aug 2022 18:18:06 +0200 Subject: [PATCH] feat(ui) - overview - events and errors wip --- .../Session_/OverviewPanel/OverviewPanel.tsx | 30 +- .../components/EventRow/EventRow.tsx | 10 +- .../FeatureSelection/FeatureSelection.tsx | 2 +- .../StackEventModal/StackEventModal.tsx | 14 + .../components/StackEventModal/index.ts | 1 + .../TimelinePointer/TimelinePointer.tsx | 61 ++- .../Session_/Player/Controls/Timeline.js | 420 +++++++++--------- 7 files changed, 304 insertions(+), 234 deletions(-) create mode 100644 frontend/app/components/Session_/OverviewPanel/components/StackEventModal/StackEventModal.tsx create mode 100644 frontend/app/components/Session_/OverviewPanel/components/StackEventModal/index.ts diff --git a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx index 7d1ee0165..53ba3ddf5 100644 --- a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx +++ b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx @@ -1,5 +1,5 @@ import { connectPlayer } from 'App/player'; -import { toggleBottomBlock, NETWORK, EXCEPTIONS } from 'Duck/components/player'; +import { toggleBottomBlock } from 'Duck/components/player'; import React from 'react'; import BottomBlock from '../BottomBlock'; import EventRow from './components/EventRow'; @@ -15,16 +15,25 @@ interface Props { resourceList: any[]; exceptionsList: any[]; eventsList: any[]; - endTime: number; toggleBottomBlock: any; + stackEventList: any[]; + issuesList: any[]; } function OverviewPanel(props: Props) { - const { resourceList, exceptionsList, eventsList, endTime } = props; + const { resourceList, exceptionsList, eventsList, stackEventList, issuesList } = props; const clickRageList = React.useMemo(() => { return eventsList.filter((item: any) => item.type === TYPES.CLICKRAGE); }, [eventsList]); - const scale = 100 / endTime; - const [selectedFeatures, setSelectedFeatures] = React.useState(['NETWORK', 'ERRORS', 'EVENTS']); + // const scale = 100 / endTime; + const [selectedFeatures, setSelectedFeatures] = React.useState(['PERFORMANCE', 'ERRORS', 'EVENTS']); + + const resources: any = { + NETWORK: resourceList, + ERRORS: exceptionsList, + EVENTS: stackEventList, + CLICKRAGE: clickRageList, + PERFORMANCE: issuesList, + }; return ( @@ -40,12 +49,11 @@ function OverviewPanel(props: Props) {
{selectedFeatures.map((feature: any, index: number) => ( -
+
} />
@@ -57,13 +65,15 @@ function OverviewPanel(props: Props) { ); } -export default connect(null, { +export default connect((state: any) => ({ + issuesList: state.getIn(['sessions', 'current', 'issues']), +}), { toggleBottomBlock, })( connectPlayer((state: any) => ({ resourceList: state.resourceList.filter((r: any) => r.isRed() || r.isYellow()), exceptionsList: state.exceptionsList, eventsList: state.eventList, - endTime: state.endTime, + stackEventList: state.stackList, }))(OverviewPanel) ); diff --git a/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx b/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx index f06003463..0447fcaa9 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx @@ -1,16 +1,18 @@ import React from 'react'; import cn from 'classnames' import { getTimelinePosition } from 'App/utils'; +import { connectPlayer } from 'App/player'; interface Props { list?: any[]; - scale?: number; title: string; className?: string; + endTime?: number; renderElement?: (item: any) => React.ReactNode; } function EventRow(props: Props) { - const { title, className, list = [], scale = 0 } = props; + const { title, className, list = [], endTime = 0 } = props; + const scale = 100 / endTime; const _list = React.useMemo(() => { return list.map((item: any, _index: number) => { return { @@ -36,4 +38,6 @@ function EventRow(props: Props) { ); } -export default EventRow; \ No newline at end of file +export default connectPlayer((state: any) => ({ + endTime: state.endTime, +}))(EventRow); \ No newline at end of file diff --git a/frontend/app/components/Session_/OverviewPanel/components/FeatureSelection/FeatureSelection.tsx b/frontend/app/components/Session_/OverviewPanel/components/FeatureSelection/FeatureSelection.tsx index 77a498785..1f74d20ab 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/FeatureSelection/FeatureSelection.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/FeatureSelection/FeatureSelection.tsx @@ -4,7 +4,7 @@ import { Checkbox } from 'UI'; const NETWORK = 'NETWORK'; const ERRORS = 'ERRORS'; const EVENTS = 'EVENTS'; -const CLICKRAGE = 'CLICK RAGE'; +const CLICKRAGE = 'CLICKRAGE'; const PERFORMANCE = 'PERFORMANCE'; interface Props { diff --git a/frontend/app/components/Session_/OverviewPanel/components/StackEventModal/StackEventModal.tsx b/frontend/app/components/Session_/OverviewPanel/components/StackEventModal/StackEventModal.tsx new file mode 100644 index 000000000..07d163dbe --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/components/StackEventModal/StackEventModal.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +interface Props { + event: any; +} +function StackEventModal(props: Props) { + return ( +
+ Content +
+ ); +} + +export default StackEventModal; diff --git a/frontend/app/components/Session_/OverviewPanel/components/StackEventModal/index.ts b/frontend/app/components/Session_/OverviewPanel/components/StackEventModal/index.ts new file mode 100644 index 000000000..93a084d28 --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/components/StackEventModal/index.ts @@ -0,0 +1 @@ +export { default } from './StackEventModal'; diff --git a/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx b/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx index b74cd068d..a9d58f4a2 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx @@ -1,17 +1,18 @@ import React from 'react'; import { connectPlayer, Controls } from 'App/player'; -import { toggleBottomBlock, NETWORK, EXCEPTIONS } from 'Duck/components/player'; +import { toggleBottomBlock, NETWORK, EXCEPTIONS, PERFORMANCE } from 'Duck/components/player'; import { useModal } from 'App/components/Modal'; import { Icon, ErrorDetails } from 'UI'; import { Tooltip } from 'react-tippy'; import { TYPES as EVENT_TYPES } from 'Types/session/event'; +import StackEventModal from '../StackEventModal'; interface Props { pointer: any; type: any; } function TimelinePointer(props: Props) { - const { showModal } = useModal(); + const { showModal, hideModal } = useModal(); const createEventClickHandler = (pointer: any, type: any) => (e: any) => { e.stopPropagation(); Controls.jump(pointer.time); @@ -19,9 +20,13 @@ function TimelinePointer(props: Props) { return; } - if (type === EXCEPTIONS) { + if (type === 'EXCEPTIONS') { showModal(, { right: true }); } + + if (type === 'EVENT') { + showModal(, { right: true }); + } // props.toggleBottomBlock(type); }; @@ -63,6 +68,43 @@ function TimelinePointer(props: Props) { ); }; + const renderStackEventElement = (item: any) => { + return ( + + {'Stack Event'} +
+ } + delay={0} + position="top" + > +
+ {/* */} +
+ + ); + }; + + const renderPerformanceElement = (item: any) => { + console.log('item', item) + return ( + + {item.name} +
+ } + delay={0} + position="top" + > +
+ {/* */} +
+ + ); + }; + const renderExceptionElement = (item: any) => { return ( { const { pointer, type } = props; - if (type === NETWORK) { + if (type === 'NETWORK') { return renderNetworkElement(pointer); } - if (type === EVENT_TYPES.CLICKRAGE) { + if (type === 'CLICKRAGE') { return renderClickRageElement(pointer); } - if (type === EXCEPTIONS) { + if (type === 'ERRORS') { return renderExceptionElement(pointer); } + if (type === 'EVENTS') { + return renderStackEventElement(pointer); + } + + if (type === 'PERFORMANCE') { + return renderPerformanceElement(pointer); + } }; return
{render()}
; } diff --git a/frontend/app/components/Session_/Player/Controls/Timeline.js b/frontend/app/components/Session_/Player/Controls/Timeline.js index 3acdb4c11..91603fbf5 100644 --- a/frontend/app/components/Session_/Player/Controls/Timeline.js +++ b/frontend/app/components/Session_/Player/Controls/Timeline.js @@ -12,206 +12,199 @@ import CustomDragLayer from './CustomDragLayer'; import { debounce } from 'App/utils'; import { Tooltip } from 'react-tippy'; -const BOUNDRY = 15 +const BOUNDRY = 15; function getTimelinePosition(value, scale) { - const pos = value * scale; + const pos = value * scale; - return pos > 100 ? 100 : pos; + return pos > 100 ? 100 : pos; } 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'; + // 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'; -} + 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, +@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 }) +@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 + 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); - this.props.jump(time); - } + seekProgress = (e) => { + const { endTime } = this.props; + const p = e.nativeEvent.offsetX / e.target.offsetWidth; + const time = Math.max(Math.round(p * endTime), 0); + this.props.jump(time); + }; - createEventClickHandler = pointer => (e) => { - e.stopPropagation(); - this.props.jump(pointer.time); - this.props.setTimelinePointer(pointer); - } + 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); + 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); + if (firstIssue && skipToIssue) { + this.props.jump(firstIssue.time); + } } - } - onDragEnd = () => { - if (this.wasPlaying) { - this.props.togglePlay(); - } - } + onDragEnd = () => { + if (this.wasPlaying) { + this.props.togglePlay(); + } + }; - onDrag = (offset) => { - const { endTime } = this.props; + 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(); - } - } + 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; + render() { + const { + events, + skip, + skipIntervals, + disabled, + endTime, + live, + logList, + exceptionsList, + resourceList, + clickRageTime, + stackList, + fetchList, + issues, + } = this.props; - const scale = 100 / endTime; + 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 => ( + return ( +
+
+ + + + {skip && + skipIntervals.map((interval) => ( +
+ ))} +
+ {/* {events.map((e) => ( +
+ ))} */} + {/* {issues.map((iss) => ( +
+ + {iss.name} +
+ } + > + + +
+ ))} */} + {/* { events.filter(e => e.type === TYPES.CLICKRAGE).map(e => (
- ))} - {typeof clickRageTime === 'number' && + ))} */} + {/* {typeof clickRageTime === 'number' &&
- } - { exceptionsList + } */} + {/* { exceptionsList .map(e => (
)) - } - { resourceList + } */} + {/* { resourceList .filter(r => r.isRed() || r.isYellow()) .map(r => (
)) - } - { fetchList + } */} + {/* { fetchList .filter(e => e.isRed()) .map(e => (
)) - } - { stackList - .filter(e => e.isRed()) - .map(e => ( -
- - Stack Event -
- { e.name } -
- } - > - - + } */} + {/* {stackList + .filter((e) => e.isRed()) + .map((e) => ( +
+ + Stack Event +
+ {e.name} +
+ } + > + + +
+ ))} */}
- )) - } -
-
- ); - } +
+ ); + } }