details.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import React from 'react';
  2. import styled from '@emotion/styled';
  3. import DetailedError from 'sentry/components/errors/detailedError';
  4. import NotFound from 'sentry/components/errors/notFound';
  5. import * as Layout from 'sentry/components/layouts/thirds';
  6. import ReplayTimeline from 'sentry/components/replays/breadcrumbs/replayTimeline';
  7. import {Provider as ReplayContextProvider} from 'sentry/components/replays/replayContext';
  8. import ReplayView from 'sentry/components/replays/replayView';
  9. import {t} from 'sentry/locale';
  10. import {PageContent} from 'sentry/styles/organization';
  11. import space from 'sentry/styles/space';
  12. import useFullscreen from 'sentry/utils/replays/hooks/useFullscreen';
  13. import useReplayData from 'sentry/utils/replays/hooks/useReplayData';
  14. import {useRouteContext} from 'sentry/utils/useRouteContext';
  15. import DetailLayout from './detail/detailLayout';
  16. import FocusArea from './detail/focusArea';
  17. import FocusTabs from './detail/focusTabs';
  18. import UserActionsNavigator from './detail/userActionsNavigator';
  19. function ReplayDetails() {
  20. const {
  21. location,
  22. params: {eventSlug, orgId},
  23. } = useRouteContext();
  24. const {
  25. t: initialTimeOffset, // Time, in seconds, where the video should start
  26. } = location.query;
  27. const {fetchError, fetching, onRetry, replay} = useReplayData({
  28. eventSlug,
  29. orgId,
  30. });
  31. const {ref: fullscreenRef, isFullscreen, toggle: toggleFullscreen} = useFullscreen();
  32. if (!fetching && !replay) {
  33. // TODO(replay): Give the user more details when errors happen
  34. console.log({fetching, fetchError}); // eslint-disable-line no-console
  35. return (
  36. <DetailLayout orgId={orgId}>
  37. <PageContent>
  38. <NotFound />
  39. </PageContent>
  40. </DetailLayout>
  41. );
  42. }
  43. if (!fetching && replay && replay.getRRWebEvents().length < 2) {
  44. return (
  45. <DetailLayout event={replay.getEvent()} orgId={orgId}>
  46. <DetailedError
  47. onRetry={onRetry}
  48. hideSupportLinks
  49. heading={t('Expected two or more replay events')}
  50. message={
  51. <React.Fragment>
  52. <p>{t('This Replay may not have captured any user actions.')}</p>
  53. <p>
  54. {t(
  55. 'Or there may be an issue loading the actions from the server, click to try loading the Replay again.'
  56. )}
  57. </p>
  58. </React.Fragment>
  59. }
  60. />
  61. </DetailLayout>
  62. );
  63. }
  64. return (
  65. <ReplayContextProvider replay={replay} initialTimeOffset={initialTimeOffset}>
  66. <DetailLayout
  67. event={replay?.getEvent()}
  68. orgId={orgId}
  69. crumbs={replay?.getRawCrumbs()}
  70. >
  71. <Layout.Body>
  72. <Layout.Main ref={fullscreenRef}>
  73. <ReplayView toggleFullscreen={toggleFullscreen} isFullscreen={isFullscreen} />
  74. </Layout.Main>
  75. <Layout.Side>
  76. <UserActionsNavigator
  77. crumbs={replay?.getRawCrumbs()}
  78. event={replay?.getEvent()}
  79. />
  80. </Layout.Side>
  81. <StickyMain fullWidth>
  82. <ReplayTimeline />
  83. <FocusTabs />
  84. </StickyMain>
  85. <Layout.Main fullWidth>
  86. <FocusArea replay={replay} />
  87. </Layout.Main>
  88. </Layout.Body>
  89. </DetailLayout>
  90. </ReplayContextProvider>
  91. );
  92. }
  93. const StickyMain = styled(Layout.Main)`
  94. position: sticky;
  95. top: 0;
  96. z-index: ${p => p.theme.zIndex.header};
  97. /* Make this component full-bleed, so the background covers everything underneath it */
  98. margin: -${space(1.5)} -${space(4)} -${space(3)};
  99. padding: ${space(1.5)} ${space(4)} 0;
  100. max-width: none;
  101. background: ${p => p.theme.background};
  102. `;
  103. export default ReplayDetails;