staticReplayPreview.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {type ComponentProps, Fragment, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {LinkButton} from 'sentry/components/button';
  4. import {Provider as ReplayContextProvider} from 'sentry/components/replays/replayContext';
  5. import ReplayPlayer from 'sentry/components/replays/replayPlayer';
  6. import ReplayProcessingError from 'sentry/components/replays/replayProcessingError';
  7. import {IconPlay} from 'sentry/icons';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
  11. import {TabKey} from 'sentry/utils/replays/hooks/useActiveReplayTab';
  12. import type ReplayReader from 'sentry/utils/replays/replayReader';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {useRoutes} from 'sentry/utils/useRoutes';
  15. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  16. type StaticReplayPreviewProps = {
  17. analyticsContext: string;
  18. initialTimeOffsetMs: number;
  19. isFetching: boolean;
  20. replay: ReplayReader | null;
  21. replayId: string;
  22. focusTab?: TabKey;
  23. fullReplayButtonProps?: Partial<ComponentProps<typeof LinkButton>>;
  24. };
  25. export function StaticReplayPreview({
  26. analyticsContext,
  27. initialTimeOffsetMs,
  28. isFetching,
  29. focusTab,
  30. replayId,
  31. fullReplayButtonProps,
  32. replay,
  33. }: StaticReplayPreviewProps) {
  34. const organization = useOrganization();
  35. const routes = useRoutes();
  36. const fullReplayUrl = {
  37. pathname: `/organizations/${organization.slug}/replays/${replayId}/`,
  38. query: {
  39. referrer: getRouteStringFromRoutes(routes),
  40. t_main: focusTab ?? TabKey.ERRORS,
  41. t: initialTimeOffsetMs / 1000,
  42. },
  43. };
  44. const offset = useMemo(
  45. () => ({
  46. offsetMs: initialTimeOffsetMs,
  47. }),
  48. [initialTimeOffsetMs]
  49. );
  50. return (
  51. <ReplayContextProvider
  52. isFetching={isFetching}
  53. replay={replay}
  54. initialTimeOffsetMs={offset}
  55. analyticsContext={analyticsContext}
  56. >
  57. <PlayerContainer data-test-id="player-container">
  58. {replay?.hasProcessingErrors() ? (
  59. <ReplayProcessingError processingErrors={replay.processingErrors()} />
  60. ) : (
  61. <Fragment>
  62. <StaticPanel>
  63. <ReplayPlayer isPreview />
  64. </StaticPanel>
  65. <CTAOverlay>
  66. <LinkButton
  67. {...fullReplayButtonProps}
  68. icon={<IconPlay />}
  69. priority="primary"
  70. to={fullReplayUrl}
  71. >
  72. {t('Open Replay')}
  73. </LinkButton>
  74. </CTAOverlay>
  75. </Fragment>
  76. )}
  77. </PlayerContainer>
  78. </ReplayContextProvider>
  79. );
  80. }
  81. const PlayerContainer = styled(FluidHeight)`
  82. position: relative;
  83. background: ${p => p.theme.background};
  84. gap: ${space(1)};
  85. max-height: 448px;
  86. `;
  87. const StaticPanel = styled(FluidHeight)`
  88. border: 1px solid ${p => p.theme.border};
  89. border-radius: ${p => p.theme.borderRadius};
  90. `;
  91. const CTAOverlay = styled('div')`
  92. position: absolute;
  93. width: 100%;
  94. height: 100%;
  95. display: flex;
  96. justify-content: center;
  97. align-items: center;
  98. background: rgba(255, 255, 255, 0.5);
  99. `;