content.tsx 5.8 KB

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