content.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import {useEffect, useRef, useState} from 'react';
  2. import {browserHistory, InjectedRouter} from 'react-router';
  3. import * as Sentry from '@sentry/react';
  4. import {Location} from 'history';
  5. import isEqual from 'lodash/isEqual';
  6. import {loadOrganizationTags} from 'sentry/actionCreators/tags';
  7. import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
  8. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  9. import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
  10. import {t} from 'sentry/locale';
  11. import {PageFilters, Project} from 'sentry/types';
  12. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  13. import {
  14. canUseMetricsData,
  15. MEPState,
  16. METRIC_SEARCH_SETTING_PARAM,
  17. } from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  18. import {PerformanceEventViewProvider} from 'sentry/utils/performance/contexts/performanceEventViewContext';
  19. import useDisableRouteAnalytics from 'sentry/utils/routeAnalytics/useDisableRouteAnalytics';
  20. import useApi from 'sentry/utils/useApi';
  21. import useOrganization from 'sentry/utils/useOrganization';
  22. import usePrevious from 'sentry/utils/usePrevious';
  23. import useProjects from 'sentry/utils/useProjects';
  24. import withPageFilters from 'sentry/utils/withPageFilters';
  25. import {DEFAULT_STATS_PERIOD, generatePerformanceEventView} from './data';
  26. import {PerformanceLanding} from './landing';
  27. import {
  28. addRoutePerformanceContext,
  29. getSelectedProjectPlatforms,
  30. handleTrendsClick,
  31. } from './utils';
  32. type Props = {
  33. location: Location;
  34. router: InjectedRouter;
  35. selection: PageFilters;
  36. demoMode?: boolean;
  37. };
  38. type State = {
  39. error?: string;
  40. };
  41. function PerformanceContent({selection, location, demoMode, router}: Props) {
  42. const api = useApi();
  43. const organization = useOrganization();
  44. const {projects} = useProjects();
  45. const mounted = useRef(false);
  46. const previousDateTime = usePrevious(selection.datetime);
  47. // TODO: remove the trackAdvancedAnalyticsEvent call and use useRouteAnalyticsParams instead
  48. useDisableRouteAnalytics();
  49. const [state, setState] = useState<State>({error: undefined});
  50. const withStaticFilters = canUseMetricsData(organization);
  51. const eventView = generatePerformanceEventView(location, projects, {
  52. withStaticFilters,
  53. });
  54. function getOnboardingProject(): Project | undefined {
  55. // XXX used by getsentry to bypass onboarding for the upsell demo state.
  56. if (demoMode) {
  57. return undefined;
  58. }
  59. if (projects.length === 0) {
  60. return undefined;
  61. }
  62. // Current selection is 'my projects' or 'all projects'
  63. if (eventView.project.length === 0 || eventView.project[0] === ALL_ACCESS_PROJECTS) {
  64. const filtered = projects.filter(p => p.firstTransactionEvent === false);
  65. if (filtered.length === projects.length) {
  66. return filtered[0];
  67. }
  68. }
  69. // Any other subset of projects.
  70. const filtered = projects.filter(
  71. p =>
  72. eventView.project.includes(parseInt(p.id, 10)) &&
  73. p.firstTransactionEvent === false
  74. );
  75. if (filtered.length === eventView.project.length) {
  76. return filtered[0];
  77. }
  78. return undefined;
  79. }
  80. const onboardingProject = getOnboardingProject();
  81. useEffect(() => {
  82. if (!mounted.current) {
  83. const selectedProjects = getSelectedProjectPlatforms(location, projects);
  84. trackAdvancedAnalyticsEvent('performance_views.overview.view', {
  85. organization,
  86. show_onboarding: onboardingProject !== undefined,
  87. project_platforms: selectedProjects,
  88. });
  89. loadOrganizationTags(api, organization.slug, selection);
  90. addRoutePerformanceContext(selection);
  91. mounted.current = true;
  92. return;
  93. }
  94. if (!isEqual(previousDateTime, selection.datetime)) {
  95. loadOrganizationTags(api, organization.slug, selection);
  96. addRoutePerformanceContext(selection);
  97. }
  98. }, [
  99. selection.datetime,
  100. previousDateTime,
  101. selection,
  102. api,
  103. organization,
  104. onboardingProject,
  105. location,
  106. projects,
  107. ]);
  108. function setError(newError?: string) {
  109. if (
  110. typeof newError === 'object' ||
  111. (Array.isArray(newError) && typeof newError[0] === 'object')
  112. ) {
  113. Sentry.withScope(scope => {
  114. scope.setExtra('error', newError);
  115. Sentry.captureException(new Error('setError failed with error type.'));
  116. });
  117. return;
  118. }
  119. setState({...state, error: newError});
  120. }
  121. function handleSearch(searchQuery: string, currentMEPState?: MEPState) {
  122. trackAdvancedAnalyticsEvent('performance_views.overview.search', {organization});
  123. browserHistory.push({
  124. pathname: location.pathname,
  125. query: {
  126. ...location.query,
  127. cursor: undefined,
  128. query: String(searchQuery).trim() || undefined,
  129. [METRIC_SEARCH_SETTING_PARAM]: currentMEPState,
  130. isDefaultQuery: false,
  131. },
  132. });
  133. }
  134. return (
  135. <SentryDocumentTitle title={t('Performance')} orgSlug={organization.slug}>
  136. <PerformanceEventViewProvider value={{eventView}}>
  137. <PageFiltersContainer
  138. defaultSelection={{
  139. datetime: {
  140. start: null,
  141. end: null,
  142. utc: false,
  143. period: DEFAULT_STATS_PERIOD,
  144. },
  145. }}
  146. >
  147. <PerformanceLanding
  148. router={router}
  149. eventView={eventView}
  150. setError={setError}
  151. handleSearch={handleSearch}
  152. handleTrendsClick={() =>
  153. handleTrendsClick({
  154. location,
  155. organization,
  156. projectPlatforms: getSelectedProjectPlatforms(location, projects),
  157. })
  158. }
  159. onboardingProject={onboardingProject}
  160. organization={organization}
  161. location={location}
  162. projects={projects}
  163. selection={selection}
  164. withStaticFilters={withStaticFilters}
  165. />
  166. </PageFiltersContainer>
  167. </PerformanceEventViewProvider>
  168. </SentryDocumentTitle>
  169. );
  170. }
  171. export default withPageFilters(PerformanceContent);