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  }