diff --git a/Presentation/HushianWebApp/Pages/FromUserSide/UserCP.razor b/Presentation/HushianWebApp/Pages/FromUserSide/UserCP.razor index 9b15447..dd8e1a8 100644 --- a/Presentation/HushianWebApp/Pages/FromUserSide/UserCP.razor +++ b/Presentation/HushianWebApp/Pages/FromUserSide/UserCP.razor @@ -171,6 +171,7 @@ LastOpenChat?.Responses.Add(model); LastOpenChat.LastText = MsgInput; await Task.Yield(); + // Scroll to bottom for user's own messages await JS.InvokeVoidAsync("scrollToBottom", "B1"); MsgInput = string.Empty; } @@ -181,7 +182,6 @@ { _shouldObserveVisibility = false; await JS.InvokeVoidAsync("observeVisibility", DotNetObjectReference.Create(this)); - await JS.InvokeVoidAsync("scrollToTarget"); } } protected override async Task OnInitializedAsync() @@ -236,9 +236,27 @@ LastOpenChat = await ChatService.GetLastOpenChatInCompany(CompanyID); if (LastOpenChat != null) { + // Check if there are unread messages + var hasUnreadMessages = LastOpenChat.Responses?.Any(m => !m.IsRead && m.Type != Common.Enums.ConversationType.UE) ?? false; + + if (hasUnreadMessages) + { + _shouldObserveVisibility = true; + StateHasChanged(); + // Scroll to target (new message separator) after render + await Task.Delay(100); + await JS.InvokeVoidAsync("scrollToTarget"); + } + else + { + // If no unread messages, scroll to bottom + await Task.Delay(100); + await JS.InvokeVoidAsync("scrollToBottom", "B1"); + } + + // Always set up visibility observation for chat bubbles _shouldObserveVisibility = true; StateHasChanged(); - } } } @@ -250,10 +268,23 @@ { msg.IsRead = true; await chatService.MarkAsReadChatItemAsync(id); + // StateHasChanged(); } - StateHasChanged(); await Task.CompletedTask; } + + // Method to handle new messages from other users + public async Task HandleNewMessage() + { + if (LastOpenChat?.Responses != null) + { + var hasUnreadMessages = LastOpenChat.Responses.Any(m => !m.IsRead && m.Type != Common.Enums.ConversationType.UE); + if (hasUnreadMessages) + { + await JS.InvokeVoidAsync("autoScrollToNewMessage"); + } + } + } async Task Logout() { await baseController.RemoveToken(); @@ -748,4 +779,51 @@ el.scrollTop = el.scrollHeight; } }; + + // Scroll to target element (new message separator) + window.scrollToTarget = () => { + const targetElement = document.getElementById('target'); + if (targetElement) { + const chatContainer = document.getElementById('B1'); + if (chatContainer) { + // Calculate the position of target element relative to chat container + const targetRect = targetElement.getBoundingClientRect(); + const containerRect = chatContainer.getBoundingClientRect(); + const relativeTop = targetRect.top - containerRect.top; + + // Scroll to show the target element with some padding + const scrollPosition = chatContainer.scrollTop + relativeTop - 100; // 100px padding + chatContainer.scrollTo({ + top: scrollPosition, + behavior: 'smooth' + }); + } + } + }; + + // Observe visibility of chat bubbles using existing scroll-visibility.js approach + window.observeVisibility = (dotNetHelper) => { + const elements = document.querySelectorAll(".chat-bubble[data-id]"); + const observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const id = entry.target.getAttribute("data-id"); + dotNetHelper.invokeMethodAsync("MarkAsRead", parseInt(id)); + observer.unobserve(entry.target); // Don't observe again + } + }); + }, { + threshold: 0.6 // 60% of the message must be visible + }); + + elements.forEach(el => observer.observe(el)); + }; + + // Auto scroll to target when new messages arrive + window.autoScrollToNewMessage = () => { + setTimeout(() => { + window.scrollToTarget(); + }, 100); // Small delay to ensure DOM is updated + }; + \ No newline at end of file