replays.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import {Fragment, useMemo} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import {useTheme} from '@emotion/react';
  4. import {Button} from 'sentry/components/button';
  5. import * as Layout from 'sentry/components/layouts/thirds';
  6. import Pagination from 'sentry/components/pagination';
  7. import {t} from 'sentry/locale';
  8. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  9. import EventView from 'sentry/utils/discover/eventView';
  10. import {decodeScalar} from 'sentry/utils/queryString';
  11. import {DEFAULT_SORT, REPLAY_LIST_FIELDS} from 'sentry/utils/replays/fetchReplayList';
  12. import useReplayList from 'sentry/utils/replays/hooks/useReplayList';
  13. import {useReplayOnboardingSidebarPanel} from 'sentry/utils/replays/hooks/useReplayOnboarding';
  14. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  15. import {useLocation} from 'sentry/utils/useLocation';
  16. import useMedia from 'sentry/utils/useMedia';
  17. import useOrganization from 'sentry/utils/useOrganization';
  18. import ReplaysFilters from 'sentry/views/replays/filters';
  19. import ReplayOnboardingPanel from 'sentry/views/replays/list/replayOnboardingPanel';
  20. import ReplayTable from 'sentry/views/replays/replayTable';
  21. import {ReplayColumns} from 'sentry/views/replays/replayTable/types';
  22. import type {ReplayListLocationQuery} from 'sentry/views/replays/types';
  23. function ReplaysList() {
  24. const location = useLocation<ReplayListLocationQuery>();
  25. const organization = useOrganization();
  26. const theme = useTheme();
  27. const hasRoomForColumns = useMedia(`(min-width: ${theme.breakpoints.small})`);
  28. const eventView = useMemo(() => {
  29. const query = decodeScalar(location.query.query, '');
  30. const conditions = new MutableSearch(query);
  31. return EventView.fromNewQueryWithLocation(
  32. {
  33. id: '',
  34. name: '',
  35. version: 2,
  36. fields: REPLAY_LIST_FIELDS,
  37. projects: [],
  38. query: conditions.formatString(),
  39. orderby: decodeScalar(location.query.sort, DEFAULT_SORT),
  40. },
  41. location
  42. );
  43. }, [location]);
  44. const {replays, pageLinks, isFetching, fetchError} = useReplayList({
  45. eventView,
  46. location,
  47. organization,
  48. });
  49. const {hasSentOneReplay, activateSidebar} = useReplayOnboardingSidebarPanel();
  50. return (
  51. <Layout.Body>
  52. <Layout.Main fullWidth>
  53. <ReplaysFilters />
  54. {hasSentOneReplay ? (
  55. <Fragment>
  56. <ReplayTable
  57. fetchError={fetchError}
  58. isFetching={isFetching}
  59. replays={replays}
  60. sort={eventView.sorts[0]}
  61. visibleColumns={[
  62. ReplayColumns.session,
  63. ...(hasRoomForColumns
  64. ? [ReplayColumns.projectId, ReplayColumns.startedAt]
  65. : []),
  66. ReplayColumns.duration,
  67. ReplayColumns.countErrors,
  68. ReplayColumns.activity,
  69. ]}
  70. />
  71. <Pagination
  72. pageLinks={pageLinks}
  73. onCursor={(cursor, path, searchQuery) => {
  74. trackAdvancedAnalyticsEvent('replay.list-paginated', {
  75. organization,
  76. direction: cursor?.endsWith(':1') ? 'prev' : 'next',
  77. });
  78. browserHistory.push({
  79. pathname: path,
  80. query: {...searchQuery, cursor},
  81. });
  82. }}
  83. />
  84. </Fragment>
  85. ) : (
  86. <ReplayOnboardingPanel>
  87. <Button onClick={activateSidebar} priority="primary">
  88. {t('Set Up Replays')}
  89. </Button>
  90. <Button
  91. href="https://docs.sentry.io/platforms/javascript/session-replay/"
  92. external
  93. >
  94. {t('Read Docs')}
  95. </Button>
  96. </ReplayOnboardingPanel>
  97. )}
  98. </Layout.Main>
  99. </Layout.Body>
  100. );
  101. }
  102. export default ReplaysList;