From 88699ff909b026511da392bf2c0a96be02abc6fd Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Tue, 5 Sep 2023 14:40:25 -0700
Subject: fix: :lipstick: nicer autoscroll

---
 extension/react-app/src/pages/gui.tsx | 90 ++++++++++++-----------------------
 1 file changed, 30 insertions(+), 60 deletions(-)

(limited to 'extension/react-app/src/pages/gui.tsx')

diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx
index c4e13d2a..c82f4f2b 100644
--- a/extension/react-app/src/pages/gui.tsx
+++ b/extension/react-app/src/pages/gui.tsx
@@ -3,7 +3,13 @@ import { defaultBorderRadius } from "../components";
 import Loader from "../components/Loader";
 import ContinueButton from "../components/ContinueButton";
 import { FullState } from "../../../schema/FullState";
-import { useCallback, useEffect, useRef, useState, useContext } from "react";
+import {
+  useEffect,
+  useRef,
+  useState,
+  useContext,
+  useLayoutEffect,
+} from "react";
 import { HistoryNode } from "../../../schema/HistoryNode";
 import StepContainer from "../components/StepContainer";
 import { GUIClientContext } from "../App";
@@ -99,70 +105,39 @@ function GUI(props: GUIProps) {
     );
   }, [bottomMessage, aboveComboBoxDivRef.current]);
 
-  const [isScrolling, setIsScrolling] = useState(false);
-
-  const [scrollTimeoutId, setScrollTimeoutId] = useState<NodeJS.Timeout>();
+  const [userScrolledAwayFromBottom, setUserScrolledAwayFromBottom] =
+    useState<boolean>(false);
 
-  const scrollToBottom = useCallback(
-    (force: boolean = false) => {
-      if (scrollTimeoutId) {
-        return;
-      }
-
-      // Scroll only if user is within 100 pixels of the bottom of the window.
-      const edgeOffset = 50;
-      const scrollPosition = window.scrollY || 0;
+  useEffect(() => {
+    const handleScroll = () => {
+      // Scroll only if user is within 200 pixels of the bottom of the window.
+      const edgeOffset = -25;
+      const scrollPosition = topGuiDivRef.current?.scrollTop || 0;
       const scrollHeight = topGuiDivRef.current?.scrollHeight || 0;
       const clientHeight = window.innerHeight || 0;
 
-      const atBottomEdge =
-        scrollPosition + clientHeight + edgeOffset >= scrollHeight;
-
-      if (isScrolling || (!atBottomEdge && !force)) {
-        return;
+      if (scrollPosition + clientHeight + edgeOffset >= scrollHeight) {
+        setUserScrolledAwayFromBottom(false);
+      } else {
+        setUserScrolledAwayFromBottom(true);
       }
-
-      window.scrollTo({
-        top: scrollHeight,
-        behavior: "smooth",
-      });
-
-      setScrollTimeoutId(
-        setTimeout(() => {
-          setScrollTimeoutId(undefined);
-        }, 800)
-      );
-    },
-    [
-      window.scrollY,
-      topGuiDivRef.current?.scrollHeight,
-      topGuiDivRef.current?.clientHeight,
-      isScrolling,
-      scrollTimeoutId,
-    ]
-  );
-
-  const [timerId, setTimerId] = useState<NodeJS.Timeout>();
-
-  useEffect(() => {
-    const handleScrollEnd = () => {
-      setIsScrolling(false);
     };
 
-    const handleScroll = () => {
-      window.clearTimeout(timerId);
-
-      setIsScrolling(true);
+    topGuiDivRef.current?.addEventListener("wheel", handleScroll);
 
-      setTimerId(setTimeout(() => handleScrollEnd(), 800));
+    return () => {
+      window.removeEventListener("wheel", handleScroll);
     };
+  }, [topGuiDivRef.current]);
 
-    window.addEventListener("scroll", handleScroll);
+  useLayoutEffect(() => {
+    if (userScrolledAwayFromBottom) return;
 
-    return () => {
-      window.removeEventListener("scroll", handleScroll);
-    };
-  }, []);
+    topGuiDivRef.current?.scrollTo({
+      top: topGuiDivRef.current?.scrollHeight,
+      behavior: "smooth" as any,
+    });
+  }, [topGuiDivRef.current?.scrollHeight, history.timeline]);
 
   useEffect(() => {
     // Cmd + Backspace to delete current step
@@ -217,15 +192,9 @@ function GUI(props: GUIProps) {
         }
         return nextStepsOpen;
       });
-
-      scrollToBottom();
     });
   }, [client]);
 
-  useEffect(() => {
-    scrollToBottom(true);
-  }, [waitingForSteps]);
-
   // #endregion
 
   useEffect(() => {
@@ -378,6 +347,7 @@ function GUI(props: GUIProps) {
   }, []);
   return (
     <div
+      className="overflow-scroll"
       ref={topGuiDivRef}
       onKeyDown={(e) => {
         if (e.key === "Enter" && e.ctrlKey) {
-- 
cgit v1.2.3-70-g09d2

