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 scrollToBottom = useCallback(() => { const messagesStartRef = useRef<HTMLDivElement>(null)
if (messagesRef.current) { const messagesEndRef = useRef<HTMLDivElement>(null)
messagesRef.current.scrollIntoView({ const containerRef = useRef<HTMLDivElement>(null)
block: "end", const isAutoScrolling = useRef(false)
behavior: "smooth"
}) console.log(`isAtTop: ${isAtTop}, isAtBottom: ${isAtBottom}, userScrolled: ${userScrolled}, isOverflowing: ${isOverflowing}`)
}
}, [])
useEffect(() => { useEffect(() => {
if (messagesRef.current) { if (!isProcessing && userScrolled) {
if (isAtBottom && !isVisible) { console.log("userScrolled")
messagesRef.current.scrollIntoView({ setUserScrolled(false)
block: "end"
})
} }
} }, [isProcessing])
}, [isAtBottom, isVisible])
useEffect(() => { useEffect(() => {
const { current } = scrollRef if (isProcessing && !userScrolled) {
scrollToBottom()
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)
} }
}, [messages])
current.addEventListener("scroll", handleScroll, {
passive: true
})
return () => {
current.removeEventListener("scroll", handleScroll)
}
}
}, [])
useEffect(() => { useEffect(() => {
if (visibilityRef.current) { const container = containerRef.current
let observer = new IntersectionObserver( if (!container) return
(entries) => {
entries.forEach((entry) => { const topObserver = new IntersectionObserver(
console.log(entry.isIntersecting) ([entry]) => {
if (entry.isIntersecting) { setIsAtTop(entry.isIntersecting)
setIsVisible(true)
} else {
setIsVisible(false)
}
})
}, },
{ { threshold: 1 }
rootMargin: "0px 0px -100px 0px"
}
) )
observer.observe(visibilityRef.current) const bottomObserver = new IntersectionObserver(
([entry]) => {
setIsAtBottom(entry.isIntersecting)
if (entry.isIntersecting) {
setUserScrolled(false)
} else if (!isAutoScrolling.current) {
setUserScrolled(true)
}
},
{ threshold: 1 }
)
return () => { if (messagesStartRef.current) {
observer.disconnect() 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(() => {
isAutoScrolling.current = true
setTimeout(() => {
if (messagesEndRef.current) {
messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
}
isAutoScrolling.current = false
}, 100)
}, [])
return { return {
messagesRef, messagesStartRef,
scrollRef, messagesEndRef,
visibilityRef, containerRef,
scrollToBottom, isAtTop,
isAtBottom, isAtBottom,
isVisible userScrolled,
isOverflowing,
scrollToTop,
scrollToBottom,
setIsAtBottom
} }
} }

View File

@ -39,3 +39,17 @@ 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)
}