From a2a956b4d454ae3cf74e2a7cd35e80ce96dba02d Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 22 Nov 2022 19:10:17 +0100 Subject: [PATCH] change(ui) - wip --- .../DevTools/BottomBlock/BottomBlock.js | 24 +- .../DevTools/ConsolePanel/ConsolePanel.tsx | 1 - .../shared/DevTools/JumpButton/JumpButton.tsx | 4 +- .../DevTools/NetworkPanel/NetworkPanel.tsx | 208 ++++++++++-------- .../shared/DevTools/TimeTable/TimeTable.tsx | 73 +++--- frontend/app/mstore/sessionStore.ts | 140 +++++++----- .../MessageDistributor/MessageDistributor.ts | 1 - frontend/app/styles/general.css | 4 + frontend/package.json | 1 + 9 files changed, 267 insertions(+), 189 deletions(-) diff --git a/frontend/app/components/shared/DevTools/BottomBlock/BottomBlock.js b/frontend/app/components/shared/DevTools/BottomBlock/BottomBlock.js index 069757e60..8b7826755 100644 --- a/frontend/app/components/shared/DevTools/BottomBlock/BottomBlock.js +++ b/frontend/app/components/shared/DevTools/BottomBlock/BottomBlock.js @@ -1,17 +1,29 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import cn from 'classnames'; import stl from './bottomBlock.module.css'; +let timer = null; const BottomBlock = ({ children = null, className = '', additionalHeight = 0, + onMouseEnter = () => {}, + onMouseLeave = () => {}, ...props -}) => ( -
- { children } -
-); +}) => { + useEffect(() => {}, []); + + return ( +
+ {children} +
+ ); +}; BottomBlock.displayName = 'BottomBlock'; diff --git a/frontend/app/components/shared/DevTools/ConsolePanel/ConsolePanel.tsx b/frontend/app/components/shared/DevTools/ConsolePanel/ConsolePanel.tsx index 320f76341..8f5835cfa 100644 --- a/frontend/app/components/shared/DevTools/ConsolePanel/ConsolePanel.tsx +++ b/frontend/app/components/shared/DevTools/ConsolePanel/ConsolePanel.tsx @@ -4,7 +4,6 @@ import Log from 'Types/session/log'; import BottomBlock from '../BottomBlock'; import { LEVEL } from 'Types/session/log'; import { Tabs, Input, Icon, NoContent } from 'UI'; -// import Autoscroll from 'App/components/Session_/Autoscroll'; import cn from 'classnames'; import ConsoleRow from '../ConsoleRow'; import { getRE } from 'App/utils'; diff --git a/frontend/app/components/shared/DevTools/JumpButton/JumpButton.tsx b/frontend/app/components/shared/DevTools/JumpButton/JumpButton.tsx index c52b0cffd..31307fd9b 100644 --- a/frontend/app/components/shared/DevTools/JumpButton/JumpButton.tsx +++ b/frontend/app/components/shared/DevTools/JumpButton/JumpButton.tsx @@ -6,10 +6,10 @@ interface Props { tooltip?: string; } function JumpButton(props: Props) { - const { tooltip = '' } = props; + const { tooltip } = props; return (
- +
{ diff --git a/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx b/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx index e62bf25ff..8c087d868 100644 --- a/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx +++ b/frontend/app/components/shared/DevTools/NetworkPanel/NetworkPanel.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import { QuestionMarkHint, Tooltip, Tabs, Input, NoContent, Icon, Toggler, Button } from 'UI'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { Tooltip, Tabs, Input, NoContent, Icon, Toggler } from 'UI'; import { getRE } from 'App/utils'; import Resource, { TYPES } from 'Types/session/resource'; import { formatBytes } from 'App/utils'; @@ -12,6 +12,11 @@ import { Duration } from 'luxon'; import { connectPlayer, jump } from 'Player'; import { useModal } from 'App/components/Modal'; import FetchDetailsModal from 'Shared/FetchDetailsModal'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; + +const INDEX_KEY = 'networkIndex'; +const INDEX_KEY_ACTIVE = 'networkActive'; const ALL = 'ALL'; const XHR = 'xhr'; @@ -67,37 +72,6 @@ export function renderStart(r: any) { ); } -// const renderXHRText = () => ( -// -// {XHR} -// -// Use our{' '} -// -// Fetch plugin -// -// {' to capture HTTP requests and responses, including status codes and bodies.'}
-// We also provide{' '} -// -// support for GraphQL -// -// {' for easy debugging of your queries.'} -// -// } -// className="ml-1" -// /> -//
-// ); - function renderSize(r: any) { if (r.responseBodySize) return formatBytes(r.responseBodySize); let triggerText; @@ -160,45 +134,76 @@ interface Props { loadTime: any; playing: boolean; domBuildingTime: any; - currentIndex: any; time: any; } function NetworkPanel(props: Props) { - const { - resources, - time, - currentIndex, - domContentLoadedTime, - loadTime, - playing, - domBuildingTime, - fetchList, - } = props; - const { showModal, hideModal } = useModal(); + const { resources, time, domContentLoadedTime, loadTime, domBuildingTime, fetchList } = props; + const { showModal } = useModal(); const [activeTab, setActiveTab] = useState(ALL); const [sortBy, setSortBy] = useState('time'); const [sortAscending, setSortAscending] = useState(true); const [filter, setFilter] = useState(''); + const [filteredList, setFilteredList] = useState([]); const [showOnlyErrors, setShowOnlyErrors] = useState(false); - const [activeRequest, setActiveRequest] = useState(false ) const onTabClick = (activeTab: any) => setActiveTab(activeTab); const onFilterChange = ({ target: { value } }: any) => setFilter(value); const additionalHeight = 0; const fetchPresented = fetchList.length > 0; + const { + sessionStore: { devTools }, + } = useStore(); - const resourcesSize = resources.reduce( - (sum: any, { decodedBodySize }: any) => sum + (decodedBodySize || 0), - 0 - ); + const activeIndex = useObserver(() => devTools[INDEX_KEY]); + const activeClick = useObserver(() => devTools[INDEX_KEY_ACTIVE]); + const [pauseSync, setPauseSync] = useState(!!activeClick); + const synRef: any = useRef({}); - const transferredSize = resources.reduce( - (sum: any, { headerSize, encodedBodySize }: any) => - sum + (headerSize || 0) + (encodedBodySize || 0), - 0 - ); + synRef.current = { + pauseSync, + activeIndex, + activeClick, + }; - const filterRE = getRE(filter, 'i'); - let filtered = React.useMemo(() => { + useEffect(() => { + if (!!activeClick) { + setPauseSync(true); + devTools.update(INDEX_KEY, activeClick); + console.log('mounting at: ', activeClick); + } + return () => { + if (synRef.current.pauseSync) { + console.log('unmouting at: ', synRef.current.activeIndex); + devTools.update(INDEX_KEY_ACTIVE, synRef.current.activeIndex); + } + }; + }, []); + + useEffect(() => { + const lastIndex = filteredList.filter((item: any) => item.time <= time).length - 1; + if (lastIndex !== activeIndex && !pauseSync) { + devTools.update(INDEX_KEY, lastIndex); + } + }, [time]); + + const { resourcesSize, transferredSize } = useMemo(() => { + const resourcesSize = resources.reduce( + (sum: any, { decodedBodySize }: any) => sum + (decodedBodySize || 0), + 0 + ); + + const transferredSize = resources.reduce( + (sum: any, { headerSize, encodedBodySize }: any) => + sum + (headerSize || 0) + (encodedBodySize || 0), + 0 + ); + return { + resourcesSize, + transferredSize, + }; + }, [resources]); + + useEffect(() => { + const filterRE = getRE(filter, 'i'); let list = resources; fetchList.forEach( (fetchCall: any) => @@ -209,9 +214,9 @@ function NetworkPanel(props: Props) { return compare(a, b, sortBy); }); - if (!sortAscending) { - list = list.reverse(); - } + // if (!sortAscending) { + // list = list.reverse(); + // } list = list.filter( ({ type, name, status, success }: any) => @@ -219,41 +224,53 @@ function NetworkPanel(props: Props) { (activeTab === ALL || type === TAB_TO_TYPE_MAP[activeTab]) && (showOnlyErrors ? parseInt(status) >= 400 || !success : true) ); - return list; - }, [filter, sortBy, sortAscending, showOnlyErrors, activeTab]); + setFilteredList(list); + }, [resources, filter, sortBy, sortAscending, showOnlyErrors, activeTab]); - // const lastIndex = currentIndex || filtered.filter((item: any) => item.time <= time).length - 1; - const referenceLines = []; - if (domContentLoadedTime != null) { - referenceLines.push({ - time: domContentLoadedTime.time, - color: DOM_LOADED_TIME_COLOR, - }); - } - if (loadTime != null) { - referenceLines.push({ - time: loadTime.time, - color: LOAD_TIME_COLOR, - }); - } + const referenceLines = useMemo(() => { + const arr = []; + + if (domContentLoadedTime != null) { + arr.push({ + time: domContentLoadedTime.time, + color: DOM_LOADED_TIME_COLOR, + }); + } + if (loadTime != null) { + arr.push({ + time: loadTime.time, + color: LOAD_TIME_COLOR, + }); + } + + return arr; + }, []); const onRowClick = (row: any) => { - showModal(, { - right: true, - }); + showModal( + , + { + right: true, + } + ); + devTools.update(INDEX_KEY, filteredList.indexOf(row)); + setPauseSync(true); }; const handleSort = (sortKey: string) => { if (sortKey === sortBy) { setSortAscending(!sortAscending); - // setSortBy('time'); } setSortBy(sortKey); }; return ( - + setPauseSync(true)} + >
Network @@ -287,7 +304,7 @@ function NetworkPanel(props: Props) { />
- + } size="small" - show={filtered.length === 0} + show={filteredList.length === 0} > { + setPauseSync(true); + devTools.update(INDEX_KEY, filteredList.indexOf(row)); + jump(row.time); + }} sortBy={sortBy} sortAscending={sortAscending} - // activeIndex={lastIndex} + activeIndex={activeIndex} > {[ // { @@ -348,28 +369,28 @@ function NetworkPanel(props: Props) { label: 'Status', dataKey: 'status', width: 70, - onClick: handleSort, + // onClick: handleSort, }, { label: 'Type', dataKey: 'type', width: 90, render: renderType, - onClick: handleSort, + // onClick: handleSort, }, { label: 'Name', width: 240, dataKey: 'name', render: renderName, - onClick: handleSort, + // onClick: handleSort, }, { label: 'Size', width: 80, dataKey: 'decodedBodySize', render: renderSize, - onClick: handleSort, + // onClick: handleSort, hidden: activeTab === XHR, }, { @@ -377,7 +398,7 @@ function NetworkPanel(props: Props) { width: 80, dataKey: 'duration', render: renderDuration, - onClick: handleSort, + // onClick: handleSort, }, ]} @@ -391,9 +412,12 @@ function NetworkPanel(props: Props) { export default connectPlayer((state: any) => ({ location: state.location, resources: state.resourceList, - fetchList: state.fetchList.map((i: any) => Resource({ ...i.toJS(), type: TYPES.XHR })), + fetchList: state.fetchList.map((i: any) => + Resource({ ...i.toJS(), type: TYPES.XHR, time: i.time < 0 ? 0 : i.time }) + ), domContentLoadedTime: state.domContentLoadedTime, loadTime: state.loadTime, + time: state.time, playing: state.playing, domBuildingTime: state.domBuildingTime, }))(NetworkPanel); diff --git a/frontend/app/components/shared/DevTools/TimeTable/TimeTable.tsx b/frontend/app/components/shared/DevTools/TimeTable/TimeTable.tsx index 2b242f331..1a224d968 100644 --- a/frontend/app/components/shared/DevTools/TimeTable/TimeTable.tsx +++ b/frontend/app/components/shared/DevTools/TimeTable/TimeTable.tsx @@ -145,8 +145,19 @@ export default class TimeTable extends React.PureComponent { scroller = React.createRef(); autoScroll = true; - componentDidMount() { - if (this.scroller.current) { + // componentDidMount() { + // if (this.scroller.current) { + // this.scroller.current.scrollToRow(this.props.activeIndex); + // } + // } + + adjustScroll(prevActiveIndex: number) { + if ( + this.props.activeIndex && + this.props.activeIndex >= 0 && + prevActiveIndex !== this.props.activeIndex && + this.scroller.current + ) { this.scroller.current.scrollToRow(this.props.activeIndex); } } @@ -161,14 +172,8 @@ export default class TimeTable extends React.PureComponent { ...computeTimeLine(this.props.rows, this.state.firstVisibleRowIndex, this.visibleCount), }); } - if ( - this.props.activeIndex && - this.props.activeIndex >= 0 && - prevProps.activeIndex !== this.props.activeIndex && - this.scroller.current - ) { - this.scroller.current.scrollToRow(this.props.activeIndex); - } + + // this.adjustScroll(prevProps.activeIndex); } onScroll = ({ @@ -190,7 +195,7 @@ export default class TimeTable extends React.PureComponent { onJump = (index: any) => { if (this.props.onJump) { - this.props.onJump(this.props.rows[index].time); + this.props.onJump(this.props.rows[index]); } }; @@ -203,23 +208,29 @@ export default class TimeTable extends React.PureComponent {
activeIndex, - })} + className={cn( + 'dev-row border-b border-color-gray-light-shade group items-center', + stl.row, + { + [stl.hoverable]: hoverable, + 'error color-red': !!row.isRed && row.isRed(), + 'cursor-pointer': typeof onRowClick === 'function', + [stl.activeRow]: activeIndex === index, + // [stl.inactiveRow]: !activeIndex || index > activeIndex, + } + )} onClick={typeof onRowClick === 'function' ? () => onRowClick(row, index) : undefined} id="table-row" > - {columns.filter((i: any) => !i.hidden).map(({ dataKey, render, width }) => ( -
- {render - ? render(row) - : row[dataKey || ''] || {'empty'}} -
- ))} + {columns + .filter((i: any) => !i.hidden) + .map(({ dataKey, render, width }) => ( +
+ {render + ? render(row) + : row[dataKey || ''] || {'empty'}} +
+ ))}
@@ -324,10 +335,15 @@ export default class TimeTable extends React.PureComponent { 'cursor-pointer': typeof onClick === 'function', })} style={{ width: `${width}px` }} - onClick={() => this.onColumnClick(dataKey, onClick)} + // onClick={() => this.onColumnClick(dataKey, onClick)} > {label} - {!!sortBy && sortBy === dataKey && } + {!!sortBy && sortBy === dataKey && ( + + )}
))}
@@ -360,6 +376,7 @@ export default class TimeTable extends React.PureComponent { {({ width }: { width: number }) => ( { rowHeight={ROW_HEIGHT} rowRenderer={this.renderRow} onScroll={this.onScroll} - scrollToAlignment="start" + scrollToAlignment="center" forceUpdateProp={timestart | timewidth | (activeIndex || 0)} /> )} diff --git a/frontend/app/mstore/sessionStore.ts b/frontend/app/mstore/sessionStore.ts index 98a7061e6..ec72c19ba 100644 --- a/frontend/app/mstore/sessionStore.ts +++ b/frontend/app/mstore/sessionStore.ts @@ -5,75 +5,97 @@ import Session from './types/session'; import Record, { LAST_7_DAYS } from 'Types/app/period'; class UserFilter { - endDate: number = new Date().getTime(); - startDate: number = new Date().getTime() - 24 * 60 * 60 * 1000; - rangeName: string = LAST_7_DAYS; - filters: any = []; - page: number = 1; - limit: number = 10; - period: any = Record({ rangeName: LAST_7_DAYS }); + endDate: number = new Date().getTime(); + startDate: number = new Date().getTime() - 24 * 60 * 60 * 1000; + rangeName: string = LAST_7_DAYS; + filters: any = []; + page: number = 1; + limit: number = 10; + period: any = Record({ rangeName: LAST_7_DAYS }); - constructor() { - makeAutoObservable(this, { - page: observable, - update: action, - }); + constructor() { + makeAutoObservable(this, { + page: observable, + update: action, + }); + } + + update(key: string, value: any) { + // @ts-ignore + this[key] = value; + + if (key === 'period') { + this.startDate = this.period.start; + this.endDate = this.period.end; } + } - update(key: string, value: any) { - this[key] = value; + setFilters(filters: any[]) { + this.filters = filters; + } - if (key === 'period') { - this.startDate = this.period.start; - this.endDate = this.period.end; - } - } + setPage(page: number) { + this.page = page; + } - setFilters(filters: any[]) { - this.filters = filters; - } + toJson() { + return { + endDate: this.period.end, + startDate: this.period.start, + filters: this.filters.map(filterMap), + page: this.page, + limit: this.limit, + }; + } +} - setPage(page: number) { - this.page = page; - } +class DevTools { + networkIndex: 0; + consoleIndex: 0; + eventsIndex: 0; + networkActive: null; + consoleActive: null; + eventsActive: null; + constructor() { + makeAutoObservable(this, { + update: action, + }); + } - toJson() { - return { - endDate: this.period.end, - startDate: this.period.start, - filters: this.filters.map(filterMap), - page: this.page, - limit: this.limit, - }; - } + update(key: string, value: any) { + // @ts-ignore + this[key] = value; + } } export default class SessionStore { - userFilter: UserFilter = new UserFilter(); + userFilter: UserFilter = new UserFilter(); + devTools: DevTools = new DevTools(); - constructor() { - makeAutoObservable(this, { - userFilter: observable, + constructor() { + makeAutoObservable(this, { + userFilter: observable, + devTools: observable, + }); + } + + resetUserFilter() { + this.userFilter = new UserFilter(); + } + + getSessions(filter: any): Promise { + return new Promise((resolve, reject) => { + sessionService + .getSessions(filter.toJson()) + .then((response: any) => { + resolve({ + sessions: response.sessions.map((session: any) => new Session().fromJson(session)), + total: response.total, + }); + }) + .catch((error: any) => { + reject(error); }); - } - - resetUserFilter() { - this.userFilter = new UserFilter(); - } - - getSessions(filter: any): Promise { - return new Promise((resolve, reject) => { - sessionService - .getSessions(filter.toJson()) - .then((response: any) => { - resolve({ - sessions: response.sessions.map((session: any) => new Session().fromJson(session)), - total: response.total, - }); - }) - .catch((error: any) => { - reject(error); - }); - }); - } + }); + } } diff --git a/frontend/app/player/MessageDistributor/MessageDistributor.ts b/frontend/app/player/MessageDistributor/MessageDistributor.ts index a80676a61..e1b59940a 100644 --- a/frontend/app/player/MessageDistributor/MessageDistributor.ts +++ b/frontend/app/player/MessageDistributor/MessageDistributor.ts @@ -132,7 +132,6 @@ export default class MessageDistributor extends StatedScreen { exceptions: session.errors.toJSON(), }) - /* === */ this.loadMessages(); } diff --git a/frontend/app/styles/general.css b/frontend/app/styles/general.css index cce982514..a21cfe239 100644 --- a/frontend/app/styles/general.css +++ b/frontend/app/styles/general.css @@ -355,4 +355,8 @@ p { width: 80px; height: 80px; transform: rotate(45deg); +} + +.dev-row { + transition: all 0.5s; } \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index af78fbdaf..c4f0a68de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -91,6 +91,7 @@ "@types/react-dom": "^18.0.4", "@types/react-redux": "^7.1.24", "@types/react-router-dom": "^5.3.3", + "@types/react-virtualized": "^9.21.21", "@typescript-eslint/eslint-plugin": "^5.24.0", "@typescript-eslint/parser": "^5.24.0", "autoprefixer": "^10.4.7",