refactor: Update useScrollAnchor to handle user scrolling and overflow

This commit is contained in:
n4ze3m 2024-06-25 21:13:45 +05:30
parent 1e9b66d823
commit 5ac03f5123
2 changed files with 108 additions and 75 deletions

View File

@ -1,87 +1,106 @@
import { useCallback, useEffect, useRef, useState } from "react" import { useCallback, useEffect, useRef, useState } from "react"
import { useMessageOption } from "./useMessageOption"
export const useScrollAnchor = () => { export const useScrollAnchor = () => {
const messagesRef = useRef<HTMLDivElement>(null) const { isProcessing, messages } = useMessageOption()
const scrollRef = useRef<HTMLDivElement>(null)
const visibilityRef = useRef<HTMLDivElement>(null)
const [isAtTop, setIsAtTop] = useState(false)
const [isAtBottom, setIsAtBottom] = useState(true) const [isAtBottom, setIsAtBottom] = useState(true)
const [isVisible, setIsVisible] = useState(false) const [userScrolled, setUserScrolled] = useState(false)
const [isOverflowing, setIsOverflowing] = useState(false)
const messagesStartRef = useRef<HTMLDivElement>(null)
const messagesEndRef = useRef<HTMLDivElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
const isAutoScrolling = useRef(false)
console.log(`isAtTop: ${isAtTop}, isAtBottom: ${isAtBottom}, userScrolled: ${userScrolled}, isOverflowing: ${isOverflowing}`)
useEffect(() => {
if (!isProcessing && userScrolled) {
console.log("userScrolled")
setUserScrolled(false)
}
}, [isProcessing])
useEffect(() => {
if (isProcessing && !userScrolled) {
scrollToBottom()
}
}, [messages])
useEffect(() => {
const container = containerRef.current
if (!container) return
const topObserver = new IntersectionObserver(
([entry]) => {
setIsAtTop(entry.isIntersecting)
},
{ threshold: 1 }
)
const bottomObserver = new IntersectionObserver(
([entry]) => {
setIsAtBottom(entry.isIntersecting)
if (entry.isIntersecting) {
setUserScrolled(false)
} else if (!isAutoScrolling.current) {
setUserScrolled(true)
}
},
{ threshold: 1 }
)
if (messagesStartRef.current) {
topObserver.observe(messagesStartRef.current)
}
if (messagesEndRef.current) {
bottomObserver.observe(messagesEndRef.current)
}
const resizeObserver = new ResizeObserver(() => {
setIsOverflowing(container.scrollHeight > container.clientHeight)
})
resizeObserver.observe(container)
return () => {
topObserver.disconnect()
bottomObserver.disconnect()
resizeObserver.disconnect()
}
}, [])
const scrollToTop = useCallback(() => {
if (messagesStartRef.current) {
messagesStartRef.current.scrollIntoView({ behavior: "smooth" })
}
}, [])
const scrollToBottom = useCallback(() => { const scrollToBottom = useCallback(() => {
if (messagesRef.current) { isAutoScrolling.current = true
messagesRef.current.scrollIntoView({
block: "end", setTimeout(() => {
behavior: "smooth" if (messagesEndRef.current) {
}) messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
} }
isAutoScrolling.current = false
}, 100)
}, []) }, [])
useEffect(() => {
if (messagesRef.current) {
if (isAtBottom && !isVisible) {
messagesRef.current.scrollIntoView({
block: "end"
})
}
}
}, [isAtBottom, isVisible])
useEffect(() => {
const { current } = scrollRef
if (current) {
const handleScroll = (event: Event) => {
const target = event.target as HTMLDivElement
const offset = 25
const isAtBottom =
target.scrollTop + target.clientHeight >= target.scrollHeight - offset
console.log(target.scrollTop, target.clientHeight, target.scrollHeight)
setIsAtBottom(isAtBottom)
}
current.addEventListener("scroll", handleScroll, {
passive: true
})
return () => {
current.removeEventListener("scroll", handleScroll)
}
}
}, [])
useEffect(() => {
if (visibilityRef.current) {
let observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
console.log(entry.isIntersecting)
if (entry.isIntersecting) {
setIsVisible(true)
} else {
setIsVisible(false)
}
})
},
{
rootMargin: "0px 0px -100px 0px"
}
)
observer.observe(visibilityRef.current)
return () => {
observer.disconnect()
}
}
})
return { return {
messagesRef, messagesStartRef,
scrollRef, messagesEndRef,
visibilityRef, containerRef,
scrollToBottom, isAtTop,
isAtBottom, isAtBottom,
isVisible userScrolled,
isOverflowing,
scrollToTop,
scrollToBottom,
setIsAtBottom
} }
} }

View File

@ -38,4 +38,18 @@ export const getAdvancedOllamaSettings = async () => {
export const copilotResumeLastChat = async () => { export const copilotResumeLastChat = async () => {
return await storage.get<boolean>("copilotResumeLastChat") return await storage.get<boolean>("copilotResumeLastChat")
}
export const defaultSidebarOpen = async () => {
const sidebarOpen = await storage.get("sidebarOpen")
if (!sidebarOpen || sidebarOpen === "") {
return "right_clk"
}
return sidebarOpen
}
export const setSidebarOpen = async (sidebarOpen: string) => {
await storage.set("sidebarOpen", sidebarOpen)
} }