diff --git a/frontend/components/messenger/ChatPanel.tsx b/frontend/components/messenger/ChatPanel.tsx index c3119ddc..df6ca66a 100644 --- a/frontend/components/messenger/ChatPanel.tsx +++ b/frontend/components/messenger/ChatPanel.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; import { MessageSquare, Pencil, Check, X, ChevronsDown } from "lucide-react"; import { useMessages, useMarkAsRead, useUpdateRoom } from "@/hooks/useMessenger"; import { useAuth } from "@/hooks/useAuth"; @@ -36,21 +36,18 @@ export function ChatPanel({ room }: ChatPanelProps) { const lastMessageId = messages?.[messages.length - 1]?.id; - // Hide messages until scrolled to bottom (prevents visible jump on room open) - useEffect(() => { + // useLayoutEffect: fires before browser paint → hide + scroll synchronously + useLayoutEffect(() => { + const el = scrollRef.current; + if (!el) return; setScrollReady(false); - }, [selectedRoomId]); + el.scrollTop = el.scrollHeight; + // Reveal after scroll is applied + requestAnimationFrame(() => setScrollReady(true)); + }, [selectedRoomId, lastMessageId]); - // Scroll to bottom when messages load or room changes - // Two-pass: immediate rAF + 600ms delayed (for async image loads) + // Second pass: re-scroll after async images load useEffect(() => { - requestAnimationFrame(() => { - const el = scrollRef.current; - if (el) { - el.scrollTop = el.scrollHeight; - setScrollReady(true); - } - }); const t = setTimeout(() => { const el = scrollRef.current; if (el) el.scrollTop = el.scrollHeight;