details.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import {Fragment} from 'react';
  2. import type {RouteComponentProps} from 'react-router';
  3. import DetailedError from 'sentry/components/errors/detailedError';
  4. import NotFound from 'sentry/components/errors/notFound';
  5. import List from 'sentry/components/list';
  6. import ListItem from 'sentry/components/list/listItem';
  7. import {
  8. Provider as ReplayContextProvider,
  9. useReplayContext,
  10. } from 'sentry/components/replays/replayContext';
  11. import {t} from 'sentry/locale';
  12. import {PageContent} from 'sentry/styles/organization';
  13. import useReplayData from 'sentry/utils/replays/hooks/useReplayData';
  14. import useReplayLayout from 'sentry/utils/replays/hooks/useReplayLayout';
  15. import useReplayPageview from 'sentry/utils/replays/hooks/useReplayPageview';
  16. import Layout from 'sentry/views/replays/detail/layout';
  17. import Page from 'sentry/views/replays/detail/page';
  18. import type {ReplayRecord} from 'sentry/views/replays/types';
  19. import {getInitialTimeOffset} from 'sentry/views/replays/utils';
  20. type Props = RouteComponentProps<
  21. {orgId: string; replaySlug: string},
  22. {},
  23. any,
  24. {event_t: string; t: number}
  25. >;
  26. function ReplayDetails({
  27. location: {
  28. query: {
  29. event_t: eventTimestamp, // Timestamp of the event or activity that was selected
  30. t: initialTimeOffset, // Time, in seconds, where the video should start
  31. },
  32. },
  33. params: {orgId: orgSlug, replaySlug},
  34. }: Props) {
  35. useReplayPageview();
  36. const {fetching, onRetry, replay, replayRecord, fetchError} = useReplayData({
  37. replaySlug,
  38. orgSlug,
  39. });
  40. const startTimestampMs = replayRecord?.startedAt.getTime() ?? 0;
  41. if (!fetching && !replay && fetchError) {
  42. if (fetchError.statusText === 'Not Found') {
  43. return (
  44. <Page orgSlug={orgSlug} replayRecord={replayRecord}>
  45. <PageContent>
  46. <NotFound />
  47. </PageContent>
  48. </Page>
  49. );
  50. }
  51. const reasons = [
  52. t('The Replay is still processing and is on its way'),
  53. t('There is an internal systems error or active issue'),
  54. ];
  55. return (
  56. <Page orgSlug={orgSlug} replayRecord={replayRecord}>
  57. <PageContent>
  58. <DetailedError
  59. onRetry={onRetry}
  60. hideSupportLinks
  61. heading={t('There was an error while fetching this Replay')}
  62. message={
  63. <Fragment>
  64. <p>{t('This could be due to a couple of reasons:')}</p>
  65. <List symbol="bullet">
  66. {reasons.map((reason, i) => (
  67. <ListItem key={i}>{reason}</ListItem>
  68. ))}
  69. </List>
  70. </Fragment>
  71. }
  72. />
  73. </PageContent>
  74. </Page>
  75. );
  76. }
  77. if (!fetching && replay && replay.getRRWebEvents().length < 2) {
  78. return (
  79. <Page orgSlug={orgSlug} replayRecord={replayRecord}>
  80. <DetailedError
  81. hideSupportLinks
  82. heading={t('Expected two or more replay events')}
  83. message={
  84. <Fragment>
  85. <p>{t('This Replay may not have captured any user actions.')}</p>
  86. <p>
  87. {t(
  88. 'Or there may be an issue loading the actions from the server, click to try loading the Replay again.'
  89. )}
  90. </p>
  91. </Fragment>
  92. }
  93. />
  94. </Page>
  95. );
  96. }
  97. return (
  98. <ReplayContextProvider
  99. replay={replay}
  100. initialTimeOffset={getInitialTimeOffset({
  101. eventTimestamp,
  102. initialTimeOffset,
  103. startTimestampMs,
  104. })}
  105. >
  106. <LoadedDetails orgSlug={orgSlug} replayRecord={replayRecord} />
  107. </ReplayContextProvider>
  108. );
  109. }
  110. function LoadedDetails({
  111. orgSlug,
  112. replayRecord,
  113. }: {
  114. orgSlug: string;
  115. replayRecord: ReplayRecord | undefined;
  116. }) {
  117. const {getLayout} = useReplayLayout();
  118. const {replay} = useReplayContext();
  119. return (
  120. <Page orgSlug={orgSlug} crumbs={replay?.getRawCrumbs()} replayRecord={replayRecord}>
  121. <Layout layout={getLayout()} />
  122. </Page>
  123. );
  124. }
  125. export default ReplayDetails;