diff --git a/src/components/message/MessagingPage.tsx b/src/components/message/MessagingPage.tsx index 4616f24..f91b2ea 100644 --- a/src/components/message/MessagingPage.tsx +++ b/src/components/message/MessagingPage.tsx @@ -118,6 +118,7 @@ export function MessagingPage() { const [conversationAvatarUrls, setConversationAvatarUrls] = useState>({}); const messagesContainerRef = useRef(null); const messagesEndRef = useRef(null); + const justSwitchedConversationRef = useRef(false); const currentUserId = session?.user?.id; const userRole = session?.user?.role; @@ -296,14 +297,21 @@ export function MessagingPage() { } }; - // Scroll to bottom when new message arrives or conversation changes + // Mark that we switched conversations so we scroll after messages load useEffect(() => { - scrollToBottom(); + justSwitchedConversationRef.current = true; }, [currentConversation?.id]); - // Scroll to bottom when new messages are added (not when loading more) + // Scroll to bottom when messages load after opening a chat, or when a new message arrives useEffect(() => { if (messages.length > 0) { + // After opening/switching a conversation — scroll once messages are rendered + if (justSwitchedConversationRef.current) { + justSwitchedConversationRef.current = false; + setTimeout(scrollToBottom, 50); + return; + } + // New incoming message — scroll if it's very recent const lastMessage = messages[messages.length - 1]; const isVeryRecent = new Date().getTime() - new Date(lastMessage.createdAt).getTime() < 2000; if (isVeryRecent) { @@ -312,6 +320,13 @@ export function MessagingPage() { } }, [messages]); + // Scroll to bottom when typing indicator appears + useEffect(() => { + if (typingUsers.size > 0) { + scrollToBottom(); + } + }, [typingUsers]); + // Filter conversations by search const filteredConversations = conversations.filter((conv) => conv.otherParty?.name?.toLowerCase().includes(searchQuery.toLowerCase()) @@ -616,10 +631,15 @@ export function MessagingPage() { {typingUserNames.length > 0 && (
-
- - - +
+
+ + + +
+ + {formatMessageTime(new Date().toISOString())} +
diff --git a/src/services/socket.service.ts b/src/services/socket.service.ts index 44f9e68..286b38f 100644 --- a/src/services/socket.service.ts +++ b/src/services/socket.service.ts @@ -161,7 +161,13 @@ class SocketService { resolve({ success: false, error: 'Not connected' }); return; } + + const timeout = setTimeout(() => { + resolve({ success: false, error: 'Socket timeout' }); + }, 10000); + this.socket.emit('join_conversation', { conversationId }, (response: { success: boolean; error?: string }) => { + clearTimeout(timeout); resolve(response); }); }); @@ -183,10 +189,17 @@ class SocketService { resolve({ success: false, error: 'Not connected' }); return; } + + // Timeout fallback in case acknowledgement never arrives + const timeout = setTimeout(() => { + resolve({ success: false, error: 'Socket timeout' }); + }, 10000); + this.socket.emit( 'send_message', { conversationId, message }, (response: { success: boolean; message?: Message; error?: string }) => { + clearTimeout(timeout); resolve(response); } ); @@ -213,7 +226,13 @@ class SocketService { resolve({ success: false, error: 'Not connected' }); return; } + + const timeout = setTimeout(() => { + resolve({ success: false, error: 'Socket timeout' }); + }, 10000); + this.socket.emit('mark_read', { conversationId }, (response: { success: boolean; error?: string }) => { + clearTimeout(timeout); resolve(response); }); });