import {useEffect, useMemo, useRef, useState} from 'react'; import type {ListRowProps} from 'react-virtualized'; import {AutoSizer, CellMeasurer, List as ReactVirtualizedList} from 'react-virtualized'; import Placeholder from 'sentry/components/placeholder'; import JumpButtons from 'sentry/components/replays/jumpButtons'; import {useReplayContext} from 'sentry/components/replays/replayContext'; import useJumpButtons from 'sentry/components/replays/useJumpButtons'; import {t} from 'sentry/locale'; import useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers'; import useExtractedDomNodes from 'sentry/utils/replays/hooks/useExtractedDomNodes'; import useVirtualizedInspector from 'sentry/views/replays/detail//useVirtualizedInspector'; import BreadcrumbFilters from 'sentry/views/replays/detail/breadcrumbs/breadcrumbFilters'; import BreadcrumbRow from 'sentry/views/replays/detail/breadcrumbs/breadcrumbRow'; import useBreadcrumbFilters from 'sentry/views/replays/detail/breadcrumbs/useBreadcrumbFilters'; import useScrollToCurrentItem from 'sentry/views/replays/detail/breadcrumbs/useScrollToCurrentItem'; import FilterLoadingIndicator from 'sentry/views/replays/detail/filterLoadingIndicator'; import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight'; import NoRowRenderer from 'sentry/views/replays/detail/noRowRenderer'; import TabItemContainer from 'sentry/views/replays/detail/tabItemContainer'; import useVirtualizedList from 'sentry/views/replays/detail/useVirtualizedList'; // Ensure this object is created once as it is an input to // `useVirtualizedList`'s memoization const cellMeasurer = { fixedWidth: true, minHeight: 53, }; function Breadcrumbs() { const {currentTime, replay} = useReplayContext(); const {onClickTimestamp} = useCrumbHandlers(); const {data: frameToExtraction, isFetching: isFetchingExtractions} = useExtractedDomNodes({replay}); const startTimestampMs = replay?.getStartTimestampMs() ?? 0; const frames = replay?.getChapterFrames(); const [scrollToRow, setScrollToRow] = useState(undefined); const filterProps = useBreadcrumbFilters({frames: frames || []}); const {expandPathsRef, items, searchTerm, setSearchTerm} = filterProps; const clearSearchTerm = () => setSearchTerm(''); const listRef = useRef(null); const deps = useMemo(() => [items, searchTerm], [items, searchTerm]); const {cache, updateList} = useVirtualizedList({ cellMeasurer, ref: listRef, deps, }); const {handleDimensionChange: handleInspectorExpanded} = useVirtualizedInspector({ cache, listRef, expandPathsRef, }); const { handleClick: onClickToJump, onRowsRendered, showJumpDownButton, showJumpUpButton, } = useJumpButtons({ currentTime, frames: items, isTable: false, setScrollToRow, }); useScrollToCurrentItem({ frames, ref: listRef, }); // Need to refresh the item dimensions as DOM data gets loaded useEffect(() => { if (!isFetchingExtractions) { updateList(); } }, [isFetchingExtractions, updateList]); const renderRow = ({index, key, style, parent}: ListRowProps) => { const item = (items || [])[index]; return ( { onClickTimestamp(item); }} onInspectorExpanded={handleInspectorExpanded} /> ); }; return ( {frames ? ( {({height, width}) => ( ( {t('No breadcrumbs recorded')} )} onRowsRendered={onRowsRendered} onScroll={() => { if (scrollToRow !== undefined) { setScrollToRow(undefined); } }} overscanRowCount={5} ref={listRef} rowCount={items.length} rowHeight={cache.rowHeight} rowRenderer={renderRow} scrollToIndex={scrollToRow} width={width} /> )} ) : ( )} {items?.length ? ( ) : null} ); } export default Breadcrumbs;