change(ui) - console sync

This commit is contained in:
Shekar Siri 2022-11-23 19:47:10 +01:00
parent 276d2bd100
commit 172fc9be25
6 changed files with 131 additions and 70 deletions

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { connectPlayer, jump } from 'Player';
import Log from 'Types/session/log';
import BottomBlock from '../BottomBlock';
@ -7,12 +7,11 @@ import { Tabs, Input, Icon, NoContent } from 'UI';
import cn from 'classnames';
import ConsoleRow from '../ConsoleRow';
import { getRE } from 'App/utils';
import {
List,
CellMeasurer,
CellMeasurerCache,
AutoSizer,
} from 'react-virtualized';
import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized';
import { useObserver } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
import { useModal } from 'App/components/Modal';
const ALL = 'ALL';
const INFO = 'INFO';
@ -57,26 +56,97 @@ const getIconProps = (level: any) => {
return null;
};
const INDEX_KEY = 'console';
let timeOut: any = null;
const TIMEOUT_DURATION = 5000;
interface Props {
logs: any;
exceptions: any;
time: any;
}
function ConsolePanel(props: Props) {
const { logs } = props;
const { logs, time } = props;
const additionalHeight = 0;
const [activeTab, setActiveTab] = useState(ALL);
const [filter, setFilter] = useState('');
// const [activeTab, setActiveTab] = useState(ALL);
// const [filter, setFilter] = useState('');
const {
sessionStore: { devTools },
} = useStore();
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false);
const [filteredList, setFilteredList] = useState([]);
const filter = useObserver(() => devTools[INDEX_KEY].filter);
const activeTab = useObserver(() => devTools[INDEX_KEY].activeTab);
const activeIndex = useObserver(() => devTools[INDEX_KEY].index);
const [pauseSync, setPauseSync] = useState(activeIndex > 0);
const synRef: any = useRef({});
const { showModal } = useModal();
const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab });
const onFilterChange = ({ target: { value } }: any) => {
devTools.update(INDEX_KEY, { filter: value });
};
synRef.current = {
pauseSync,
activeIndex,
};
const removePause = () => {
setIsDetailsModalActive(false);
clearTimeout(timeOut);
timeOut = setTimeout(() => {
devTools.update(INDEX_KEY, { index: getCurrentIndex() });
setPauseSync(false);
}, TIMEOUT_DURATION);
};
const onMouseLeave = () => {
if (isDetailsModalActive) return;
removePause();
};
useEffect(() => {
if (pauseSync) {
removePause();
}
return () => {
clearTimeout(timeOut);
if (!synRef.current.pauseSync) {
devTools.update(INDEX_KEY, { index: 0 });
}
};
}, []);
const getCurrentIndex = () => {
return filteredList.filter((item: any) => item.time <= time).length - 1;
};
useEffect(() => {
const currentIndex = getCurrentIndex();
if (currentIndex !== activeIndex && !pauseSync) {
devTools.update(INDEX_KEY, { index: currentIndex });
}
}, [time]);
const cache = new CellMeasurerCache({
fixedWidth: true,
keyMapper: (index: number) => filtered[index],
keyMapper: (index: number) => filteredList[index],
});
const _list = React.useRef();
const showDetails = (log: any) => {
setIsDetailsModalActive(true);
showModal(<ErrorDetailsModal errorId={log.errorId} />, { right: true, onClose: removePause });
devTools.update(INDEX_KEY, { index: filteredList.indexOf(log) });
setPauseSync(true);
};
const _rowRenderer = ({ index, key, parent, style }: any) => {
const item = filtered[index];
const item = filteredList[index];
return (
// @ts-ignore
<CellMeasurer cache={cache} columnIndex={0} key={key} rowIndex={index} parent={parent}>
{({ measure }: any) => (
<ConsoleRow
@ -85,6 +155,7 @@ function ConsolePanel(props: Props) {
jump={jump}
iconProps={getIconProps(item.level)}
renderWithNL={renderWithNL}
onClick={() => showDetails(item)}
recalcHeight={() => {
measure();
(_list as any).current.recomputeRowHeights(index);
@ -95,7 +166,7 @@ function ConsolePanel(props: Props) {
);
};
const filtered = React.useMemo(() => {
React.useMemo(() => {
const filterRE = getRE(filter, 'i');
let list = logs;
@ -104,14 +175,23 @@ function ConsolePanel(props: Props) {
(!!filter ? filterRE.test(value) : true) &&
(activeTab === ALL || activeTab === LEVEL_TAB[level])
);
return list;
}, [filter, activeTab]);
setFilteredList(list);
}, [logs, filter, activeTab]);
const onTabClick = (activeTab: any) => setActiveTab(activeTab);
const onFilterChange = ({ target: { value } }: any) => setFilter(value);
useEffect(() => {
if (_list.current) {
// @ts-ignore
_list.current.scrollToRow(activeIndex);
}
}, [activeIndex]);
return (
<BottomBlock style={{ height: 300 + additionalHeight + 'px' }}>
<BottomBlock
style={{ height: 300 + additionalHeight + 'px' }}
onMouseEnter={() => setPauseSync(true)}
onMouseLeave={onMouseLeave}
>
{/* @ts-ignore */}
<BottomBlock.Header>
<div className="flex items-center">
<span className="font-semibold color-gray-medium mr-4">Console</span>
@ -125,8 +205,11 @@ function ConsolePanel(props: Props) {
name="filter"
height={28}
onChange={onFilterChange}
value={filter}
/>
{/* @ts-ignore */}
</BottomBlock.Header>
{/* @ts-ignore */}
<BottomBlock.Content className="overflow-y-auto">
<NoContent
title={
@ -136,23 +219,28 @@ function ConsolePanel(props: Props) {
</div>
}
size="small"
show={filtered.length === 0}
show={filteredList.length === 0}
>
{/* @ts-ignore */}
<AutoSizer>
{({ height, width }: any) => (
// @ts-ignore
<List
ref={_list}
deferredMeasurementCache={cache}
overscanRowCount={5}
rowCount={Math.ceil(filtered.length || 1)}
rowCount={Math.ceil(filteredList.length || 1)}
rowHeight={cache.rowHeight}
rowRenderer={_rowRenderer}
width={width}
height={height}
// scrollToIndex={activeIndex}
scrollToAlignment="center"
/>
)}
</AutoSizer>
</NoContent>
{/* @ts-ignore */}
</BottomBlock.Content>
</BottomBlock>
);
@ -170,6 +258,7 @@ export default connectPlayer((state: any) => {
})
);
return {
time: state.time,
logs: logs.concat(logExceptions),
};
})(ConsolePanel);

View file

@ -2,8 +2,6 @@ import React, { useState } from 'react';
import cn from 'classnames';
import { Icon } from 'UI';
import JumpButton from 'Shared/DevTools/JumpButton';
import { useModal } from 'App/components/Modal';
import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal';
interface Props {
log: any;
@ -12,24 +10,20 @@ interface Props {
renderWithNL?: any;
style?: any;
recalcHeight?: () => void;
onClick: () => void;
}
function ConsoleRow(props: Props) {
const { log, iconProps, jump, renderWithNL, style, recalcHeight } = props;
const { showModal } = useModal();
const [expanded, setExpanded] = useState(false);
const lines = log.value.split('\n').filter((l: any) => !!l);
const canExpand = lines.length > 1;
const clickable = canExpand || !!log.errorId;
const onErrorClick = () => {
showModal(<ErrorDetailsModal errorId={log.errorId} />, { right: true });
};
const toggleExpand = () => {
setExpanded(!expanded)
setTimeout(() => recalcHeight(), 0)
}
setExpanded(!expanded);
setTimeout(() => recalcHeight(), 0);
};
return (
<div
style={style}
@ -43,9 +37,7 @@ function ConsoleRow(props: Props) {
'cursor-pointer underline decoration-dotted decoration-gray-200': !!log.errorId,
}
)}
onClick={
clickable ? () => (!!log.errorId ? onErrorClick() : toggleExpand()) : () => {}
}
onClick={clickable ? () => (!!log.errorId ? props.onClick() : toggleExpand()) : () => {}}
>
<div className="mr-2">
<Icon size="14" {...iconProps} />
@ -57,7 +49,13 @@ function ConsoleRow(props: Props) {
)}
<span>{renderWithNL(lines.pop())}</span>
</div>
{canExpand && expanded && lines.map((l: string, i: number) => <div key={l.slice(0,4)+i} className="ml-4 mb-1">{l}</div>)}
{canExpand &&
expanded &&
lines.map((l: string, i: number) => (
<div key={l.slice(0, 4) + i} className="ml-4 mb-1">
{l}
</div>
))}
</div>
<JumpButton onClick={() => jump(log.time)} />
</div>

View file

@ -142,9 +142,6 @@ function NetworkPanel(props: Props) {
const { resources, time, domContentLoadedTime, loadTime, domBuildingTime, fetchList } = props;
const { showModal } = useModal();
const [sortBy, setSortBy] = useState('time');
const [sortAscending, setSortAscending] = useState(true);
const [filteredList, setFilteredList] = useState([]);
const [showOnlyErrors, setShowOnlyErrors] = useState(false);
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false);
@ -234,13 +231,6 @@ function NetworkPanel(props: Props) {
(list = list.filter((networkCall: any) => networkCall.url !== fetchCall.url))
);
list = list.concat(fetchList);
// list = list.sort((a: any, b: any) => {
// return compare(a, b, sortBy);
// });
// if (!sortAscending) {
// list = list.reverse();
// }
list = list.filter(
({ type, name, status, success }: any) =>
@ -249,7 +239,7 @@ function NetworkPanel(props: Props) {
(showOnlyErrors ? parseInt(status) >= 400 || !success : true)
);
setFilteredList(list);
}, [resources, filter, sortBy, sortAscending, showOnlyErrors, activeTab]);
}, [resources, filter, showOnlyErrors, activeTab]);
const referenceLines = useMemo(() => {
const arr = [];
@ -283,13 +273,6 @@ function NetworkPanel(props: Props) {
setPauseSync(true);
};
const handleSort = (sortKey: string) => {
if (sortKey === sortBy) {
setSortAscending(!sortAscending);
}
setSortBy(sortKey);
};
useEffect(() => {
devTools.update(INDEX_KEY, { filter, activeTab });
}, [filter, activeTab]);
@ -387,8 +370,6 @@ function NetworkPanel(props: Props) {
devTools.update(INDEX_KEY, { index: filteredList.indexOf(row) });
jump(row.time);
}}
sortBy={sortBy}
sortAscending={sortAscending}
activeIndex={activeIndex}
>
{[
@ -401,28 +382,24 @@ function NetworkPanel(props: Props) {
label: 'Status',
dataKey: 'status',
width: 70,
// onClick: handleSort,
},
{
label: 'Type',
dataKey: 'type',
width: 90,
render: renderType,
// onClick: handleSort,
},
{
label: 'Name',
width: 240,
dataKey: 'name',
render: renderName,
// onClick: handleSort,
},
{
label: 'Size',
width: 80,
dataKey: 'decodedBodySize',
render: renderSize,
// onClick: handleSort,
hidden: activeTab === XHR,
},
{
@ -430,7 +407,6 @@ function NetworkPanel(props: Props) {
width: 80,
dataKey: 'duration',
render: renderDuration,
// onClick: handleSort,
},
]}
</TimeTable>

View file

@ -127,6 +127,13 @@ function StackEventPanel(props: Props) {
);
};
useEffect(() => {
if (_list.current) {
// @ts-ignore
_list.current.scrollToRow(activeIndex);
}
}, [activeIndex]);
return (
<BottomBlock
style={{ height: 300 + additionalHeight + 'px' }}
@ -171,7 +178,6 @@ function StackEventPanel(props: Props) {
rowRenderer={_rowRenderer}
width={width}
height={height}
scrollToIndex={activeIndex}
scrollToAlignment="center"
/>
)}

View file

@ -72,8 +72,6 @@ type Props = {
hoverable?: boolean;
onRowClick?: (row: any, index: number) => void;
onJump?: (time: any) => void;
sortBy?: string;
sortAscending?: boolean;
};
type TimeLineInfo = {
@ -281,8 +279,6 @@ export default class TimeTable extends React.PureComponent<Props, State> {
referenceLines = [],
additionalHeight = 0,
activeIndex,
sortBy = '',
sortAscending = true,
} = this.props;
const columns = this.props.children.filter((i: any) => !i.hidden);
const { timewidth, timestart } = this.state;
@ -338,12 +334,6 @@ export default class TimeTable extends React.PureComponent<Props, State> {
// onClick={() => this.onColumnClick(dataKey, onClick)}
>
<span>{label}</span>
{!!sortBy && sortBy === dataKey && (
<Icon
name={sortAscending ? 'caret-down-fill' : 'caret-up-fill'}
className="ml-1"
/>
)}
</div>
))}
</div>

View file

@ -59,10 +59,12 @@ interface BaseDevState {
class DevTools {
network: BaseDevState;
stackEvent: BaseDevState;
console: BaseDevState;
constructor() {
this.network = { index: 0, filter: '', activeTab: 'ALL', isError: false };
this.stackEvent = { index: 0, filter: '', activeTab: 'ALL', isError: false };
this.console = { index: 0, filter: '', activeTab: 'ALL', isError: false };
makeAutoObservable(this, {
update: action,
});