diff --git a/www/app/globals.css b/www/app/globals.css index f788bea..526170c 100644 --- a/www/app/globals.css +++ b/www/app/globals.css @@ -2,6 +2,9 @@ @tailwind components; @tailwind utilities; +html { + scroll-behavior: smooth; +} .typing_dot { animation: typing 1s infinite; @@ -28,4 +31,4 @@ .typing_dot:nth-child(3) { animation-delay: 0.4s; -} \ No newline at end of file +} diff --git a/www/app/page.tsx b/www/app/page.tsx index fc0cd00..fc31452 100644 --- a/www/app/page.tsx +++ b/www/app/page.tsx @@ -15,7 +15,14 @@ import { } from "react-icons/fa"; // import { IoIosArrowDown } from "react-icons/io"; // import { GrClose } from "react-icons/gr"; -import { useRef, useEffect, useState, useCallback } from "react"; +import { + useRef, + useEffect, + useState, + useCallback, + use, + ElementRef, +} from "react"; import { v4 as uuidv4 } from "uuid"; import Typing from "@/components/typing"; @@ -56,9 +63,12 @@ export default function Home() { conversation_id: "", name: "", }); - const input = useRef(null); + const input = useRef>(null); const supabase = createClientComponentClient(); + const isAtBottom = useRef(true); + const messageContainerRef = useRef>(null); + const newChat = useCallback(async () => { return await fetch(`${URL}/api/conversations/insert?user_id=${userId}`) .then((res) => res.json()) @@ -144,8 +154,33 @@ export default function Home() { setMessages([defaultMessage, ...messages]); }); } + + // scroll to bottom + const messageContainer = messageContainerRef.current; + if (messageContainer) { + messageContainer.scrollTop = messageContainer.scrollHeight; + } }, [currentConversation, userId]); + useEffect(() => { + const messageContainer = messageContainerRef.current; + if (!messageContainer) return; + + const func = () => { + const val = + Math.round( + messageContainer.scrollHeight - messageContainer.scrollTop + ) === messageContainer.clientHeight; + isAtBottom.current = val; + }; + + messageContainer.addEventListener("scroll", func); + + return () => { + messageContainer.removeEventListener("scroll", func); + }; + }, []); + // async function newChat() { // return await fetch(`${URL}/api/conversations/insert?user_id=${userId}`) // .then((res) => res.json()) @@ -207,6 +242,11 @@ export default function Home() { const reader = data.body?.pipeThrough(new TextDecoderStream()).getReader()!; + const messageContainer = messageContainerRef.current; + if (messageContainer) { + messageContainer.scrollTop = messageContainer.scrollHeight; + } + // clear the last message setMessages((prev) => { prev[prev.length - 1].text = ""; @@ -240,6 +280,13 @@ export default function Home() { prev[prev.length - 1].text += value; return [...prev]; }); + + if (isAtBottom.current) { + const messageContainer = messageContainerRef.current; + if (messageContainer) { + messageContainer.scrollTop = messageContainer.scrollHeight; + } + } } } } @@ -285,7 +332,10 @@ export default function Home() {

)} -
+
{messages.map((message, i) => (