uiScreens.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import {useTheme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import Alert from 'sentry/components/alert';
  4. import SearchBar from 'sentry/components/performance/searchBar';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import type {NewQuery} from 'sentry/types/organization';
  8. import {defined} from 'sentry/utils';
  9. import EventView from 'sentry/utils/discover/eventView';
  10. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  11. import {decodeScalar} from 'sentry/utils/queryString';
  12. import {escapeFilterValue, MutableSearch} from 'sentry/utils/tokenizeSearch';
  13. import {useLocation} from 'sentry/utils/useLocation';
  14. import useOrganization from 'sentry/utils/useOrganization';
  15. import usePageFilters from 'sentry/utils/usePageFilters';
  16. import useRouter from 'sentry/utils/useRouter';
  17. import {useReleaseSelection} from 'sentry/views/insights/common/queries/useReleases';
  18. import {appendReleaseFilters} from 'sentry/views/insights/common/utils/releaseComparison';
  19. import {TOP_SCREENS} from 'sentry/views/insights/mobile/constants';
  20. import {getFreeTextFromQuery} from 'sentry/views/insights/mobile/screenload/components/screensView';
  21. import {useTableQuery} from 'sentry/views/insights/mobile/screenload/components/tables/screensTable';
  22. import {YAxis, YAXIS_COLUMNS} from 'sentry/views/insights/mobile/screenload/constants';
  23. import {transformReleaseEvents} from 'sentry/views/insights/mobile/screenload/utils';
  24. import {TopScreensChart} from 'sentry/views/insights/mobile/ui/components/charts/topScreensChart';
  25. import {UIScreensTable} from 'sentry/views/insights/mobile/ui/components/tables/uiScreensTable';
  26. import {Referrer} from 'sentry/views/insights/mobile/ui/referrers';
  27. import {SpanMetricsField} from 'sentry/views/insights/types';
  28. import {prepareQueryForLandingPage} from 'sentry/views/performance/data';
  29. import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
  30. const Y_AXES = [YAxis.SLOW_FRAMES, YAxis.FROZEN_FRAMES, YAxis.FRAMES_DELAY];
  31. const Y_AXIS_COLUMNS = [
  32. 'avg(mobile.slow_frames)',
  33. 'avg(mobile.frozen_frames)',
  34. 'avg(mobile.frames_delay)',
  35. ];
  36. export function UIScreens() {
  37. const theme = useTheme();
  38. const router = useRouter();
  39. const {selection} = usePageFilters();
  40. const location = useLocation();
  41. const organization = useOrganization();
  42. const {query: locationQuery} = location;
  43. const {
  44. primaryRelease,
  45. secondaryRelease,
  46. isLoading: isReleasesLoading,
  47. } = useReleaseSelection();
  48. const query = new MutableSearch(['transaction.op:ui.load']);
  49. const searchQuery = decodeScalar(locationQuery.query, '');
  50. if (searchQuery) {
  51. query.addStringFilter(prepareQueryForLandingPage(searchQuery, false));
  52. }
  53. const queryString = appendReleaseFilters(query, primaryRelease, secondaryRelease);
  54. // TODO: Replace with a default sort on the count column when added
  55. const orderby = decodeScalar(locationQuery.sort, '');
  56. const newQuery: NewQuery = {
  57. name: '',
  58. fields: [
  59. SpanMetricsField.PROJECT_ID,
  60. 'transaction',
  61. `division_if(mobile.slow_frames,mobile.total_frames,release,${primaryRelease})`,
  62. `division_if(mobile.slow_frames,mobile.total_frames,release,${secondaryRelease})`,
  63. `division_if(mobile.frozen_frames,mobile.total_frames,release,${primaryRelease})`,
  64. `division_if(mobile.frozen_frames,mobile.total_frames,release,${secondaryRelease})`,
  65. `avg_if(mobile.frames_delay,release,${primaryRelease})`,
  66. `avg_if(mobile.frames_delay,release,${secondaryRelease})`,
  67. `avg_compare(mobile.frames_delay,release,${primaryRelease},${secondaryRelease})`,
  68. ],
  69. query: queryString,
  70. dataset: DiscoverDatasets.SPANS_METRICS,
  71. version: 2,
  72. projects: selection.projects,
  73. };
  74. newQuery.orderby = orderby;
  75. const tableEventView = EventView.fromNewQueryWithLocation(newQuery, location);
  76. const {
  77. data: topTransactionsData,
  78. isPending: topTransactionsLoading,
  79. pageLinks,
  80. } = useTableQuery({
  81. eventView: tableEventView,
  82. enabled: !isReleasesLoading,
  83. referrer: Referrer.OVERVIEW_SCREENS_TABLE,
  84. });
  85. const topTransactions =
  86. topTransactionsData?.data?.slice(0, 5).map(datum => datum.transaction as string) ??
  87. [];
  88. // TODO: Fill with transaction.op filter
  89. const topEventsQuery = new MutableSearch([]);
  90. const topEventsQueryString = `${appendReleaseFilters(
  91. topEventsQuery,
  92. primaryRelease,
  93. secondaryRelease
  94. )} ${
  95. topTransactions.length > 0
  96. ? escapeFilterValue(
  97. `transaction:[${topTransactions.map(name => `"${name}"`).join()}]`
  98. )
  99. : ''
  100. }`.trim();
  101. const {data: releaseEvents, isPending: isReleaseEventsLoading} = useTableQuery({
  102. eventView: EventView.fromNewQueryWithPageFilters(
  103. {
  104. name: '',
  105. fields: ['transaction', 'release', ...Y_AXIS_COLUMNS],
  106. yAxis: Y_AXIS_COLUMNS,
  107. query: topEventsQueryString,
  108. dataset: DiscoverDatasets.SPANS_METRICS,
  109. version: 2,
  110. },
  111. selection
  112. ),
  113. enabled: !topTransactionsLoading,
  114. referrer: Referrer.MOBILE_UI_BAR_CHART,
  115. });
  116. if (!defined(primaryRelease) && !isReleasesLoading) {
  117. return (
  118. <Alert type="warning" showIcon>
  119. {t(
  120. 'No screens found on recent releases. Please try a single iOS or Android project, a single environment or a smaller date range.'
  121. )}
  122. </Alert>
  123. );
  124. }
  125. const tableSearchFilters = new MutableSearch(['transaction.op:ui.load']);
  126. const derivedQuery = getTransactionSearchQuery(location, tableEventView.query);
  127. const transformedReleaseEvents = transformReleaseEvents({
  128. yAxes: Y_AXES,
  129. primaryRelease,
  130. secondaryRelease,
  131. colorPalette: theme.charts.getColorPalette(TOP_SCREENS - 2),
  132. releaseEvents,
  133. topTransactions,
  134. });
  135. return (
  136. <Layout>
  137. <ChartContainer>
  138. <TopScreensChart
  139. yAxis={YAXIS_COLUMNS[YAxis.SLOW_FRAMES]}
  140. isLoading={isReleaseEventsLoading}
  141. chartHeight={200}
  142. topTransactions={topTransactions}
  143. transformedReleaseEvents={transformedReleaseEvents}
  144. />
  145. <TopScreensChart
  146. yAxis={YAXIS_COLUMNS[YAxis.FROZEN_FRAMES]}
  147. isLoading={isReleaseEventsLoading}
  148. chartHeight={200}
  149. topTransactions={topTransactions}
  150. transformedReleaseEvents={transformedReleaseEvents}
  151. />
  152. <TopScreensChart
  153. yAxis={YAXIS_COLUMNS[YAxis.FRAMES_DELAY]}
  154. isLoading={isReleaseEventsLoading}
  155. chartHeight={200}
  156. topTransactions={topTransactions}
  157. transformedReleaseEvents={transformedReleaseEvents}
  158. />
  159. </ChartContainer>
  160. <SearchBar
  161. eventView={tableEventView}
  162. onSearch={search => {
  163. router.push({
  164. pathname: router.location.pathname,
  165. query: {
  166. ...location.query,
  167. cursor: undefined,
  168. query: String(search).trim() || undefined,
  169. },
  170. });
  171. }}
  172. organization={organization}
  173. query={getFreeTextFromQuery(derivedQuery)}
  174. placeholder={t('Search for Screen')}
  175. additionalConditions={
  176. new MutableSearch(
  177. appendReleaseFilters(tableSearchFilters, primaryRelease, secondaryRelease)
  178. )
  179. }
  180. />
  181. <UIScreensTable
  182. eventView={tableEventView}
  183. data={topTransactionsData}
  184. isLoading={topTransactionsLoading}
  185. pageLinks={pageLinks}
  186. />
  187. </Layout>
  188. );
  189. }
  190. const Layout = styled('div')`
  191. display: flex;
  192. flex-direction: column;
  193. gap: ${space(1)};
  194. `;
  195. const ChartContainer = styled('div')`
  196. display: grid;
  197. grid-template-columns: 33% 33% 33%;
  198. gap: ${space(1)};
  199. `;