Эх сурвалжийг харах

fix: smart scroll in chat panel — stop auto-jumping on poll refresh

Split scroll logic into two effects:
- conversationId changes → scroll to bottom once (instant, 80ms delay for render)
- messagesData poll → only scroll if user is already within 120px of bottom

Added scrollWrapperRef to query the Radix ScrollArea viewport so we can
read scrollTop/scrollHeight/clientHeight without hacking the component API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tony T 19 цаг өмнө
parent
commit
69c5340a35

+ 31 - 2
client/src/pages/AgentDashboard.tsx

@@ -229,6 +229,8 @@ function ChatPanel({
 }) {
   const [replyText, setReplyText] = useState("");
   const messagesEndRef = useRef<HTMLDivElement>(null);
+  const scrollWrapperRef = useRef<HTMLDivElement>(null);
+  const prevMsgCountRef = useRef(0);
   const utils = trpc.useUtils();
 
   const { data: messagesData, isLoading } = trpc.agent.messages.useQuery(
@@ -251,8 +253,33 @@ function ChatPanel({
     },
   });
 
+  // Initial open: scroll to bottom immediately when conversation changes
   useEffect(() => {
-    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+    prevMsgCountRef.current = 0;
+    const timer = setTimeout(() => {
+      messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
+    }, 80);
+    return () => clearTimeout(timer);
+  }, [conversationId]);
+
+  // Polling update: only scroll to bottom if user is already near the bottom
+  useEffect(() => {
+    if (!messagesData) return;
+    const count = messagesData.length;
+    if (count > prevMsgCountRef.current && prevMsgCountRef.current > 0) {
+      // New message arrived — check if user is near bottom before scrolling
+      const viewport = scrollWrapperRef.current?.querySelector(
+        "[data-radix-scroll-area-viewport]"
+      ) as HTMLElement | null;
+      if (viewport) {
+        const { scrollTop, scrollHeight, clientHeight } = viewport;
+        const isNearBottom = scrollHeight - scrollTop - clientHeight < 120;
+        if (isNearBottom) {
+          messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+        }
+      }
+    }
+    prevMsgCountRef.current = count;
   }, [messagesData]);
 
   const handleSend = () => {
@@ -332,7 +359,8 @@ function ChatPanel({
       </div>
 
       {/* Messages */}
-      <ScrollArea className="flex-1 p-4">
+      <div ref={scrollWrapperRef} className="flex-1 min-h-0">
+      <ScrollArea className="h-full p-4">
         <div className="space-y-3">
           {isLoading ? (
             <div className="text-center py-8 text-sm" style={{ color: "#a8a29e" }}>Loading messages...</div>
@@ -342,6 +370,7 @@ function ChatPanel({
           <div ref={messagesEndRef} />
         </div>
       </ScrollArea>
+      </div>
 
       {/* Reply input */}
       {conversation?.status !== "closed" && (