Browse Source

feat(replays): Replay layout timeline size consistent when full screening (#59004)

The timeline size was resetting to the default size when full screened,
so moved the timeline states to replay context to keep size consistent.
The default size has also been changed to 1x, with the largest size
being the amount of minutes a replay is

Relates to https://github.com/getsentry/team-replay/issues/199
Closes https://github.com/getsentry/sentry/issues/59005
Catherine Lee 1 year ago
parent
commit
b12809a0f9

+ 12 - 11
static/app/components/replays/breadcrumbs/replayTimeline.tsx

@@ -21,13 +21,16 @@ import toPercent from 'sentry/utils/number/toPercent';
 import {useDimensions} from 'sentry/utils/useDimensions';
 import useOrganization from 'sentry/utils/useOrganization';
 
-type Props = {size: number};
+type Props = {};
 
-function ReplayTimeline({size}: Props) {
-  const {replay, currentTime} = useReplayContext();
+function ReplayTimeline({}: Props) {
+  const {replay, currentTime, timelineScale} = useReplayContext();
 
   const panelRef = useRef<HTMLDivElement>(null);
-  const mouseTrackingProps = useTimelineScrubberMouseTracking({elem: panelRef}, size);
+  const mouseTrackingProps = useTimelineScrubberMouseTracking(
+    {elem: panelRef},
+    timelineScale
+  );
 
   const stackedRef = useRef<HTMLDivElement>(null);
   const {width} = useDimensions<HTMLDivElement>({elementRef: stackedRef});
@@ -45,19 +48,17 @@ function ReplayTimeline({size}: Props) {
   const networkFrames = replay.getNetworkFrames();
 
   // start of the timeline is in the middle
-  const initialTranslatePercentage = 50 / size;
+  const initialTranslate = 0.5 / timelineScale;
 
-  const translatePercentage = toPercent(
-    initialTranslatePercentage -
-      (currentTime > durationMs ? 1 : divide(currentTime, durationMs))
-  );
+  const translate =
+    initialTranslate - (currentTime > durationMs ? 1 : divide(currentTime, durationMs));
 
   return hasNewTimeline ? (
     <VisiblePanel ref={panelRef} {...mouseTrackingProps}>
       <Stacked
         style={{
-          width: `${size}%`,
-          transform: `translate(${translatePercentage}, 0%)`,
+          width: `${toPercent(timelineScale)}`,
+          transform: `translate(${toPercent(translate)}, 0%)`,
         }}
         ref={stackedRef}
       >

+ 1 - 1
static/app/components/replays/player/useScrubberMouseTracking.tsx

@@ -54,7 +54,7 @@ export function useTimelineScrubberMouseTracking<T extends Element>(
 
       if (left >= 0) {
         const percent = (left - width / 2) / width;
-        const time = currentTime + (percent * durationMs) / (size / 100);
+        const time = currentTime + (percent * durationMs) / size;
         setCurrentHoverTime(time);
       } else {
         setCurrentHoverTime(undefined);

+ 15 - 0
static/app/components/replays/replayContext.tsx

@@ -114,11 +114,21 @@ interface ReplayPlayerContextProps extends HighlightCallbacks {
    */
   setSpeed: (speed: number) => void;
 
+  /**
+   * Set the timeline width to the specific scale, starting at 1x and growing larger
+   */
+  setTimelineScale: (size: number) => void;
+
   /**
    * The speed for normal playback
    */
   speed: number;
 
+  /**
+   * Scale of the timeline width
+   */
+  timelineScale: number;
+
   /**
    * Start or stop playback
    *
@@ -153,7 +163,9 @@ const ReplayPlayerContext = createContext<ReplayPlayerContextProps>({
   setCurrentHoverTime: () => {},
   setCurrentTime: () => {},
   setSpeed: () => {},
+  setTimelineScale: () => {},
   speed: 1,
+  timelineScale: 1,
   togglePlayPause: () => {},
   toggleSkipInactive: () => {},
 });
@@ -220,6 +232,7 @@ export function Provider({
   const [buffer, setBufferTime] = useState({target: -1, previous: -1});
   const playTimer = useRef<number | undefined>(undefined);
   const didApplyInitialOffset = useRef(false);
+  const [timelineScale, setTimelineScale] = useState(1);
 
   const isFinished = replayerRef.current?.getCurrentTime() === finishedAtMS;
 
@@ -525,7 +538,9 @@ export function Provider({
         setCurrentHoverTime,
         setCurrentTime,
         setSpeed,
+        setTimelineScale,
         speed,
+        timelineScale,
         togglePlayPause,
         toggleSkipInactive,
         ...value,

+ 10 - 6
static/app/components/replays/replayController.tsx

@@ -146,7 +146,10 @@ function ReplayOptionsMenu({speedOptions}: {speedOptions: number[]}) {
   );
 }
 
-function TimelineSizeBar({size, setSize}) {
+function TimelineSizeBar() {
+  const {timelineScale, setTimelineScale, replay} = useReplayContext();
+  const durationMs = replay?.getDurationMs();
+  const maxScale = durationMs ? Math.ceil(durationMs / 60000) : 10;
   return (
     <ButtonBar merged>
       <Button
@@ -154,16 +157,18 @@ function TimelineSizeBar({size, setSize}) {
         title={t('Zoom out')}
         icon={<IconSubtract size="xs" />}
         borderless
-        onClick={() => setSize(Math.max(size - 50, 100))}
+        onClick={() => setTimelineScale(Math.max(timelineScale - 0.5, 1))}
         aria-label={t('Zoom out')}
+        disabled={timelineScale === 1}
       />
       <Button
         size="xs"
         title={t('Zoom in')}
         icon={<IconAdd size="xs" />}
         borderless
-        onClick={() => setSize(Math.min(size + 50, 1000))}
+        onClick={() => setTimelineScale(Math.min(timelineScale + 0.5, maxScale))}
         aria-label={t('Zoom in')}
+        disabled={timelineScale === maxScale}
       />
     </ButtonBar>
   );
@@ -180,7 +185,6 @@ function ReplayControls({
   const isFullscreen = useIsFullscreen();
   const {currentTime, replay} = useReplayContext();
   const durationMs = replay?.getDurationMs();
-  const [size, setSize] = useState(300);
 
   // If the browser supports going fullscreen or not. iPhone Safari won't do
   // it. https://caniuse.com/fullscreen
@@ -221,10 +225,10 @@ function ReplayControls({
           <TimeAndScrubberGrid isCompact={isCompact}>
             <Time style={{gridArea: 'currentTime'}}>{formatTime(currentTime)}</Time>
             <div style={{gridArea: 'timeline'}}>
-              <ReplayTimeline size={size} />
+              <ReplayTimeline />
             </div>
             <div style={{gridArea: 'timelineSize'}}>
-              <TimelineSizeBar size={size} setSize={setSize} />
+              <TimelineSizeBar />
             </div>
             <StyledScrubber
               style={{gridArea: 'scrubber'}}

+ 1 - 1
static/app/views/replays/detail/layout/index.tsx

@@ -41,7 +41,7 @@ function ReplayLayout({layout = LayoutKey.TOPBAR}: Props) {
 
   const timeline = hasNewTimeline ? null : (
     <ErrorBoundary mini>
-      <ReplayTimeline size={100} />
+      <ReplayTimeline />
     </ErrorBoundary>
   );