replays.tsx 3.4 KB

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