github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/components/TimelineChart/SyncTimelines/useSync.ts (about) 1 import { useEffect, useState } from 'react'; 2 import { centerTimelineData } from '@webapp/components/TimelineChart/centerTimelineData'; 3 import { TimelineData } from '@webapp/components/TimelineChart/TimelineChartWrapper'; 4 import { getSelectionBoundaries } from '@webapp/components/TimelineChart/SyncTimelines/getSelectionBoundaries'; 5 import { Selection } from '../markings'; 6 7 interface UseSyncParams { 8 timeline: TimelineData; 9 leftSelection: { 10 from: string; 11 to: string; 12 }; 13 rightSelection: { 14 from: string; 15 to: string; 16 }; 17 onSync: (from: string, until: string) => void; 18 } 19 20 const timeOffset = 5 * 60 * 1000; 21 const selectionOffset = 5000; 22 23 export const getTitle = (leftInRange: boolean, rightInRange: boolean) => { 24 if (!leftInRange && !rightInRange) { 25 return 'Warning: Baseline and Comparison timeline selections are out of range'; 26 } 27 if (!rightInRange) { 28 return 'Warning: Comparison timeline selection is out of range'; 29 } 30 return 'Warning: Baseline timeline selection is out of range'; 31 }; 32 33 const isInRange = ( 34 from: number, 35 to: number, 36 selectionFrom: number, 37 selectionTo: number 38 ) => { 39 return selectionFrom + timeOffset >= from && selectionTo - timeOffset <= to; 40 }; 41 42 export function useSync({ 43 timeline, 44 leftSelection, 45 rightSelection, 46 onSync, 47 }: UseSyncParams) { 48 const [isIgnoring, setIgnoring] = useState(false); 49 50 useEffect(() => { 51 if (isIgnoring) { 52 setIgnoring(false); 53 } 54 }, [leftSelection, rightSelection, timeline]); 55 56 const { from: leftFrom, to: leftTo } = getSelectionBoundaries( 57 leftSelection as Selection 58 ); 59 60 const { from: rightFrom, to: rightTo } = getSelectionBoundaries( 61 rightSelection as Selection 62 ); 63 64 const centeredData = centerTimelineData(timeline); 65 66 const timelineFrom = centeredData?.[0]?.[0]; 67 const timelineTo = centeredData?.[centeredData?.length - 1]?.[0]; 68 69 const isLeftInRange = isInRange(timelineFrom, timelineTo, leftFrom, leftTo); 70 const isRightInRange = isInRange( 71 timelineFrom, 72 timelineTo, 73 rightFrom, 74 rightTo 75 ); 76 77 const onSyncClick = () => { 78 const selectionsLimits = [leftFrom, leftTo, rightFrom, rightTo]; 79 const selectionMin = Math.min(...selectionsLimits); 80 const selectionMax = Math.max(...selectionsLimits); 81 // when some of selection is in relative time (now, now-1h etc.), we have to extend detecting time buffer 82 // 1) to prevent falsy detections 83 // 2) to workaraund pecularity that when we change selection we don't refetch main timeline 84 const offset = [ 85 leftSelection.from, 86 rightSelection.from, 87 leftSelection.to, 88 rightSelection.to, 89 ].some((p) => String(p).startsWith('now')) 90 ? selectionOffset 91 : 1; 92 93 onSync(String(selectionMin - offset), String(selectionMax + offset)); 94 }; 95 96 return { 97 isWarningHidden: 98 !timeline.data?.samples.length || 99 (isLeftInRange && isRightInRange) || 100 isIgnoring, 101 title: getTitle(isLeftInRange, isRightInRange), 102 onIgnore: () => setIgnoring(true), 103 onSyncClick, 104 }; 105 }