From baa8be67cb86e4abecb86fd3c058e5a0e3e849ae Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 23 Jun 2026 12:15:34 +0000 Subject: [PATCH] feat(chat): autoscroll chat to newest message when pinned to bottom Co-authored-by: Chaitanya Mittal --- apps/web/src/components/emoji-chat.tsx | 40 +++++++++++++++++++++++++ apps/web/src/components/room-client.tsx | 12 ++------ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/emoji-chat.tsx b/apps/web/src/components/emoji-chat.tsx index c033e2c..2a74818 100644 --- a/apps/web/src/components/emoji-chat.tsx +++ b/apps/web/src/components/emoji-chat.tsx @@ -4,6 +4,46 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import data from "@emoji-mart/data"; import Picker from "@emoji-mart/react"; +import type { ChatMessage } from "@together/shared"; + +/** + * Scrollable chat log that keeps the newest message in view. Auto-scrolls only + * when the viewer is already pinned to the bottom, so scrolling up to re-read + * history is not interrupted by incoming messages. + */ +export function ChatMessages({ messages }: { messages: ChatMessage[] }) { + const containerRef = useRef(null); + const pinnedToBottomRef = useRef(true); + + const handleScroll = useCallback(() => { + const el = containerRef.current; + if (!el) return; + const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight; + pinnedToBottomRef.current = distanceFromBottom < 48; + }, []); + + useEffect(() => { + const el = containerRef.current; + if (!el || !pinnedToBottomRef.current) return; + el.scrollTop = el.scrollHeight; + }, [messages.length]); + + return ( +
+ {messages.map((msg) => ( +
+ {msg.senderName} + · + {msg.body} +
+ ))} +
+ ); +} interface EmojiPickerButtonProps { onSelect: (emoji: string) => void; diff --git a/apps/web/src/components/room-client.tsx b/apps/web/src/components/room-client.tsx index ab61885..edb725b 100644 --- a/apps/web/src/components/room-client.tsx +++ b/apps/web/src/components/room-client.tsx @@ -33,7 +33,7 @@ import { } from "lucide-react"; import { useRoomSocket } from "@/hooks/use-room-socket"; import { useYouTubePlayer } from "@/hooks/use-youtube-player"; -import { ChatInput } from "@/components/emoji-chat"; +import { ChatInput, ChatMessages } from "@/components/emoji-chat"; import { PlaybackSeekBar } from "@/components/playback-seek-bar"; import { SettingsDrawer } from "@/components/room-settings"; import { useUserPreferences } from "@/hooks/use-user-preferences"; @@ -410,15 +410,7 @@ export function RoomClient({ const chatPanel = ( <> -
- {roomState?.chat.map((msg) => ( -
- {msg.senderName} - · - {msg.body} -
- ))} -
+ send({ type: "chat", body })} slowModeSeconds={settings?.slowModeSeconds}