From fbbd69732e4f8d9b047e2254f4c5a903eabf4d66 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Jul 2022 18:16:02 +0200 Subject: [PATCH] feat(ui) - timeline overview - wip --- .../Session_/BottomBlock/BottomBlock.js | 6 +- .../Session_/OverviewPanel/OverviewPanel.tsx | 116 ++++++++++++++++++ .../components/EventRow/EventRow.tsx | 39 ++++++ .../components/EventRow/index.ts | 1 + .../Session_/OverviewPanel/index.ts | 1 + .../OverviewPanel/overviewPanel.module.css | 13 ++ .../Session_/Player/Controls/Controls.js | 12 ++ .../app/components/Session_/Player/Player.js | 5 + .../app/components/Session_/PlayerBlock.js | 7 +- frontend/app/duck/components/player.js | 1 + frontend/app/utils.ts | 13 +- 11 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx create mode 100644 frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx create mode 100644 frontend/app/components/Session_/OverviewPanel/components/EventRow/index.ts create mode 100644 frontend/app/components/Session_/OverviewPanel/index.ts create mode 100644 frontend/app/components/Session_/OverviewPanel/overviewPanel.module.css diff --git a/frontend/app/components/Session_/BottomBlock/BottomBlock.js b/frontend/app/components/Session_/BottomBlock/BottomBlock.js index 39983c0c1..069757e60 100644 --- a/frontend/app/components/Session_/BottomBlock/BottomBlock.js +++ b/frontend/app/components/Session_/BottomBlock/BottomBlock.js @@ -3,9 +3,9 @@ import cn from 'classnames'; import stl from './bottomBlock.module.css'; const BottomBlock = ({ - children, - className, - additionalHeight, + children = null, + className = '', + additionalHeight = 0, ...props }) => (
diff --git a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx new file mode 100644 index 000000000..d634643b0 --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx @@ -0,0 +1,116 @@ +import { connectPlayer } from 'App/player'; +import React from 'react'; +import BottomBlock from '../BottomBlock'; +import EventRow from './components/EventRow'; +import { TYPES } from 'Types/session/event'; +import { Icon } from 'UI'; +import { Tooltip } from 'react-tippy'; +import stl from './overviewPanel.module.css'; + +interface Props { + resourceList: any[]; + exceptionsList: any[]; + eventsList: any[]; + endTime: number; +} +function OverviewPanel(props: Props) { + const { resourceList, exceptionsList, eventsList, endTime } = props; + const clickRageList = React.useMemo(() => { + return eventsList.filter((item: any) => item.type === TYPES.CLICKRAGE); + }, [eventsList]); + + const containerRef = React.useRef(null); + const innerRef = React.createRef(); + const scale = 100 / endTime; + + let width = 100; + const SPEED = 5; + + const onWheel = (e: React.UIEvent) => { + e.preventDefault(); + e.stopPropagation(); + const delta = e.deltaY; + if (delta > 0) { + width += SPEED; + } else { + width -= SPEED; + } + if (width < 100) { + width = 100; + } + if (innerRef.current) { + innerRef.current.style.width = width + '%'; + if (containerRef.current) { + containerRef.current.style.left = (100 - width) / 2 + '%'; + } + } + }; + + const renderNetworkElement = (item: any) => { + return
; + }; + + const renderClickRageElement = (item: any) => { + return ( +
+ +
+ ); + }; + + const renderExceptionElement = (item: any) => { + // console.log('item', item); + return ( + + {'Exception'} +
+ {item.message} +
+ } + delay={0} + position="top" + > + + + ); + }; + + return ( + + +
+ Overview +
+
+ +
+
+ + +
+ +
+ +
+
+
+
+ ); +} + +export default connectPlayer((state: any) => ({ + resourceList: state.resourceList, + exceptionsList: state.exceptionsList, + eventsList: state.eventList, + endTime: state.endTime, +}))(OverviewPanel); + +const VerticalPointerLine = connectPlayer((state: any) => ({ + time: state.time, + scale: 100 / state.endTime, +}))(({ time, scale }: any) => { + const left = time * scale; + return
; +}); diff --git a/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx b/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx new file mode 100644 index 000000000..b1fec7cec --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import cn from 'classnames' +import { getTimelinePosition } from 'App/utils'; + +interface Props { + list?: any[]; + scale?: number; + title: string; + className?: string; + renderElement?: (item: any) => React.ReactNode; +} +function EventRow(props: Props) { + const { title, className, list = [], scale = 0 } = props; + const _list = React.useMemo(() => { + return list.map((item: any, _index: number) => { + return { + ...item.toJS(), + left: getTimelinePosition(item.time, scale), + } + }) + }, [list]); + return ( +
+
{title}
+
+ {_list.map((item: any, index: number) => { + return ( +
+ {props.renderElement ? props.renderElement(item) : null} +
+ ) + } + )} +
+
+ ); +} + +export default EventRow; \ No newline at end of file diff --git a/frontend/app/components/Session_/OverviewPanel/components/EventRow/index.ts b/frontend/app/components/Session_/OverviewPanel/components/EventRow/index.ts new file mode 100644 index 000000000..ec0281d5a --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/components/EventRow/index.ts @@ -0,0 +1 @@ +export { default } from './EventRow'; \ No newline at end of file diff --git a/frontend/app/components/Session_/OverviewPanel/index.ts b/frontend/app/components/Session_/OverviewPanel/index.ts new file mode 100644 index 000000000..328795cd7 --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/index.ts @@ -0,0 +1 @@ +export { default } from './OverviewPanel'; \ No newline at end of file diff --git a/frontend/app/components/Session_/OverviewPanel/overviewPanel.module.css b/frontend/app/components/Session_/OverviewPanel/overviewPanel.module.css new file mode 100644 index 000000000..979eebb13 --- /dev/null +++ b/frontend/app/components/Session_/OverviewPanel/overviewPanel.module.css @@ -0,0 +1,13 @@ +.popup { + max-width: 300px !important; + /* max-height: 300px !important; */ + overflow: hidden; + text-overflow: ellipsis; + & span { + display: block; + max-height: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} diff --git a/frontend/app/components/Session_/Player/Controls/Controls.js b/frontend/app/components/Session_/Player/Controls/Controls.js index e92099393..0aa33283d 100644 --- a/frontend/app/components/Session_/Player/Controls/Controls.js +++ b/frontend/app/components/Session_/Player/Controls/Controls.js @@ -15,6 +15,7 @@ import { fullscreenOn, fullscreenOff, toggleBottomBlock, + OVERVIEW, CONSOLE, NETWORK, STACKEVENTS, @@ -377,6 +378,17 @@ export default class Controls extends React.Component { containerClassName="mx-2" /> )} */} + toggleBottomTools(OVERVIEW) } + active={ bottomBlock === OVERVIEW && !inspectorMode} + label="OVERVIEW" + noIcon + labelClassName="!text-base font-semibold" + // count={ logCount } + // hasErrors={ logRedCount > 0 } + containerClassName="mx-2" + /> toggleBottomTools(CONSOLE) } diff --git a/frontend/app/components/Session_/Player/Player.js b/frontend/app/components/Session_/Player/Player.js index 4b5006338..6d7089d77 100644 --- a/frontend/app/components/Session_/Player/Player.js +++ b/frontend/app/components/Session_/Player/Player.js @@ -18,6 +18,7 @@ import { EXCEPTIONS, LONGTASKS, INSPECTOR, + OVERVIEW, } from 'Duck/components/player'; import Network from '../Network'; import Console from '../Console/Console'; @@ -40,6 +41,7 @@ import Controls from './Controls'; import Overlay from './Overlay'; import stl from './player.module.css'; import { updateLastPlayedSession } from 'Duck/sessions'; +import OverviewPanel from '../OverviewPanel'; @connectPlayer(state => ({ live: state.live, @@ -104,6 +106,9 @@ export default class Player extends React.PureComponent {
{ !fullscreen && !!bottomBlock &&
+ { //bottomBlock === OVERVIEW && + + } { bottomBlock === CONSOLE && } diff --git a/frontend/app/components/Session_/PlayerBlock.js b/frontend/app/components/Session_/PlayerBlock.js index e6bcbaa33..487809649 100644 --- a/frontend/app/components/Session_/PlayerBlock.js +++ b/frontend/app/components/Session_/PlayerBlock.js @@ -3,7 +3,7 @@ import cn from "classnames"; import { connect } from 'react-redux'; import { } from 'Player'; import { - NONE, + NONE, OVERVIEW, } from 'Duck/components/player'; import Player from './Player'; import SubHeader from './Subheader'; @@ -37,8 +37,9 @@ export default class PlayerBlock extends React.PureComponent { />} diff --git a/frontend/app/duck/components/player.js b/frontend/app/duck/components/player.js index 1a34bcd95..cf6263bf4 100644 --- a/frontend/app/duck/components/player.js +++ b/frontend/app/duck/components/player.js @@ -12,6 +12,7 @@ export const FETCH = 8; export const EXCEPTIONS = 9; export const LONGTASKS = 10; export const INSPECTOR = 11; +export const OVERVIEW = 12; const TOGGLE_FULLSCREEN = 'player/TOGGLE_FS'; const TOGGLE_BOTTOM_BLOCK = 'player/SET_BOTTOM_BLOCK'; diff --git a/frontend/app/utils.ts b/frontend/app/utils.ts index 9765d69c3..53d7375ad 100644 --- a/frontend/app/utils.ts +++ b/frontend/app/utils.ts @@ -324,8 +324,12 @@ export const fetchErrorCheck = async (response: any) => { export const cleanSessionFilters = (data: any) => { const { filters, ...rest } = data; const _fitlers = filters.filter((f: any) => { - if (f.operator === 'isAny' || f.operator === 'onAny') { return true } // ignore filter with isAny/onAny operator - if (Array.isArray(f.filters) && f.filters.length > 0) { return true } // ignore subfilters + if (f.operator === 'isAny' || f.operator === 'onAny') { + return true; + } // ignore filter with isAny/onAny operator + if (Array.isArray(f.filters) && f.filters.length > 0) { + return true; + } // ignore subfilters return f.value !== '' && Array.isArray(f.value) && f.value.length > 0; }); @@ -343,3 +347,8 @@ export const setSessionFilter = (filter: any) => { export const compareJsonObjects = (obj1: any, obj2: any) => { return JSON.stringify(obj1) === JSON.stringify(obj2); }; + +export function getTimelinePosition(value: any, scale: any) { + const pos = value * scale; + return pos > 100 ? 100 : pos; +}