Skip to content
This repository was archived by the owner on Sep 15, 2024. It is now read-only.

Commit

Permalink
Improve [UI/UX] [Front End] [Chat] Scrolling (#281)
Browse files Browse the repository at this point in the history
* Improve [UI/UX] [Front End] [Chat] Scrolling

- [+] feat(chat.tsx): improve chat scroll functionality
  - Add onScroll function to track user scroll state
  - Modify scrollDomToBottom to prevent auto-scroll when user scrolls up
  - Add scrollToBottomSmooth function for smooth scrolling
  - Update logic in _Chat to better handle auto-scroll
  - Replace scrollToBottom with scrollToBottomSmooth in chat component

* Fix Stupid React Warning

- [+] fix(chat.tsx): include props in useEffect dependencies in ChatActions function
  • Loading branch information
H0llyW00dzZ authored Feb 22, 2024
1 parent bc0a9b9 commit 3fa698a
Showing 1 changed file with 58 additions and 37 deletions.
95 changes: 58 additions & 37 deletions app/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -453,31 +453,55 @@ function useScrollToBottom() {
// for auto-scroll
const scrollRef = useRef<HTMLDivElement>(null);
const [autoScroll, setAutoScroll] = useState(true);
const config = useAppConfig();
let isAutoScrollEnabled: boolean = config.autoScrollMessage;
const userHasScrolledUp = useRef(false);

function onScroll() {
if (!scrollRef.current) return;

const { scrollTop, scrollHeight, clientHeight } = scrollRef.current;
const isAtBottom = scrollTop + clientHeight >= scrollHeight;

userHasScrolledUp.current = !isAtBottom;
}

function scrollDomToBottom() {
const dom = scrollRef.current;
if (dom) {
requestAnimationFrame(() => { // this stupid frame might conflict with smooth behavior
setAutoScroll(isAutoScrollEnabled);
// Improve Use smooth scrolling behavior
dom.scrollTo({ top: dom.scrollHeight, behavior: 'smooth' });
});
if (dom && !userHasScrolledUp.current) {
dom.scrollTop = dom.scrollHeight;
}
}

const scrollToBottomSmooth = () => {
const scrollContainer = scrollRef.current;
if (scrollContainer) {
const scrollHeight = scrollContainer.scrollHeight;
const height = scrollContainer.clientHeight;
const maxScrollTop = scrollHeight - height;
scrollContainer.scrollTo({ top: maxScrollTop, behavior: 'smooth' });
}
};

// auto scroll
useEffect(() => {
if (autoScroll) {
scrollDomToBottom();
}
const dom = scrollRef.current;
dom?.addEventListener("scroll", onScroll);

scrollDomToBottom();

return () => {
dom?.removeEventListener("scroll", onScroll);
};
});

return {
scrollRef,
autoScroll,
setAutoScroll,
scrollDomToBottom,
scrollToBottomSmooth,
};
}

Expand Down Expand Up @@ -539,7 +563,7 @@ export function ChatActions(props: {
);
showToast(nextModel);
}
}, [chatStore, currentModel, models]);
}, [props, chatStore, currentModel, models]);

return (
<div className={styles["chat-input-actions"]}>
Expand Down Expand Up @@ -1220,40 +1244,26 @@ function _Chat() {
// Determine if the user is at the top or bottom edge of the chat.
const isTouchTopEdge = target.scrollTop <= edgeThreshold;
const isTouchBottomEdge = bottomHeight >= target.scrollHeight - edgeThreshold;
const isHitBottom = bottomHeight >= target.scrollHeight - (isMobileScreen ? 4 : 10);

// If the user is manually scrolling, disable auto-scroll.
if (isTouchTopEdge || isTouchBottomEdge) {
setAutoScroll(false);
} else {
// Only enable auto-scroll if the `config.autoScrollMessage` is true
// and the user has not manually scrolled to the top or bottom edge.
if (config.autoScrollMessage) {
setAutoScroll(true);
}
}

// Update the message render index only if auto-scroll is enabled.
if (config.autoScrollMessage) {
const nextPageMsgIndex = msgRenderIndex + CHAT_PAGE_SIZE;
const prevPageMsgIndex = msgRenderIndex - CHAT_PAGE_SIZE;

const nextPageMsgIndex = msgRenderIndex + CHAT_PAGE_SIZE;
const prevPageMsgIndex = msgRenderIndex - CHAT_PAGE_SIZE;

if (isTouchTopEdge && !isTouchBottomEdge) {
setMsgRenderIndex(prevPageMsgIndex);
} else if (isTouchBottomEdge) {
setMsgRenderIndex(nextPageMsgIndex);
}
if (isTouchTopEdge && !isTouchBottomEdge) {
setMsgRenderIndex(prevPageMsgIndex);
} else if (isTouchBottomEdge) {
setMsgRenderIndex(nextPageMsgIndex);
}

// Determine if the user has scrolled to the bottom of the chat.
const isHitBottom = bottomHeight >= target.scrollHeight - (isMobileScreen ? 4 : 10);
setHitBottom(isHitBottom);
setAutoScroll(isHitBottom);
}, [
setHitBottom,
setAutoScroll,
isMobileScreen,
msgRenderIndex,
setHitBottom,
setAutoScroll,
isMobileScreen,
msgRenderIndex,
setMsgRenderIndex, // Added setMsgRenderIndex
config.autoScrollMessage // Include this dependency as indicated by the stupid complexity react warning
]);

// Use the custom hook to debounce the onChatBodyScroll function
Expand Down Expand Up @@ -1469,6 +1479,17 @@ function _Chat() {
setAttachImages(images);
}

// this now better
const scrollToBottomSmooth = () => {
const scrollContainer = scrollRef.current;
if (scrollContainer) {
const scrollHeight = scrollContainer.scrollHeight;
const height = scrollContainer.clientHeight;
const maxScrollTop = scrollHeight - height;
scrollContainer.scrollTo({ top: maxScrollTop, behavior: 'smooth' });
}
};

return (
<div className={styles.chat} key={session.id}>
<div className="window-header" data-tauri-drag-region>
Expand Down Expand Up @@ -1756,7 +1777,7 @@ function _Chat() {
setAttachImages={setAttachImages}
setUploading={setUploading}
showPromptModal={() => setShowPromptModal(true)}
scrollToBottom={scrollToBottom}
scrollToBottom={scrollToBottomSmooth}
hitBottom={hitBottom}
uploading={uploading}
showPromptHints={() => {
Expand Down

0 comments on commit 3fa698a

Please sign in to comment.