Merge pull request #833 from openreplay/dev-tools-sync
dev tools sync - Network, Console, Events
This commit is contained in:
commit
c773ed99ab
15 changed files with 639 additions and 257 deletions
|
|
@ -42,6 +42,7 @@ import { updateLastPlayedSession } from 'Duck/sessions';
|
|||
import OverviewPanel from '../OverviewPanel';
|
||||
import ConsolePanel from 'Shared/DevTools/ConsolePanel';
|
||||
import ProfilerPanel from 'Shared/DevTools/ProfilerPanel';
|
||||
import StackEventPanel from 'Shared/DevTools/StackEventPanel';
|
||||
|
||||
@connectPlayer((state) => ({
|
||||
live: state.live,
|
||||
|
|
@ -115,7 +116,8 @@ export default class Player extends React.PureComponent {
|
|||
// <Network />
|
||||
<NetworkPanel />
|
||||
)}
|
||||
{bottomBlock === STACKEVENTS && <StackEvents />}
|
||||
{/* {bottomBlock === STACKEVENTS && <StackEvents />} */}
|
||||
{bottomBlock === STACKEVENTS && <StackEventPanel />}
|
||||
{bottomBlock === STORAGE && <Storage />}
|
||||
{bottomBlock === PROFILER && <ProfilerPanel />}
|
||||
{bottomBlock === PERFORMANCE && <ConnectedPerformance />}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}) => (
|
||||
<div className={ cn(stl.wrapper, "flex flex-col mb-2") } { ...props } >
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}) => {
|
||||
useEffect(() => {}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(stl.wrapper, 'flex flex-col mb-2')}
|
||||
{...props}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
BottomBlock.displayName = 'BottomBlock';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
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';
|
||||
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';
|
||||
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';
|
||||
|
|
@ -58,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
|
||||
|
|
@ -86,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);
|
||||
|
|
@ -96,7 +166,7 @@ function ConsolePanel(props: Props) {
|
|||
);
|
||||
};
|
||||
|
||||
let filtered = React.useMemo(() => {
|
||||
React.useMemo(() => {
|
||||
const filterRE = getRE(filter, 'i');
|
||||
let list = logs;
|
||||
|
||||
|
|
@ -105,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>
|
||||
|
|
@ -126,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={
|
||||
|
|
@ -137,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>
|
||||
);
|
||||
|
|
@ -171,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>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ interface Props {
|
|||
tooltip?: string;
|
||||
}
|
||||
function JumpButton(props: Props) {
|
||||
const { tooltip = '' } = props;
|
||||
const { tooltip } = props;
|
||||
return (
|
||||
<div className="absolute right-0 top-0 bottom-0 my-auto flex items-center">
|
||||
<Tooltip title={tooltip} disabled={!!tooltip}>
|
||||
<Tooltip title={tooltip} disabled={!tooltip}>
|
||||
<div
|
||||
className="mr-2 border cursor-pointer invisible group-hover:visible rounded-lg bg-active-blue text-xs flex items-center px-2 py-1 color-teal hover:shadow h-6"
|
||||
onClick={(e: any) => {
|
||||
|
|
|
|||
|
|
@ -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,10 @@ 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 = 'network';
|
||||
|
||||
const ALL = 'ALL';
|
||||
const XHR = 'xhr';
|
||||
|
|
@ -67,37 +71,6 @@ export function renderStart(r: any) {
|
|||
);
|
||||
}
|
||||
|
||||
// const renderXHRText = () => (
|
||||
// <span className="flex items-center">
|
||||
// {XHR}
|
||||
// <QuestionMarkHint
|
||||
// content={
|
||||
// <>
|
||||
// Use our{' '}
|
||||
// <a
|
||||
// className="color-teal underline"
|
||||
// target="_blank"
|
||||
// href="https://docs.openreplay.com/plugins/fetch"
|
||||
// >
|
||||
// Fetch plugin
|
||||
// </a>
|
||||
// {' to capture HTTP requests and responses, including status codes and bodies.'} <br />
|
||||
// We also provide{' '}
|
||||
// <a
|
||||
// className="color-teal underline"
|
||||
// target="_blank"
|
||||
// href="https://docs.openreplay.com/plugins/graphql"
|
||||
// >
|
||||
// support for GraphQL
|
||||
// </a>
|
||||
// {' for easy debugging of your queries.'}
|
||||
// </>
|
||||
// }
|
||||
// className="ml-1"
|
||||
// />
|
||||
// </span>
|
||||
// );
|
||||
|
||||
function renderSize(r: any) {
|
||||
if (r.responseBodySize) return formatBytes(r.responseBodySize);
|
||||
let triggerText;
|
||||
|
|
@ -152,6 +125,9 @@ export function renderDuration(r: any) {
|
|||
);
|
||||
}
|
||||
|
||||
let timeOut: any = null;
|
||||
const TIMEOUT_DURATION = 5000;
|
||||
|
||||
interface Props {
|
||||
location: any;
|
||||
resources: any;
|
||||
|
|
@ -160,58 +136,101 @@ 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 [activeTab, setActiveTab] = useState(ALL);
|
||||
const [sortBy, setSortBy] = useState('time');
|
||||
const [sortAscending, setSortAscending] = useState(true);
|
||||
const [filter, setFilter] = useState('');
|
||||
const { resources, time, domContentLoadedTime, loadTime, domBuildingTime, fetchList } = props;
|
||||
const { showModal } = useModal();
|
||||
|
||||
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 [isDetailsModalActive, setIsDetailsModalActive] = useState(false);
|
||||
const additionalHeight = 0;
|
||||
const fetchPresented = fetchList.length > 0;
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
} = useStore();
|
||||
// const [filter, setFilter] = useState(devTools[INDEX_KEY].filter);
|
||||
// const [activeTab, setActiveTab] = useState(ALL);
|
||||
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 resourcesSize = resources.reduce(
|
||||
(sum: any, { decodedBodySize }: any) => sum + (decodedBodySize || 0),
|
||||
0
|
||||
);
|
||||
const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({ target: { value } }: any) => {
|
||||
devTools.update(INDEX_KEY, { filter: value });
|
||||
};
|
||||
|
||||
const transferredSize = resources.reduce(
|
||||
(sum: any, { headerSize, encodedBodySize }: any) =>
|
||||
sum + (headerSize || 0) + (encodedBodySize || 0),
|
||||
0
|
||||
);
|
||||
synRef.current = {
|
||||
pauseSync,
|
||||
activeIndex,
|
||||
};
|
||||
|
||||
const filterRE = getRE(filter, 'i');
|
||||
let filtered = React.useMemo(() => {
|
||||
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 { 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) =>
|
||||
(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) =>
|
||||
|
|
@ -219,41 +238,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, 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 = [];
|
||||
|
||||
const onRowClick = (row: any) => {
|
||||
showModal(<FetchDetailsModal resource={row} rows={filtered} fetchPresented={fetchPresented} />, {
|
||||
right: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSort = (sortKey: string) => {
|
||||
if (sortKey === sortBy) {
|
||||
setSortAscending(!sortAscending);
|
||||
// setSortBy('time');
|
||||
if (domContentLoadedTime != null) {
|
||||
arr.push({
|
||||
time: domContentLoadedTime.time,
|
||||
color: DOM_LOADED_TIME_COLOR,
|
||||
});
|
||||
}
|
||||
setSortBy(sortKey);
|
||||
if (loadTime != null) {
|
||||
arr.push({
|
||||
time: loadTime.time,
|
||||
color: LOAD_TIME_COLOR,
|
||||
});
|
||||
}
|
||||
|
||||
return arr;
|
||||
}, []);
|
||||
|
||||
const showDetailsModal = (row: any) => {
|
||||
setIsDetailsModalActive(true);
|
||||
showModal(
|
||||
<FetchDetailsModal resource={row} rows={filteredList} fetchPresented={fetchPresented} />,
|
||||
{
|
||||
right: true,
|
||||
onClose: removePause,
|
||||
}
|
||||
);
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(row) });
|
||||
setPauseSync(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
devTools.update(INDEX_KEY, { filter, activeTab });
|
||||
}, [filter, activeTab]);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<BottomBlock style={{ height: 300 + additionalHeight + 'px' }} className="border">
|
||||
<BottomBlock
|
||||
style={{ height: 300 + additionalHeight + 'px' }}
|
||||
className="border"
|
||||
onMouseEnter={() => setPauseSync(true)}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<BottomBlock.Header>
|
||||
<div className="flex items-center">
|
||||
<span className="font-semibold color-gray-medium mr-4">Network</span>
|
||||
|
|
@ -274,6 +305,7 @@ function NetworkPanel(props: Props) {
|
|||
onChange={onFilterChange}
|
||||
height={28}
|
||||
width={230}
|
||||
value={filter}
|
||||
/>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content>
|
||||
|
|
@ -287,7 +319,7 @@ function NetworkPanel(props: Props) {
|
|||
/>
|
||||
</div>
|
||||
<InfoLine>
|
||||
<InfoLine.Point label={filtered.length} value=" requests" />
|
||||
<InfoLine.Point label={filteredList.length + ''} value=" requests" />
|
||||
<InfoLine.Point
|
||||
label={formatBytes(transferredSize)}
|
||||
value="transferred"
|
||||
|
|
@ -325,18 +357,20 @@ function NetworkPanel(props: Props) {
|
|||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={filtered.length === 0}
|
||||
show={filteredList.length === 0}
|
||||
>
|
||||
<TimeTable
|
||||
rows={filtered}
|
||||
rows={filteredList}
|
||||
referenceLines={referenceLines}
|
||||
renderPopup
|
||||
onRowClick={onRowClick}
|
||||
onRowClick={showDetailsModal}
|
||||
additionalHeight={additionalHeight}
|
||||
onJump={jump}
|
||||
sortBy={sortBy}
|
||||
sortAscending={sortAscending}
|
||||
// activeIndex={lastIndex}
|
||||
onJump={(row: any) => {
|
||||
setPauseSync(true);
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(row) });
|
||||
jump(row.time);
|
||||
}}
|
||||
activeIndex={activeIndex}
|
||||
>
|
||||
{[
|
||||
// {
|
||||
|
|
@ -348,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,
|
||||
},
|
||||
{
|
||||
|
|
@ -377,7 +407,6 @@ function NetworkPanel(props: Props) {
|
|||
width: 80,
|
||||
dataKey: 'duration',
|
||||
render: renderDuration,
|
||||
onClick: handleSort,
|
||||
},
|
||||
]}
|
||||
</TimeTable>
|
||||
|
|
@ -391,9 +420,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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { hideHint } from 'Duck/components/player';
|
||||
import { Tooltip, Tabs, Input, NoContent, Icon, Toggler } from 'UI';
|
||||
import { getRE } from 'App/utils';
|
||||
import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized';
|
||||
|
||||
import BottomBlock from '../BottomBlock';
|
||||
import { connectPlayer, jump } from 'Player';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import { DATADOG, SENTRY, STACKDRIVER, typeList } from 'Types/session/stackEvent';
|
||||
import { connect } from 'react-redux';
|
||||
import StackEventRow from 'Shared/DevTools/StackEventRow';
|
||||
import StackEventModal from '../StackEventModal';
|
||||
|
||||
let timeOut: any = null;
|
||||
const TIMEOUT_DURATION = 5000;
|
||||
const INDEX_KEY = 'stackEvent';
|
||||
const ALL = 'ALL';
|
||||
const TABS = [ALL, ...typeList].map((tab) => ({ text: tab, key: tab }));
|
||||
|
||||
interface Props {
|
||||
list: any;
|
||||
hideHint: any;
|
||||
time: any;
|
||||
}
|
||||
function StackEventPanel(props: Props) {
|
||||
const { list, time } = props;
|
||||
const additionalHeight = 0;
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
} = useStore();
|
||||
const { showModal } = useModal();
|
||||
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({});
|
||||
synRef.current = {
|
||||
pauseSync,
|
||||
activeIndex,
|
||||
};
|
||||
const _list = React.useRef();
|
||||
|
||||
const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab });
|
||||
const onFilterChange = ({ target: { value } }: any) => {
|
||||
devTools.update(INDEX_KEY, { filter: value });
|
||||
};
|
||||
|
||||
const getCurrentIndex = () => {
|
||||
return filteredList.filter((item: any) => item.time <= time).length - 1;
|
||||
};
|
||||
|
||||
const removePause = () => {
|
||||
clearTimeout(timeOut);
|
||||
setIsDetailsModalActive(false);
|
||||
timeOut = setTimeout(() => {
|
||||
devTools.update(INDEX_KEY, { index: getCurrentIndex() });
|
||||
setPauseSync(false);
|
||||
}, TIMEOUT_DURATION);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const currentIndex = getCurrentIndex();
|
||||
if (currentIndex !== activeIndex && !pauseSync) {
|
||||
devTools.update(INDEX_KEY, { index: currentIndex });
|
||||
}
|
||||
}, [time]);
|
||||
|
||||
const onMouseLeave = () => {
|
||||
if (isDetailsModalActive) return;
|
||||
removePause();
|
||||
};
|
||||
|
||||
React.useMemo(() => {
|
||||
const filterRE = getRE(filter, 'i');
|
||||
let list = props.list;
|
||||
|
||||
list = list.filter(
|
||||
({ name, source }: any) =>
|
||||
(!!filter ? filterRE.test(name) : true) && (activeTab === ALL || activeTab === source)
|
||||
);
|
||||
|
||||
setFilteredList(list);
|
||||
}, [filter, activeTab]);
|
||||
|
||||
const tabs = useMemo(() => {
|
||||
return TABS.filter(({ key }) => key === ALL || list.some(({ source }: any) => key === source));
|
||||
}, []);
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
keyMapper: (index: number) => filteredList[index],
|
||||
});
|
||||
|
||||
const showDetails = (item: any) => {
|
||||
setIsDetailsModalActive(true);
|
||||
showModal(<StackEventModal event={item} />, { right: true, onClose: removePause });
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
|
||||
setPauseSync(true);
|
||||
};
|
||||
|
||||
const _rowRenderer = ({ index, key, parent, style }: any) => {
|
||||
const item = filteredList[index];
|
||||
|
||||
return (
|
||||
// @ts-ignore
|
||||
<CellMeasurer cache={cache} columnIndex={0} key={key} rowIndex={index} parent={parent}>
|
||||
{() => (
|
||||
<StackEventRow
|
||||
isActive={activeIndex === index}
|
||||
style={style}
|
||||
key={item.key}
|
||||
event={item}
|
||||
onJump={() => {
|
||||
setPauseSync(true);
|
||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
|
||||
jump(item.time);
|
||||
}}
|
||||
onClick={() => showDetails(item)}
|
||||
/>
|
||||
)}
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (_list.current) {
|
||||
// @ts-ignore
|
||||
_list.current.scrollToRow(activeIndex);
|
||||
}
|
||||
}, [activeIndex]);
|
||||
|
||||
return (
|
||||
<BottomBlock
|
||||
style={{ height: 300 + additionalHeight + 'px' }}
|
||||
onMouseEnter={() => setPauseSync(true)}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<BottomBlock.Header>
|
||||
<div className="flex items-center">
|
||||
<span className="font-semibold color-gray-medium mr-4">Stack Events</span>
|
||||
<Tabs tabs={tabs} active={activeTab} onClick={onTabClick} border={false} />
|
||||
</div>
|
||||
<Input
|
||||
className="input-small h-8"
|
||||
placeholder="Filter by keyword"
|
||||
icon="search"
|
||||
iconPosition="left"
|
||||
name="filter"
|
||||
height={28}
|
||||
onChange={onFilterChange}
|
||||
value={filter}
|
||||
/>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content className="overflow-y-auto">
|
||||
<NoContent
|
||||
title={
|
||||
<div className="capitalize flex items-center mt-16">
|
||||
<Icon name="info-circle" className="mr-2" size="18" />
|
||||
No Data
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={filteredList.length === 0}
|
||||
>
|
||||
<AutoSizer>
|
||||
{({ height, width }: any) => (
|
||||
<List
|
||||
ref={_list}
|
||||
deferredMeasurementCache={cache}
|
||||
overscanRowCount={5}
|
||||
rowCount={Math.ceil(filteredList.length || 1)}
|
||||
rowHeight={cache.rowHeight}
|
||||
rowRenderer={_rowRenderer}
|
||||
width={width}
|
||||
height={height}
|
||||
scrollToAlignment="center"
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</NoContent>
|
||||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
hintIsHidden:
|
||||
state.getIn(['components', 'player', 'hiddenHints', 'stack']) ||
|
||||
!state.getIn(['site', 'list']).some((s: any) => s.stackIntegrations),
|
||||
}),
|
||||
{ hideHint }
|
||||
)(
|
||||
connectPlayer((state: any) => ({
|
||||
list: state.stackList,
|
||||
time: state.time,
|
||||
}))(StackEventPanel)
|
||||
);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './StackEventPanel';
|
||||
|
|
@ -3,21 +3,18 @@ import JumpButton from '../JumpButton';
|
|||
import { Icon } from 'UI';
|
||||
import cn from 'classnames';
|
||||
import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import StackEventModal from '../StackEventModal';
|
||||
|
||||
interface Props {
|
||||
event: any;
|
||||
onJump: any;
|
||||
style?: any;
|
||||
isActive?: boolean;
|
||||
onClick?: any;
|
||||
}
|
||||
function StackEventRow(props: Props) {
|
||||
const { event, onJump } = props;
|
||||
const { event, onJump, style, isActive } = props;
|
||||
let message = event.payload[0] || '';
|
||||
message = typeof message === 'string' ? message : JSON.stringify(message);
|
||||
const onClickDetails = () => {
|
||||
showModal(<StackEventModal event={event} />, { right: true });
|
||||
};
|
||||
const { showModal } = useModal();
|
||||
|
||||
const iconProps: any = React.useMemo(() => {
|
||||
const { source } = event;
|
||||
|
|
@ -30,11 +27,13 @@ function StackEventRow(props: Props) {
|
|||
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
data-scroll-item={event.isRed()}
|
||||
onClick={onClickDetails}
|
||||
onClick={props.onClick}
|
||||
className={cn(
|
||||
'group flex items-center py-2 px-4 border-b cursor-pointer relative',
|
||||
'hover:bg-active-blue'
|
||||
'hover:bg-active-blue',
|
||||
{ 'bg-teal-light': isActive }
|
||||
)}
|
||||
>
|
||||
<div className={cn('mr-auto flex items-start')}>
|
||||
|
|
|
|||
|
|
@ -72,8 +72,6 @@ type Props = {
|
|||
hoverable?: boolean;
|
||||
onRowClick?: (row: any, index: number) => void;
|
||||
onJump?: (time: any) => void;
|
||||
sortBy?: string;
|
||||
sortAscending?: boolean;
|
||||
};
|
||||
|
||||
type TimeLineInfo = {
|
||||
|
|
@ -145,8 +143,19 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
scroller = React.createRef<List>();
|
||||
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 +170,8 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
...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 +193,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
|
||||
onJump = (index: any) => {
|
||||
if (this.props.onJump) {
|
||||
this.props.onJump(this.props.rows[index].time);
|
||||
this.props.onJump(this.props.rows[index]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -203,23 +206,29 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
<div
|
||||
style={rowStyle}
|
||||
key={key}
|
||||
className={cn('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,
|
||||
})}
|
||||
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 }) => (
|
||||
<div className={stl.cell} style={{ width: `${width}px` }}>
|
||||
{render
|
||||
? render(row)
|
||||
: row[dataKey || ''] || <i className="color-gray-light">{'empty'}</i>}
|
||||
</div>
|
||||
))}
|
||||
{columns
|
||||
.filter((i: any) => !i.hidden)
|
||||
.map(({ dataKey, render, width }) => (
|
||||
<div className={stl.cell} style={{ width: `${width}px` }}>
|
||||
{render
|
||||
? render(row)
|
||||
: row[dataKey || ''] || <i className="color-gray-light">{'empty'}</i>}
|
||||
</div>
|
||||
))}
|
||||
<div className={cn('relative flex-1 flex', stl.timeBarWrapper)}>
|
||||
<BarRow resource={row} timestart={timestart} timewidth={timewidth} popup={renderPopup} />
|
||||
</div>
|
||||
|
|
@ -270,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;
|
||||
|
|
@ -324,10 +331,9 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
'cursor-pointer': typeof onClick === 'function',
|
||||
})}
|
||||
style={{ width: `${width}px` }}
|
||||
onClick={() => this.onColumnClick(dataKey, onClick)}
|
||||
// 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>
|
||||
|
|
@ -360,6 +366,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
<AutoSizer disableHeight>
|
||||
{({ width }: { width: number }) => (
|
||||
<List
|
||||
scrollToIndex={this.props.activeIndex || 0}
|
||||
ref={this.scroller}
|
||||
className={stl.list}
|
||||
height={this.tableHeight + additionalHeight}
|
||||
|
|
@ -369,7 +376,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
|
|||
rowHeight={ROW_HEIGHT}
|
||||
rowRenderer={this.renderRow}
|
||||
onScroll={this.onScroll}
|
||||
scrollToAlignment="start"
|
||||
scrollToAlignment="center"
|
||||
forceUpdateProp={timestart | timewidth | (activeIndex || 0)}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Button } from 'UI';
|
|||
import FetchPluginMessage from './components/FetchPluginMessage';
|
||||
import { TYPES } from 'Types/session/resource';
|
||||
import FetchTabs from './components/FetchTabs/FetchTabs';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
||||
interface Props {
|
||||
resource: any;
|
||||
|
|
@ -16,6 +17,9 @@ function FetchDetailsModal(props: Props) {
|
|||
const [first, setFirst] = useState(false);
|
||||
const [last, setLast] = useState(false);
|
||||
const isXHR = resource.type === TYPES.XHR || resource.type === TYPES.FETCH;
|
||||
const {
|
||||
sessionStore: { devTools },
|
||||
} = useStore();
|
||||
|
||||
useEffect(() => {
|
||||
const index = rows.indexOf(resource);
|
||||
|
|
@ -28,6 +32,7 @@ function FetchDetailsModal(props: Props) {
|
|||
const index = rows.indexOf(resource);
|
||||
if (index > 0) {
|
||||
setResource(rows[index - 1]);
|
||||
devTools.update('network', { index: index - 1 })
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -35,6 +40,7 @@ function FetchDetailsModal(props: Props) {
|
|||
const index = rows.indexOf(resource);
|
||||
if (index < rows.length - 1) {
|
||||
setResource(rows[index + 1]);
|
||||
devTools.update('network', { index: index + 1 })
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,75 +5,105 @@ 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;
|
||||
}
|
||||
interface BaseDevState {
|
||||
index: number;
|
||||
filter: string;
|
||||
activeTab: string;
|
||||
isError: boolean;
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
endDate: this.period.end,
|
||||
startDate: this.period.start,
|
||||
filters: this.filters.map(filterMap),
|
||||
page: this.page,
|
||||
limit: this.limit,
|
||||
};
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
update(key: string, value: any) {
|
||||
// @ts-ignore
|
||||
this[key] = Object.assign(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<any> {
|
||||
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<any> {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,6 @@ export default class MessageDistributor extends StatedScreen {
|
|||
exceptions: session.errors.toJSON(),
|
||||
})
|
||||
|
||||
|
||||
/* === */
|
||||
this.loadMessages();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -355,4 +355,8 @@ p {
|
|||
width: 80px;
|
||||
height: 80px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.dev-row {
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue