change(ui) - console sync
This commit is contained in:
parent
276d2bd100
commit
172fc9be25
6 changed files with 131 additions and 70 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue