replayTimeline.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import {useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import Panel from 'sentry/components/panels/panel';
  4. import Placeholder from 'sentry/components/placeholder';
  5. import {
  6. MajorGridlines,
  7. MinorGridlines,
  8. } from 'sentry/components/replays/breadcrumbs/gridlines';
  9. import ReplayTimelineEvents from 'sentry/components/replays/breadcrumbs/replayTimelineEvents';
  10. import ReplayTimelineSpans from 'sentry/components/replays/breadcrumbs/replayTimelineSpans';
  11. import Stacked from 'sentry/components/replays/breadcrumbs/stacked';
  12. import {
  13. CompactTimelineScrubber,
  14. TimelineScrubber,
  15. } from 'sentry/components/replays/player/scrubber';
  16. import useScrubberMouseTracking from 'sentry/components/replays/player/useScrubberMouseTracking';
  17. import {useReplayContext} from 'sentry/components/replays/replayContext';
  18. import {divide} from 'sentry/components/replays/utils';
  19. import toPercent from 'sentry/utils/number/toPercent';
  20. import {useDimensions} from 'sentry/utils/useDimensions';
  21. import useOrganization from 'sentry/utils/useOrganization';
  22. type Props = {size: number};
  23. function ReplayTimeline({size}: Props) {
  24. const {replay, currentTime} = useReplayContext();
  25. const panelRef = useRef<HTMLDivElement>(null);
  26. const mouseTrackingProps = useScrubberMouseTracking({elem: panelRef});
  27. const panelWidth = useDimensions<HTMLDivElement>({elementRef: panelRef}).width;
  28. const stackedRef = useRef<HTMLDivElement>(null);
  29. const {width} = useDimensions<HTMLDivElement>({elementRef: stackedRef});
  30. const organization = useOrganization();
  31. const hasNewTimeline = organization.features.includes('session-replay-new-timeline');
  32. if (!replay) {
  33. return <Placeholder height={hasNewTimeline ? '20px' : '54px'} />;
  34. }
  35. const durationMs = replay.getDurationMs();
  36. const startTimestampMs = replay.getReplay().started_at.getTime();
  37. const chapterFrames = replay.getChapterFrames();
  38. const networkFrames = replay.getNetworkFrames();
  39. // start of the timeline is in the middle
  40. const initialTranslatePercentage = 50 / size;
  41. const translatePercentage = toPercent(
  42. initialTranslatePercentage -
  43. (currentTime > durationMs ? 1 : divide(currentTime, durationMs))
  44. );
  45. return hasNewTimeline ? (
  46. <VisiblePanel ref={panelRef} {...mouseTrackingProps}>
  47. <Stacked
  48. style={{
  49. width: `${size}%`,
  50. transform: `translate(${translatePercentage}, 0%)`,
  51. }}
  52. ref={stackedRef}
  53. >
  54. <MajorGridlines durationMs={durationMs} width={panelWidth} />
  55. <CompactTimelineScrubber />
  56. <TimelineEventsContainer>
  57. <ReplayTimelineEvents
  58. durationMs={durationMs}
  59. frames={chapterFrames}
  60. startTimestampMs={startTimestampMs}
  61. width={width}
  62. />
  63. </TimelineEventsContainer>
  64. </Stacked>
  65. </VisiblePanel>
  66. ) : (
  67. <PanelNoMargin ref={panelRef} {...mouseTrackingProps}>
  68. <Stacked ref={stackedRef}>
  69. <MinorGridlines durationMs={durationMs} width={width} />
  70. <MajorGridlines durationMs={durationMs} width={width} />
  71. <TimelineScrubber />
  72. <div style={{paddingTop: '36px'}}>
  73. <ReplayTimelineSpans
  74. durationMs={durationMs}
  75. frames={networkFrames}
  76. startTimestampMs={startTimestampMs}
  77. />
  78. </div>
  79. <div style={{paddingTop: '26px'}}>
  80. <ReplayTimelineEvents
  81. durationMs={durationMs}
  82. frames={chapterFrames}
  83. startTimestampMs={startTimestampMs}
  84. width={width}
  85. />
  86. </div>
  87. </Stacked>
  88. </PanelNoMargin>
  89. );
  90. }
  91. const PanelNoMargin = styled(Panel)`
  92. margin: 0;
  93. `;
  94. const VisiblePanel = styled(Panel)`
  95. margin: 0;
  96. border: 0;
  97. overflow: hidden;
  98. background: ${p => p.theme.translucentInnerBorder};
  99. `;
  100. const TimelineEventsContainer = styled('div')`
  101. padding-top: 10px;
  102. padding-bottom: 10px;
  103. `;
  104. export default ReplayTimeline;