replaysList.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {Fragment, useMemo} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import {Location} from 'history';
  4. import Pagination from 'sentry/components/pagination';
  5. import type {Organization} from 'sentry/types';
  6. import {trackAnalytics} from 'sentry/utils/analytics';
  7. import EventView from 'sentry/utils/discover/eventView';
  8. import {decodeScalar} from 'sentry/utils/queryString';
  9. import {DEFAULT_SORT} from 'sentry/utils/replays/fetchReplayList';
  10. import useReplayList from 'sentry/utils/replays/hooks/useReplayList';
  11. import {useHaveSelectedProjectsSentAnyReplayEvents} from 'sentry/utils/replays/hooks/useReplayOnboarding';
  12. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  13. import {useLocation} from 'sentry/utils/useLocation';
  14. import useOrganization from 'sentry/utils/useOrganization';
  15. import ReplayOnboardingPanel from 'sentry/views/replays/list/replayOnboardingPanel';
  16. import {ReplaySearchAlert} from 'sentry/views/replays/list/replaySearchAlert';
  17. import ReplayTable from 'sentry/views/replays/replayTable';
  18. import {ReplayColumns} from 'sentry/views/replays/replayTable/types';
  19. import type {ReplayListLocationQuery} from 'sentry/views/replays/types';
  20. import {REPLAY_LIST_FIELDS} from 'sentry/views/replays/types';
  21. function ReplaysList() {
  22. const location = useLocation<ReplayListLocationQuery>();
  23. const organization = useOrganization();
  24. const eventView = useMemo(() => {
  25. const query = decodeScalar(location.query.query, '');
  26. const conditions = new MutableSearch(query);
  27. return EventView.fromNewQueryWithLocation(
  28. {
  29. id: '',
  30. name: '',
  31. version: 2,
  32. fields: REPLAY_LIST_FIELDS,
  33. projects: [],
  34. query: conditions.formatString(),
  35. orderby: decodeScalar(location.query.sort, DEFAULT_SORT),
  36. },
  37. location
  38. );
  39. }, [location]);
  40. const hasSessionReplay = organization.features.includes('session-replay');
  41. const {hasSentOneReplay, fetching} = useHaveSelectedProjectsSentAnyReplayEvents();
  42. return hasSessionReplay && !fetching && hasSentOneReplay ? (
  43. <ReplaysListTable
  44. eventView={eventView}
  45. location={location}
  46. organization={organization}
  47. />
  48. ) : (
  49. <ReplayOnboardingPanel />
  50. );
  51. }
  52. function ReplaysListTable({
  53. eventView,
  54. location,
  55. organization,
  56. }: {
  57. eventView: EventView;
  58. location: Location;
  59. organization: Organization;
  60. }) {
  61. const {replays, pageLinks, isFetching, fetchError} = useReplayList({
  62. eventView,
  63. location,
  64. organization,
  65. });
  66. return (
  67. <Fragment>
  68. <ReplaySearchAlert />
  69. <ReplayTable
  70. fetchError={fetchError}
  71. isFetching={isFetching}
  72. replays={replays}
  73. sort={eventView.sorts[0]}
  74. visibleColumns={[
  75. ReplayColumns.replay,
  76. ReplayColumns.os,
  77. ReplayColumns.browser,
  78. ReplayColumns.duration,
  79. ReplayColumns.countErrors,
  80. ReplayColumns.activity,
  81. ]}
  82. />
  83. <Pagination
  84. pageLinks={pageLinks}
  85. onCursor={(cursor, path, searchQuery) => {
  86. trackAnalytics('replay.list-paginated', {
  87. organization,
  88. direction: cursor?.endsWith(':1') ? 'prev' : 'next',
  89. });
  90. browserHistory.push({
  91. pathname: path,
  92. query: {...searchQuery, cursor},
  93. });
  94. }}
  95. />
  96. </Fragment>
  97. );
  98. }
  99. export default ReplaysList;