fix read state tracking

This commit is contained in:
2026-04-19 17:33:48 -06:00
parent a85e295c30
commit f8c985efb5
2 changed files with 29 additions and 3 deletions

View File

@@ -14,8 +14,10 @@ import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.atomic.AtomicInteger;
@@ -43,6 +45,7 @@ public class ChatRealtimeClient implements WebSocket.Listener {
private volatile String currentStatus = "Chat disconnected";
private final Map<Long, ConversationResponse> globalConversations = new HashMap<>();
private final Set<Long> readConversationIds = new HashSet<>();
private final List<Consumer<Boolean>> notificationListeners = new ArrayList<>();
private boolean lastNotificationState = false;
@@ -79,10 +82,24 @@ public class ChatRealtimeClient implements WebSocket.Listener {
if (conv != null) {
conv.setLastSenderId(senderId);
}
readConversationIds.add(conversationId);
}
updateNotificationState();
}
public void markConversationRead(Long conversationId) {
synchronized (lock) {
readConversationIds.add(conversationId);
}
updateNotificationState();
}
public boolean isConversationRead(Long conversationId) {
synchronized (lock) {
return readConversationIds.contains(conversationId);
}
}
public boolean hasActionableChats() {
synchronized (lock) {
UserSession session = UserSession.getInstance();
@@ -98,7 +115,8 @@ public class ChatRealtimeClient implements WebSocket.Listener {
// Needs reply (assigned to me and last sender was someone else - customer)
if (currentUserId != null && currentUserId.equals(conv.getStaffId())) {
if (conv.getLastSenderId() != null && !conv.getLastSenderId().equals(currentUserId)) {
if (conv.getLastSenderId() != null && !conv.getLastSenderId().equals(currentUserId)
&& !readConversationIds.contains(conv.getId())) {
return true;
}
}
@@ -302,6 +320,7 @@ public class ChatRealtimeClient implements WebSocket.Listener {
conversationsSubscriptionId = null;
conversationMessagesSubscriptionId = null;
globalConversations.clear();
readConversationIds.clear();
updateNotificationState();
}
@@ -352,13 +371,15 @@ public class ChatRealtimeClient implements WebSocket.Listener {
.notifyNewMessage(message.getSenderDisplayName(), message.getContent());
}
// Also update globalConversation last sender if this is the active conversation
synchronized (lock) {
ConversationResponse conv = globalConversations.get(message.getConversationId());
if (conv != null) {
conv.setLastMessage(message.getContent());
conv.setLastSenderId(message.getSenderId());
}
if (message.getSenderId() != null && !message.getSenderId().equals(currentUserId)) {
readConversationIds.remove(message.getConversationId());
}
}
updateNotificationState();
} else {

View File

@@ -282,6 +282,8 @@ public class ChatController {
realtimeClient.subscribeToConversation(newValue.getId());
}
updateChatState(newValue);
realtimeClient.markConversationRead(newValue.getId());
refreshSections();
}
private void updateChatState(ConversationResponse conv) {
@@ -320,7 +322,8 @@ public class ChatController {
Long currentUserId = session.getUserId();
boolean needsPickup = item.getHumanRequestedAt() != null && item.getStaffId() == null;
boolean needsReply = currentUserId != null && currentUserId.equals(item.getStaffId())
&& item.getLastSenderId() != null && !item.getLastSenderId().equals(currentUserId);
&& item.getLastSenderId() != null && !item.getLastSenderId().equals(currentUserId)
&& !ChatRealtimeClient.getInstance().isConversationRead(item.getId());
if (needsPickup || needsReply) {
title.setStyle("-fx-font-weight: bold; -fx-text-fill: #FF6B6B;");
@@ -460,6 +463,8 @@ public class ChatController {
vbMessages.getChildren().add(createMessageBubble(message));
if (message.getId() != null) renderedMessageIds.add(message.getId());
scrollMessagesToBottom();
realtimeClient.markConversationRead(message.getConversationId());
refreshSections();
}
} catch (Exception e) {
ActivityLogger.getInstance().logException(