Просмотр исходного кода

fix: preserve scroll position when user scrolls up in chat window

Auto-scroll to bottom only when user is already near the bottom (<100px).
If user scrolls up to read history, new messages don't snap them back.
Sending a message always resets to bottom-tracking mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tony T 6 дней назад
Родитель
Сommit
be2a1e2454
1 измененных файлов с 16 добавлено и 3 удалено
  1. 16 3
      client/src/components/ChatbotWidgetLive.tsx

+ 16 - 3
client/src/components/ChatbotWidgetLive.tsx

@@ -22,6 +22,8 @@ export default function ChatbotWidgetLive() {
   const [isTyping, setIsTyping] = useState(false);
   const [showQuickReplies, setShowQuickReplies] = useState(true);
   const messagesEndRef = useRef<HTMLDivElement>(null);
+  const scrollContainerRef = useRef<HTMLDivElement>(null);
+  const isNearBottomRef = useRef(true);
 
   const startSession = trpc.chat.startSession.useMutation({
     onSuccess: (data) => {
@@ -55,11 +57,19 @@ export default function ChatbotWidgetLive() {
     }
   }, [messagesData]);
 
-  // Auto-scroll
+  // Auto-scroll — only when user is already near the bottom
   useEffect(() => {
-    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+    if (isNearBottomRef.current) {
+      messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+    }
   }, [localMessages, isTyping]);
 
+  const handleScroll = () => {
+    const el = scrollContainerRef.current;
+    if (!el) return;
+    isNearBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 100;
+  };
+
   const handleOpen = () => {
     setIsOpen(true);
     if (!sessionId) {
@@ -74,6 +84,9 @@ export default function ChatbotWidgetLive() {
     setIsTyping(true);
     setShowQuickReplies(false);
 
+    // Always scroll to bottom when user sends a message
+    isNearBottomRef.current = true;
+
     // Optimistically add user message
     setLocalMessages(prev => [...prev, {
       id: Date.now(),
@@ -150,7 +163,7 @@ export default function ChatbotWidgetLive() {
           </div>
 
           {/* Messages */}
-          <div className="flex-1 overflow-y-auto p-3 space-y-3">
+          <div ref={scrollContainerRef} onScroll={handleScroll} className="flex-1 overflow-y-auto p-3 space-y-3">
             {startSession.isPending ? (
               <div className="flex items-center justify-center py-8">
                 <Loader2 className="w-5 h-5 animate-spin" style={{ color: "#14532D" }} />