content.tsx 5.2 KB

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