aboutsummaryrefslogtreecommitdiff
path: root/apps/web/app/lib/hooks/use-auto-scroll.ts
blob: b54e99ea155e650e2cd8342836fb6c7c09d848ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import { useEffect, useRef, useState } from "react";

interface UseAutoScrollOptions {
  offset?: number;
  bottomThreshold?: number;
}

export function useAutoScroll({ offset = 250, bottomThreshold = 10 }: UseAutoScrollOptions = {}) {
  const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
  const scrollRef = useRef<HTMLDivElement>(null);
  const bottomRef = useRef<HTMLDivElement>(null);

  const scrollToBottom = (behavior: ScrollBehavior = "smooth") => {
    if (bottomRef.current && shouldAutoScroll) {
      const parent = scrollRef.current;
      if (!parent) return;

      // Calculate the scroll position accounting for the offset
      const targetScrollTop = bottomRef.current.offsetTop - parent.clientHeight + offset;
      parent.scrollTo({ top: targetScrollTop, behavior });
    }
  };

  const handleScroll = () => {
    if (!scrollRef.current || !bottomRef.current) return;

    const { scrollTop, clientHeight } = scrollRef.current;
    const targetScrollTop = bottomRef.current.offsetTop - clientHeight + offset;
    
    // Consider the user at the bottom if they're within the threshold of the target scroll position
    const isAtBottom = Math.abs(targetScrollTop - scrollTop) < bottomThreshold;

    if (shouldAutoScroll !== isAtBottom) {
      setShouldAutoScroll(isAtBottom);
    }
  };

  useEffect(() => {
    const scrollElement = scrollRef.current;
    if (!scrollElement) return;

    const handleTouchStart = () => {
      setShouldAutoScroll(false);
    };

    scrollElement.addEventListener("touchstart", handleTouchStart);
    scrollElement.addEventListener("scroll", handleScroll);

    return () => {
      scrollElement.removeEventListener("touchstart", handleTouchStart);
      scrollElement.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return {
    scrollRef,
    bottomRef,
    shouldAutoScroll,
    scrollToBottom,
    setShouldAutoScroll
  };
}