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;