refactor(frontend): StackEventsPanel & ProfilePanel

This commit is contained in:
Alex Kaminskii 2022-11-26 13:33:18 +01:00
parent dda632a5dd
commit 52c9edcc98
3 changed files with 77 additions and 119 deletions

View file

@ -5,7 +5,6 @@ import { Tabs, Input, Icon, NoContent } from 'UI';
import cn from 'classnames'; import cn from 'classnames';
import ConsoleRow from '../ConsoleRow'; import ConsoleRow from '../ConsoleRow';
import useLatestRef from 'App/hooks/useLatestRef' import useLatestRef from 'App/hooks/useLatestRef'
import { getRE } from 'App/utils';
import { PlayerContext } from 'App/components/Session/playerContext'; import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized'; import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized';
@ -159,7 +158,7 @@ function ConsolePanel() {
return ( return (
<BottomBlock <BottomBlock
style={{ height: 300 + 'px' }} style={{ height: '300px' }}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
> >

View file

@ -1,13 +1,14 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { TextEllipsis, Input } from 'UI';
import { getRE } from 'App/utils';
import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { TextEllipsis, Input } from 'UI';
import { PlayerContext } from 'App/components/Session/playerContext';
import useInputState from 'App/hooks/useInputState'
import TimeTable from '../TimeTable'; import TimeTable from '../TimeTable';
import BottomBlock from '../BottomBlock'; import BottomBlock from '../BottomBlock';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import ProfilerModal from '../ProfilerModal'; import ProfilerModal from '../ProfilerModal';
import { useRegExListFilterMemo } from '../useListFilter'
const renderDuration = (p: any) => `${p.duration}ms`; const renderDuration = (p: any) => `${p.duration}ms`;
const renderName = (p: any) => <TextEllipsis text={p.name} />; const renderName = (p: any) => <TextEllipsis text={p.name} />;
@ -15,19 +16,12 @@ const renderName = (p: any) => <TextEllipsis text={p.name} />;
function ProfilerPanel() { function ProfilerPanel() {
const { store } = React.useContext(PlayerContext) const { store } = React.useContext(PlayerContext)
const profiles = store.get().profilesList const profiles = store.get().profilesList as any[] // TODO lest internal types
const { showModal } = useModal(); const { showModal } = useModal();
const [filter, setFilter] = useState(''); const [ filter, onFilterChange ] = useInputState()
const filtered: any = React.useMemo(() => { const filtered = useRegExListFilterMemo(profiles, pr => pr.name, filter)
const filterRE = getRE(filter, 'i');
let list = profiles;
list = list.filter(({ name }: any) => (!!filter ? filterRE.test(name) : true));
return list;
}, [filter]);
const onFilterChange = ({ target: { value } }: any) => setFilter(value);
const onRowClick = (profile: any) => { const onRowClick = (profile: any) => {
showModal(<ProfilerModal profile={profile} />, { right: true }); showModal(<ProfilerModal profile={profile} />, { right: true });
}; };

View file

@ -1,95 +1,61 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { hideHint } from 'Duck/components/player'; import { observer } from 'mobx-react-lite';
import { Tooltip, Tabs, Input, NoContent, Icon, Toggler } from 'UI'; import { Tooltip, Tabs, Input, NoContent, Icon, Toggler } from 'UI';
import { getRE } from 'App/utils';
import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized'; import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized';
import { PlayerContext } from 'App/components/Session/playerContext';
import BottomBlock from '../BottomBlock'; import BottomBlock from '../BottomBlock';
import { connectPlayer, jump } from 'Player';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useObserver } from 'mobx-react-lite'; import { typeList } from 'Types/session/stackEvent';
import { DATADOG, SENTRY, STACKDRIVER, typeList } from 'Types/session/stackEvent';
import { connect } from 'react-redux';
import StackEventRow from 'Shared/DevTools/StackEventRow'; import StackEventRow from 'Shared/DevTools/StackEventRow';
import StackEventModal from '../StackEventModal';
let timeOut: any = null; import StackEventModal from '../StackEventModal';
const TIMEOUT_DURATION = 5000; import useAutoscroll from '../useAutoscroll';
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter'
const INDEX_KEY = 'stackEvent'; const INDEX_KEY = 'stackEvent';
const ALL = 'ALL'; const ALL = 'ALL';
const TABS = [ALL, ...typeList].map((tab) => ({ text: tab, key: tab })); const TAB_KEYS = [ ALL, ...typeList] as const
const TABS = TAB_KEYS.map((tab) => ({ text: tab, key: tab }))
function StackEventPanel() {
const { player, store } = React.useContext(PlayerContext)
const jump = (t: number) => player.jump(t)
const { stackList: list, stackListNow: listNow } = store.get()
interface Props {
list: any;
hideHint: any;
time: any;
}
function StackEventPanel(props: Props) {
const { list, time } = props;
const additionalHeight = 0;
const { const {
sessionStore: { devTools }, sessionStore: { devTools },
} = useStore(); } = useStore();
const { showModal } = useModal(); const { showModal } = useModal();
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false); const [isDetailsModalActive, setIsDetailsModalActive] = useState(false) // TODO:embed that into useModal
const [filteredList, setFilteredList] = useState([]); const filter = devTools[INDEX_KEY].filter
const filter = useObserver(() => devTools[INDEX_KEY].filter); const activeTab = devTools[INDEX_KEY].activeTab)
const activeTab = useObserver(() => devTools[INDEX_KEY].activeTab); const activeIndex = devTools[INDEX_KEY].index
const activeIndex = useObserver(() => devTools[INDEX_KEY].index);
const [pauseSync, setPauseSync] = useState(activeIndex > 0); let filteredList = useRegExListFilterMemo(list, it => it.name, filter)
const synRef: any = useRef({}); filteredList = useTabListFilterMemo(filteredList, it => it.source, ALL, activeTab)
synRef.current = {
pauseSync, const onTabClick = (activeTab: typeof TAB_KEYS[number]) => devTools.update(INDEX_KEY, { activeTab })
const onFilterChange = ({ target: { value } }: any) => devTools.update(INDEX_KEY, { filter: value })
const tabs = useMemo(() =>
TABS.filter(({ key }) => key === ALL || list.some(({ source }) => key === source)),
[ list.length ],
)
const {
timeoutStartAutoscroll,
stopAutoscroll,
} = useAutoscroll(
activeIndex, activeIndex,
}; listNow.length,
const _list = React.useRef(); index => devTools.update(INDEX_KEY, { index })
)
const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab }); const onMouseEnter = stopAutoscroll
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 = () => { const onMouseLeave = () => {
if (isDetailsModalActive) return; if (isDetailsModalActive) { return }
removePause(); timeoutStartAutoscroll()
}; }
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({ const cache = new CellMeasurerCache({
fixedWidth: true, fixedWidth: true,
@ -97,11 +63,29 @@ function StackEventPanel(props: Props) {
}); });
const showDetails = (item: any) => { const showDetails = (item: any) => {
setIsDetailsModalActive(true); setIsDetailsModalActive(true)
showModal(<StackEventModal event={item} />, { right: true, onClose: removePause }); showModal(
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) }); <StackEventModal event={item} />,
setPauseSync(true); {
}; right: true,
onClose: () => {
setIsDetailsModalActive(false)
timeoutStartAutoscroll()
}
}
)
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) })
stopAutoscroll()
}
const _list = React.useRef()
useEffect(() => {
if (_list.current) {
// @ts-ignore
_list.current.scrollToRow(activeIndex)
}
}, [ activeIndex ])
const _rowRenderer = ({ index, key, parent, style }: any) => { const _rowRenderer = ({ index, key, parent, style }: any) => {
const item = filteredList[index]; const item = filteredList[index];
@ -116,7 +100,7 @@ function StackEventPanel(props: Props) {
key={item.key} key={item.key}
event={item} event={item}
onJump={() => { onJump={() => {
setPauseSync(true); stopAutoscroll()
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) }); devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
jump(item.time); jump(item.time);
}} }}
@ -125,19 +109,12 @@ function StackEventPanel(props: Props) {
)} )}
</CellMeasurer> </CellMeasurer>
); );
}; }
useEffect(() => {
if (_list.current) {
// @ts-ignore
_list.current.scrollToRow(activeIndex);
}
}, [activeIndex]);
return ( return (
<BottomBlock <BottomBlock
style={{ height: 300 + additionalHeight + 'px' }} style={{ height: '300px' }}
onMouseEnter={() => setPauseSync(true)} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
> >
<BottomBlock.Header> <BottomBlock.Header>
@ -187,16 +164,4 @@ function StackEventPanel(props: Props) {
); );
} }
export default connect( export default observer(StackEventPanel)
(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)
);