github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/pages/ContinuousDiffView.tsx (about)

     1  import React, { useEffect } from 'react';
     2  import { useAppDispatch, useAppSelector } from '@webapp/redux/hooks';
     3  import Box from '@webapp/ui/Box';
     4  import {
     5    fetchDiffView,
     6    selectContinuousState,
     7    actions,
     8    fetchTagValues,
     9    selectQueries,
    10    selectTimelineSides,
    11    selectAnnotationsOrDefault,
    12  } from '@webapp/redux/reducers/continuous';
    13  import { FlamegraphRenderer } from '@pyroscope/flamegraph/src/FlamegraphRenderer';
    14  import usePopulateLeftRightQuery from '@webapp/hooks/populateLeftRightQuery.hook';
    15  import useTimelines, {
    16    leftColor,
    17    rightColor,
    18    selectionColor,
    19  } from '@webapp/hooks/timeline.hook';
    20  import useTimeZone from '@webapp/hooks/timeZone.hook';
    21  import useColorMode from '@webapp/hooks/colorMode.hook';
    22  import useTags from '@webapp/hooks/tags.hook';
    23  import Toolbar from '@webapp/components/Toolbar';
    24  import TagsBar from '@webapp/components/TagsBar';
    25  import TimelineChartWrapper from '@webapp/components/TimelineChart/TimelineChartWrapper';
    26  import SyncTimelines from '@webapp/components/TimelineChart/SyncTimelines';
    27  import useExportToFlamegraphDotCom from '@webapp/components/exportToFlamegraphDotCom.hook';
    28  import { LoadingOverlay } from '@webapp/ui/LoadingOverlay';
    29  import ExportData from '@webapp/components/ExportData';
    30  import ChartTitle from '@webapp/components/ChartTitle';
    31  import { isExportToFlamegraphDotComEnabled } from '@webapp/util/features';
    32  import PageTitle from '@webapp/components/PageTitle';
    33  import { formatTitle } from './formatTitle';
    34  import { isLoadingOrReloading } from './loading';
    35  
    36  function ComparisonDiffApp() {
    37    const dispatch = useAppDispatch();
    38    const { colorMode } = useColorMode();
    39    const {
    40      diffView,
    41      refreshToken,
    42      maxNodes,
    43      leftFrom,
    44      rightFrom,
    45      leftUntil,
    46      rightUntil,
    47    } = useAppSelector(selectContinuousState);
    48    const { leftQuery, rightQuery } = useAppSelector(selectQueries);
    49    const annotations = useAppSelector(selectAnnotationsOrDefault('diffView'));
    50  
    51    usePopulateLeftRightQuery();
    52    const { leftTags, rightTags } = useTags();
    53    const { leftTimeline, rightTimeline } = useTimelines();
    54  
    55    const timelines = useAppSelector(selectTimelineSides);
    56    const exportToFlamegraphDotComFn = useExportToFlamegraphDotCom(
    57      diffView.profile
    58    );
    59  
    60    const { offset } = useTimeZone();
    61    const timezone = offset === 0 ? 'utc' : 'browser';
    62  
    63    const isLoading = isLoadingOrReloading([
    64      diffView.type,
    65      timelines.left.type,
    66      timelines.right.type,
    67    ]);
    68  
    69    useEffect(() => {
    70      if (rightQuery && leftQuery) {
    71        const fetchData = dispatch(
    72          fetchDiffView({
    73            leftQuery,
    74            leftFrom,
    75            leftUntil,
    76  
    77            rightQuery,
    78            rightFrom,
    79            rightUntil,
    80          })
    81        );
    82        return fetchData.abort;
    83      }
    84      return undefined;
    85    }, [
    86      leftFrom,
    87      leftUntil,
    88      leftQuery,
    89      rightFrom,
    90      rightUntil,
    91      rightQuery,
    92      refreshToken,
    93      maxNodes,
    94    ]);
    95  
    96    const exportData = diffView.profile && (
    97      <ExportData
    98        flamebearer={diffView.profile}
    99        exportJSON
   100        exportPNG
   101        // disable this until we fix it
   102        //      exportHTML
   103        exportFlamegraphDotCom={isExportToFlamegraphDotComEnabled}
   104        exportFlamegraphDotComFn={exportToFlamegraphDotComFn}
   105      />
   106    );
   107  
   108    return (
   109      <div>
   110        <PageTitle title={formatTitle('Diff', leftQuery, rightQuery)} />
   111        <div className="main-wrapper">
   112          <Toolbar
   113            onSelectedApp={(query) => {
   114              dispatch(actions.setQuery(query));
   115            }}
   116          />
   117          <Box>
   118            <LoadingOverlay active={isLoading}>
   119              <TimelineChartWrapper
   120                data-testid="timeline-main"
   121                id="timeline-chart-diff"
   122                format="lines"
   123                height="125px"
   124                annotations={annotations}
   125                timelineA={leftTimeline}
   126                timelineB={rightTimeline}
   127                onSelect={(from, until) => {
   128                  dispatch(actions.setFromAndUntil({ from, until }));
   129                }}
   130                syncCrosshairsWith={[
   131                  'timeline-chart-left',
   132                  'timeline-chart-right',
   133                ]}
   134                selection={{
   135                  left: {
   136                    from: leftFrom,
   137                    to: leftUntil,
   138                    color: leftColor,
   139                    overlayColor: leftColor.alpha(0.3),
   140                  },
   141                  right: {
   142                    from: rightFrom,
   143                    to: rightUntil,
   144                    color: rightColor,
   145                    overlayColor: rightColor.alpha(0.3),
   146                  },
   147                }}
   148                selectionType="double"
   149                timezone={timezone}
   150                title={<ChartTitle titleKey={diffView.profile?.metadata.units} />}
   151              />
   152              <SyncTimelines
   153                isDataLoading={isLoading}
   154                timeline={leftTimeline}
   155                leftSelection={{ from: leftFrom, to: leftUntil }}
   156                rightSelection={{ from: rightFrom, to: rightUntil }}
   157                onSync={(from, until) => {
   158                  dispatch(actions.setFromAndUntil({ from, until }));
   159                }}
   160              />
   161            </LoadingOverlay>
   162          </Box>
   163          <div className="diff-instructions-wrapper">
   164            <Box className="diff-instructions-wrapper-side">
   165              <LoadingOverlay active={isLoading}>
   166                <ChartTitle titleKey="baseline" color={leftColor} />
   167                <TagsBar
   168                  query={leftQuery}
   169                  tags={leftTags}
   170                  onRefresh={() => dispatch(actions.refresh())}
   171                  onSetQuery={(q) => dispatch(actions.setLeftQuery(q))}
   172                  onSelectedLabel={(label, query) => {
   173                    dispatch(fetchTagValues({ query, label }));
   174                  }}
   175                />
   176                <TimelineChartWrapper
   177                  data-testid="timeline-left"
   178                  key="timeline-chart-left"
   179                  id="timeline-chart-left"
   180                  timelineA={leftTimeline}
   181                  syncCrosshairsWith={[
   182                    'timeline-chart-diff',
   183                    'timeline-chart-right',
   184                  ]}
   185                  selectionWithHandler
   186                  onSelect={(from, until) => {
   187                    dispatch(actions.setLeft({ from, until }));
   188                  }}
   189                  selection={{
   190                    left: {
   191                      from: leftFrom,
   192                      to: leftUntil,
   193                      color: selectionColor,
   194                      overlayColor: selectionColor.alpha(0.3),
   195                    },
   196                  }}
   197                  selectionType="single"
   198                  timezone={timezone}
   199                />
   200              </LoadingOverlay>
   201            </Box>
   202            <Box className="diff-instructions-wrapper-side">
   203              <LoadingOverlay active={isLoading}>
   204                <ChartTitle titleKey="comparison" color={rightColor} />
   205                <TagsBar
   206                  query={rightQuery}
   207                  tags={rightTags}
   208                  onRefresh={() => dispatch(actions.refresh())}
   209                  onSetQuery={(q) => dispatch(actions.setRightQuery(q))}
   210                  onSelectedLabel={(label, query) => {
   211                    dispatch(fetchTagValues({ query, label }));
   212                  }}
   213                />
   214                <TimelineChartWrapper
   215                  data-testid="timeline-right"
   216                  key="timeline-chart-right"
   217                  id="timeline-chart-right"
   218                  selectionWithHandler
   219                  timelineA={rightTimeline}
   220                  syncCrosshairsWith={[
   221                    'timeline-chart-diff',
   222                    'timeline-chart-left',
   223                  ]}
   224                  onSelect={(from, until) => {
   225                    dispatch(actions.setRight({ from, until }));
   226                  }}
   227                  selection={{
   228                    right: {
   229                      from: rightFrom,
   230                      to: rightUntil,
   231                      color: selectionColor,
   232                      overlayColor: selectionColor.alpha(0.3),
   233                    },
   234                  }}
   235                  selectionType="single"
   236                  timezone={timezone}
   237                />
   238              </LoadingOverlay>
   239            </Box>
   240          </div>
   241          <Box>
   242            <LoadingOverlay active={isLoading} spinnerPosition="baseline">
   243              <ChartTitle titleKey="diff" />
   244              <FlamegraphRenderer
   245                showCredit={false}
   246                profile={diffView.profile}
   247                ExportData={exportData}
   248                colorMode={colorMode}
   249              />
   250            </LoadingOverlay>
   251          </Box>
   252        </div>
   253      </div>
   254    );
   255  }
   256  
   257  export default ComparisonDiffApp;