import React, { useEffect, useRef, useState } from 'react'; import { LogLevel, ILog } from 'Player'; import BottomBlock from '../BottomBlock'; import { Tabs, Input, Icon, NoContent } from 'UI'; import cn from 'classnames'; import ConsoleRow from '../ConsoleRow'; import { getRE } from 'App/utils'; import { PlayerContext } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite'; import { List, CellMeasurer, CellMeasurerCache, AutoSizer } from 'react-virtualized'; 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'; const WARNINGS = 'WARNINGS'; const ERRORS = 'ERRORS'; const LEVEL_TAB = { [LogLevel.INFO]: INFO, [LogLevel.LOG]: INFO, [LogLevel.WARN]: WARNINGS, [LogLevel.ERROR]: ERRORS, [LogLevel.EXCEPTION]: ERRORS, } as const const TABS = [ALL, ERRORS, WARNINGS, INFO].map((tab) => ({ text: tab, key: tab })); function renderWithNL(s = '') { if (typeof s !== 'string') return ''; return s.split('\n').map((line, i) =>
{line}
); } const getIconProps = (level: any) => { switch (level) { case LogLevel.INFO: case LogLevel.LOG: return { name: 'console/info', color: 'blue2', }; case LogLevel.WARN: case LogLevel.WARNING: return { name: 'console/warning', color: 'red2', }; case LogLevel.ERROR: return { name: 'console/error', color: 'red', }; } return null; }; const INDEX_KEY = 'console'; const TIMEOUT_DURATION = 5000; function ConsolePanel() { const { sessionStore: { devTools }, } = useStore() const filter = devTools[INDEX_KEY].filter; const activeTab = devTools[INDEX_KEY].activeTab; // Why do we need to keep index in the store? if we could get read of it it would simplify the code const activeIndex = devTools[INDEX_KEY].index; const [isDetailsModalActive, setIsDetailsModalActive] = useState(false); const [filteredList, setFilteredList] = useState([]); const [autoScroll, setAutoScroll] = useState(activeIndex === 0); const { showModal } = useModal(); const [logs, setLogs] = useState([]) const { player, store } = React.useContext(PlayerContext) const jump = (t: number) => player.jump(t) const { logList, exceptionsList, logListNow, exceptionsListNow } = store.get() useEffect(() => { setLogs(logList.concat(exceptionsList).sort((a, b) => a.time - b.time)) }, [logList.length, exceptionsList.length ]) useEffect(() => { const filterRE = getRE(filter, 'i') const list = logs.filter( ({ value, level }: ILog) => (!!filter ? filterRE.test(value) : true) && (activeTab === ALL || activeTab === LEVEL_TAB[level]) ) setFilteredList(list) }, [logs.length, filter, activeTab]) const onTabClick = (activeTab: any) => devTools.update(INDEX_KEY, { activeTab }) const onFilterChange = ({ target: { value } }: any) => devTools.update(INDEX_KEY, { filter: value }) const autoScrollIndex = logListNow.length + exceptionsListNow.length // AutoScroll index update useEffect(() => { if (autoScroll && autoScrollIndex !== activeIndex) { devTools.update(INDEX_KEY, { index: autoScrollIndex }) // can just scroll here } }, [autoScroll, autoScrollIndex]) const timeoutIDRef = React.useRef>() const timeoutStartAutoScroll = () => { clearTimeout(timeoutIDRef.current); timeoutIDRef.current = setTimeout(() => { setAutoScroll(true) }, TIMEOUT_DURATION); } const stopAutoScroll = () => { clearTimeout(timeoutIDRef.current) setAutoScroll(false) } const onMouseEnter = stopAutoScroll const onMouseLeave = () => { if (isDetailsModalActive) { return } timeoutStartAutoScroll() } // latest ref const autoScrollRef = useRef(autoScroll) useEffect(() => { autoScrollRef.current = autoScroll }, [ autoScroll ]) useEffect(() => { if (!autoScroll) { timeoutStartAutoScroll() } return () => { clearTimeout(timeoutIDRef.current) if (autoScrollRef.current) { devTools.update(INDEX_KEY, { index: 0 }) // index:0 means autoscroll is active } } }, []) const cache = new CellMeasurerCache({ fixedWidth: true, keyMapper: (index: number) => filteredList[index], }); const _list = React.useRef(); useEffect(() => { if (_list.current) { // @ts-ignore _list.current.scrollToRow(activeIndex); } }, [activeIndex]); const showDetails = (log: any) => { setIsDetailsModalActive(true); showModal( , { right: true, onClose: () => { setIsDetailsModalActive(false) timeoutStartAutoScroll() } }); devTools.update(INDEX_KEY, { index: filteredList.indexOf(log) }); stopAutoScroll() } const _rowRenderer = ({ index, key, parent, style }: any) => { const item = filteredList[index]; return ( {/* @ts-ignore */} {({ measure }: any) => ( showDetails(item)} recalcHeight={() => { measure(); (_list as any).current.recomputeRowHeights(index); }} /> )} ); } return ( {/* @ts-ignore */}
Console
{/* @ts-ignore */}
{/* @ts-ignore */} No Data } size="small" show={filteredList.length === 0} > {/* @ts-ignore */} {({ height, width }: any) => ( // @ts-ignore )} {/* @ts-ignore */}
); } export default observer(ConsolePanel);