123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- import {Fragment, useEffect, useMemo} from 'react';
- import styled from '@emotion/styled';
- import * as Sentry from '@sentry/react';
- import Alert from 'sentry/components/alert';
- import _EventsRequest from 'sentry/components/charts/eventsRequest';
- import {getInterval} from 'sentry/components/charts/utils';
- import LoadingContainer from 'sentry/components/loading/loadingContainer';
- import {CHART_PALETTE} from 'sentry/constants/chartPalette';
- import {t} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import type {Series, SeriesDataUnit} from 'sentry/types/echarts';
- import {defined} from 'sentry/utils';
- import {tooltipFormatterUsingAggregateOutputType} from 'sentry/utils/discover/charts';
- import EventView from 'sentry/utils/discover/eventView';
- import {DiscoverDatasets} from 'sentry/utils/discover/types';
- import {MutableSearch} from 'sentry/utils/tokenizeSearch';
- import {useLocation} from 'sentry/utils/useLocation';
- import usePageFilters from 'sentry/utils/usePageFilters';
- import {formatVersion} from 'sentry/utils/versions/formatVersion';
- import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
- import MiniChartPanel from 'sentry/views/insights/common/components/miniChartPanel';
- import {useReleaseSelection} from 'sentry/views/insights/common/queries/useReleases';
- import {formatVersionAndCenterTruncate} from 'sentry/views/insights/common/utils/centerTruncate';
- import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/insights/common/utils/constants';
- import {appendReleaseFilters} from 'sentry/views/insights/common/utils/releaseComparison';
- import {useEventsStatsQuery} from 'sentry/views/insights/common/utils/useEventsStatsQuery';
- import useCrossPlatformProject from 'sentry/views/insights/mobile/common/queries/useCrossPlatformProject';
- import {ScreensBarChart} from 'sentry/views/insights/mobile/screenload/components/charts/screenBarChart';
- import {useTableQuery} from 'sentry/views/insights/mobile/screenload/components/tables/screensTable';
- import {
- CHART_TITLES,
- OUTPUT_TYPE,
- YAXIS_COLUMNS,
- } from 'sentry/views/insights/mobile/screenload/constants';
- import {transformDeviceClassEvents} from 'sentry/views/insights/mobile/screenload/utils';
- export enum YAxis {
- WARM_START = 0,
- COLD_START = 1,
- TTID = 2,
- TTFD = 3,
- SLOW_FRAME_RATE = 4,
- FROZEN_FRAME_RATE = 5,
- THROUGHPUT = 6,
- COUNT = 7,
- }
- type Props = {
- yAxes: YAxis[];
- additionalFilters?: string[];
- chartHeight?: number;
- };
- export function ScreenCharts({yAxes, additionalFilters}: Props) {
- const pageFilter = usePageFilters();
- const location = useLocation();
- const {isProjectCrossPlatform, selectedPlatform: platform} = useCrossPlatformProject();
- const yAxisCols = yAxes.map(val => YAXIS_COLUMNS[val]);
- const {
- primaryRelease,
- secondaryRelease,
- isLoading: isReleasesLoading,
- } = useReleaseSelection();
- const queryString = useMemo(() => {
- const query = new MutableSearch([
- 'event.type:transaction',
- 'transaction.op:ui.load',
- ...(additionalFilters ?? []),
- ]);
- if (isProjectCrossPlatform) {
- query.addFilterValue('os.name', platform);
- }
- return appendReleaseFilters(query, primaryRelease, secondaryRelease);
- }, [
- additionalFilters,
- isProjectCrossPlatform,
- platform,
- primaryRelease,
- secondaryRelease,
- ]);
- const {
- data: series,
- isPending: isSeriesLoading,
- error: seriesError,
- } = useEventsStatsQuery({
- eventView: EventView.fromNewQueryWithPageFilters(
- {
- name: '',
- fields: ['release', ...yAxisCols],
- topEvents: '2',
- yAxis: [...yAxisCols],
- query: queryString,
- dataset: DiscoverDatasets.METRICS,
- version: 2,
- interval: getInterval(
- pageFilter.selection.datetime,
- STARFISH_CHART_INTERVAL_FIDELITY
- ),
- },
- pageFilter.selection
- ),
- enabled: !isReleasesLoading,
- // TODO: Change referrer
- referrer: 'api.starfish.mobile-screen-series',
- initialData: {},
- });
- useEffect(() => {
- if (defined(primaryRelease) || isReleasesLoading) {
- return;
- }
- Sentry.captureException(new Error('Screen summary missing releases'));
- }, [primaryRelease, isReleasesLoading]);
- const transformedReleaseSeries: {
- [yAxisName: string]: {
- [releaseVersion: string]: Series;
- };
- } = {};
- yAxes.forEach(val => {
- transformedReleaseSeries[YAXIS_COLUMNS[val]] = {};
- });
- if (defined(series)) {
- Object.keys(series).forEach(release => {
- const isPrimary = release === primaryRelease;
- Object.keys(series[release]).forEach(yAxis => {
- const label = release;
- if (yAxis in transformedReleaseSeries) {
- const data =
- series[release][yAxis]?.data.map(datum => {
- return {
- name: datum[0] * 1000,
- value: datum[1][0].count,
- } as SeriesDataUnit;
- }) ?? [];
- const color = isPrimary ? CHART_PALETTE[3][0] : CHART_PALETTE[3][1];
- transformedReleaseSeries[yAxis][release] = {
- seriesName: formatVersion(label, true),
- color,
- data,
- };
- }
- });
- });
- }
- const {data: deviceClassEvents, isPending: isDeviceClassEventsLoading} = useTableQuery({
- eventView: EventView.fromNewQueryWithLocation(
- {
- name: '',
- fields: ['device.class', 'release', ...yAxisCols],
- orderby: yAxisCols[0],
- yAxis: yAxisCols,
- query: queryString,
- dataset: DiscoverDatasets.METRICS,
- version: 2,
- },
- location
- ),
- enabled: !isReleasesLoading,
- referrer: 'api.starfish.mobile-device-breakdown',
- });
- if (isReleasesLoading) {
- return <LoadingContainer />;
- }
- if (!defined(primaryRelease) && !isReleasesLoading) {
- return (
- <Alert type="warning" showIcon>
- {t('Invalid selection. Try a different release or date range.')}
- </Alert>
- );
- }
- const transformedEvents = transformDeviceClassEvents({
- yAxes,
- primaryRelease,
- secondaryRelease,
- data: deviceClassEvents,
- });
- function renderCharts() {
- return (
- <Fragment>
- <Container>
- <div>
- <StyledRow>
- <ChartsContainerItem key="deviceClass">
- <ScreensBarChart
- chartOptions={[
- {
- title: t('TTID by Device Class'),
- yAxis: YAXIS_COLUMNS[yAxes[0]],
- series: Object.values(transformedEvents[YAXIS_COLUMNS[yAxes[0]]]),
- xAxisLabel: ['high', 'medium', 'low', 'Unknown'],
- subtitle: primaryRelease
- ? t(
- '%s v. %s',
- formatVersionAndCenterTruncate(primaryRelease, 12),
- secondaryRelease
- ? formatVersionAndCenterTruncate(secondaryRelease, 12)
- : ''
- )
- : '',
- },
- ]}
- chartKey="spansChart"
- chartHeight={80}
- isLoading={isDeviceClassEventsLoading}
- />
- </ChartsContainerItem>
- <ChartsContainerItem key="xyz">
- <MiniChartPanel
- title={t('Average TTID')}
- subtitle={
- primaryRelease
- ? t(
- '%s v. %s',
- formatVersionAndCenterTruncate(primaryRelease, 12),
- secondaryRelease
- ? formatVersionAndCenterTruncate(secondaryRelease, 12)
- : ''
- )
- : ''
- }
- >
- <Chart
- height={80}
- data={Object.values(
- transformedReleaseSeries[YAXIS_COLUMNS[yAxes[0]]]
- )}
- loading={isSeriesLoading}
- grid={{
- left: '0',
- right: '0',
- top: '8px',
- bottom: '0',
- }}
- showLegend
- definedAxisTicks={2}
- type={ChartType.LINE}
- aggregateOutputFormat={OUTPUT_TYPE[YAxis.TTID]}
- tooltipFormatterOptions={{
- valueFormatter: value =>
- tooltipFormatterUsingAggregateOutputType(
- value,
- OUTPUT_TYPE[YAxis.TTID]
- ),
- }}
- error={seriesError}
- />
- </MiniChartPanel>
- </ChartsContainerItem>
- </StyledRow>
- <StyledRow>
- <ChartsContainerItem key="deviceClass">
- <ScreensBarChart
- chartOptions={[
- {
- title: t('TTFD by Device Class'),
- yAxis: YAXIS_COLUMNS[yAxes[1]],
- series: Object.values(transformedEvents[YAXIS_COLUMNS[yAxes[1]]]),
- xAxisLabel: ['high', 'medium', 'low', 'Unknown'],
- subtitle: primaryRelease
- ? t(
- '%s v. %s',
- formatVersionAndCenterTruncate(primaryRelease, 12),
- secondaryRelease
- ? formatVersionAndCenterTruncate(secondaryRelease, 12)
- : ''
- )
- : '',
- },
- ]}
- chartKey="spansChart"
- chartHeight={80}
- isLoading={isDeviceClassEventsLoading}
- />
- </ChartsContainerItem>
- <ChartsContainerItem key="xyz">
- <MiniChartPanel
- title={t('Average TTFD')}
- subtitle={
- primaryRelease
- ? t(
- '%s v. %s',
- formatVersionAndCenterTruncate(primaryRelease, 12),
- secondaryRelease
- ? formatVersionAndCenterTruncate(secondaryRelease, 12)
- : ''
- )
- : ''
- }
- >
- <Chart
- height={80}
- data={Object.values(
- transformedReleaseSeries[YAXIS_COLUMNS[yAxes[1]]]
- )}
- loading={isSeriesLoading}
- grid={{
- left: '0',
- right: '0',
- top: '8px',
- bottom: '0',
- }}
- showLegend
- definedAxisTicks={2}
- type={ChartType.LINE}
- aggregateOutputFormat={OUTPUT_TYPE[YAxis.TTFD]}
- tooltipFormatterOptions={{
- valueFormatter: value =>
- tooltipFormatterUsingAggregateOutputType(
- value,
- OUTPUT_TYPE[YAxis.TTFD]
- ),
- }}
- error={seriesError}
- />
- </MiniChartPanel>
- </ChartsContainerItem>
- </StyledRow>
- </div>
- <ChartsContainerItem key="xyz">
- <MiniChartPanel
- title={CHART_TITLES[YAxis.COUNT]}
- subtitle={
- primaryRelease
- ? t(
- '%s v. %s',
- formatVersionAndCenterTruncate(primaryRelease, 12),
- secondaryRelease
- ? formatVersionAndCenterTruncate(secondaryRelease, 12)
- : ''
- )
- : ''
- }
- >
- <Chart
- data={Object.values(transformedReleaseSeries[YAXIS_COLUMNS[yAxes[2]]])}
- height={245}
- loading={isSeriesLoading}
- grid={{
- left: '0',
- right: '0',
- top: '8px',
- bottom: '0',
- }}
- showLegend
- definedAxisTicks={2}
- type={ChartType.LINE}
- aggregateOutputFormat={OUTPUT_TYPE[YAxis.COUNT]}
- tooltipFormatterOptions={{
- valueFormatter: value =>
- tooltipFormatterUsingAggregateOutputType(
- value,
- OUTPUT_TYPE[YAxis.COUNT]
- ),
- }}
- error={seriesError}
- />
- </MiniChartPanel>
- </ChartsContainerItem>
- </Container>
- </Fragment>
- );
- }
- return <div data-test-id="starfish-mobile-view">{renderCharts()}</div>;
- }
- const StyledRow = styled('div')`
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- grid-column-gap: ${space(2)};
- `;
- const ChartsContainerItem = styled('div')`
- flex: 1;
- `;
- export const Spacer = styled('div')`
- margin-top: ${space(3)};
- `;
- const Container = styled('div')`
- display: grid;
- grid-template-columns: 2fr 1fr;
- grid-column-gap: ${space(2)};
- `;
|