replayView.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import React, {useEffect, useRef, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import debounce from 'lodash/debounce';
  4. import {Panel, PanelBody, PanelHeader as _PanelHeader} from 'sentry/components/panels';
  5. import HorizontalMouseTracking from 'sentry/components/replays/player/horizontalMouseTracking';
  6. import {PlayerScrubber} from 'sentry/components/replays/player/scrubber';
  7. import ReplayController from 'sentry/components/replays/replayController';
  8. import ReplayCurrentUrl from 'sentry/components/replays/replayCurrentUrl';
  9. import ReplayPlayer from 'sentry/components/replays/replayPlayer';
  10. import space from 'sentry/styles/space';
  11. // How much to reveal under the player, so people can see the 'pagefold' and
  12. // know that they can scroll the page.
  13. const BOTTOM_REVEAL_PIXELS = 70;
  14. const SCREEN_HEIGHT_DIVISOR = 2;
  15. type Props = {
  16. isFullscreen: boolean;
  17. toggleFullscreen: () => void;
  18. };
  19. function ReplayView({isFullscreen, toggleFullscreen}: Props) {
  20. const containerRef = useRef<HTMLDivElement>(null);
  21. const playerRef = useRef<HTMLDivElement>(null);
  22. const [windowInnerHeight, setWindowInnerHeight] = useState(window.innerHeight);
  23. const [playerHeight, setPlayerHeight] = useState(0);
  24. useEffect(() => {
  25. const onResize = debounce(() => {
  26. setWindowInnerHeight(window.innerHeight);
  27. });
  28. window.addEventListener('resize', onResize);
  29. return () => {
  30. window.removeEventListener('resize', onResize);
  31. };
  32. }, []);
  33. useEffect(() => {
  34. const containerBottom =
  35. (containerRef.current?.offsetTop || 0) + (containerRef.current?.offsetHeight || 0);
  36. const playerOffsetHeight = playerRef.current?.offsetHeight || 0;
  37. const calc =
  38. windowInnerHeight - (containerBottom - playerOffsetHeight) - BOTTOM_REVEAL_PIXELS;
  39. setPlayerHeight(Math.max(200, calc / SCREEN_HEIGHT_DIVISOR));
  40. }, [windowInnerHeight]);
  41. return (
  42. <PanelNoMargin ref={containerRef} isFullscreen={isFullscreen}>
  43. <PanelHeader>
  44. <ReplayCurrentUrl />
  45. </PanelHeader>
  46. <PanelHeader ref={playerRef} disablePadding noBorder>
  47. <ReplayPlayer height={isFullscreen ? Infinity : playerHeight} />
  48. </PanelHeader>
  49. <HorizontalMouseTracking>
  50. <PlayerScrubber />
  51. </HorizontalMouseTracking>
  52. <ReplayControllerWrapper>
  53. <ReplayController toggleFullscreen={toggleFullscreen} />
  54. </ReplayControllerWrapper>
  55. </PanelNoMargin>
  56. );
  57. }
  58. const ReplayControllerWrapper = styled(PanelBody)`
  59. padding: ${space(1)};
  60. `;
  61. const PanelNoMargin = styled(Panel)<{isFullscreen: boolean}>`
  62. margin-bottom: 0;
  63. ${p =>
  64. p.isFullscreen
  65. ? `height: 100%;
  66. display: grid;
  67. grid-template-rows: auto 1fr auto;
  68. `
  69. : ''}
  70. `;
  71. const PanelHeader = styled(_PanelHeader)<{noBorder?: boolean}>`
  72. display: block;
  73. padding: 0;
  74. ${p => (p.noBorder ? 'border-bottom: none;' : '')}
  75. `;
  76. export default ReplayView;