From 7847bf19ccb4d90c5d112cbd17ac317ccd21d7b8 Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Tue, 14 Apr 2026 23:34:07 -0600 Subject: [PATCH 1/2] fixed chat messaging on same account with different devices --- .../petstoremobile/fragments/ChatFragment.java | 4 +--- .../viewmodels/ChatListViewModel.java | 5 +++++ .../websocket/StompChatManager.java | 1 + .../controllers/ChatController.java | 16 ++++++++++------ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java index 47f755a2..0aee1de5 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java @@ -470,9 +470,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis public void onMessageReceived(MessageDTO dto) { requireActivity().runOnUiThread(() -> { if (activeConversationId != null && activeConversationId.equals(dto.getConversationId())) { - if (!tokenManager.getUserId().equals(dto.getSenderId())) { - viewModel.addMessageLocally(dto); - } + viewModel.addMessageLocally(dto); } viewModel.updateConversationLocally(new ConversationDTO(dto.getConversationId(), 0L, 0L, dto.getContent(), "")); }); diff --git a/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java b/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java index 88123907..f0bad647 100644 --- a/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java +++ b/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java @@ -133,6 +133,11 @@ public class ChatListViewModel extends ViewModel { public void addMessageLocally(MessageDTO dto) { List current = new ArrayList<>(messageList.getValue()); + if (dto.getId() != null) { + for (Message m : current) { + if (dto.getId().equals(m.getId())) return; + } + } current.add(dtoToModel(dto)); messageList.setValue(current); } diff --git a/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java b/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java index f0f5d7ec..3aad4b8d 100644 --- a/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java +++ b/android/app/src/main/java/com/example/petstoremobile/websocket/StompChatManager.java @@ -95,6 +95,7 @@ public class StompChatManager { headers.put("Authorization", "Bearer " + authToken); stompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, webSocketUrl, headers); + stompClient.withClientHeartbeat(0).withServerHeartbeat(0); compositeDisposable.add( stompClient.lifecycle() diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java index 17033f16..c746db2e 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/ChatController.java @@ -35,9 +35,11 @@ import java.io.File; import java.time.format.DateTimeFormatter; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; public class ChatController { @@ -87,6 +89,7 @@ public class ChatController { private final ObservableList activeConversations = FXCollections.observableArrayList(); private final ObservableList closedConversations = FXCollections.observableArrayList(); private final Map customerLabels = new HashMap<>(); + private final Set renderedMessageIds = new HashSet<>(); private final ChatRealtimeClient realtimeClient = ChatRealtimeClient.getInstance(); private ConversationResponse selectedConversation; private File selectedAttachmentFile; @@ -131,12 +134,7 @@ public class ChatController { }); realtimeClient.setConversationListener(conversation -> Platform.runLater(() -> upsertConversation(conversation))); - realtimeClient.setMessageListener(message -> Platform.runLater(() -> { - Long myId = UserSession.getInstance().getUserId(); - if (message.getSenderId() == null || !message.getSenderId().equals(myId)) { - appendMessageIfSelected(message); - } - })); + realtimeClient.setMessageListener(message -> Platform.runLater(() -> appendMessageIfSelected(message))); realtimeClient.setStatusListener(status -> Platform.runLater(() -> lblChatStatus.setText(status))); realtimeClient.subscribeToConversations(); @@ -419,9 +417,11 @@ public class ChatController { private void renderMessages(List messages) { vbMessages.getChildren().clear(); + renderedMessageIds.clear(); for (MessageResponse message : messages) { try { vbMessages.getChildren().add(createMessageBubble(message)); + if (message.getId() != null) renderedMessageIds.add(message.getId()); } catch (Exception e) { ActivityLogger.getInstance().logException( "ChatController.renderMessages", @@ -436,7 +436,11 @@ public class ChatController { try { upsertConversationForMessage(message); if (selectedConversation != null && selectedConversation.getId().equals(message.getConversationId())) { + if (message.getId() != null && renderedMessageIds.contains(message.getId())) { + return; + } vbMessages.getChildren().add(createMessageBubble(message)); + if (message.getId() != null) renderedMessageIds.add(message.getId()); scrollMessagesToBottom(); } } catch (Exception e) { From 111498255328aca90308f5d83a96b3287ce2f19d Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Tue, 14 Apr 2026 23:42:23 -0600 Subject: [PATCH 2/2] added time stamp and sender name to android chat --- .../adapters/MessageAdapter.java | 51 +++++++++++++++---- .../petstoremobile/dtos/MessageDTO.java | 12 +++++ .../fragments/ChatFragment.java | 2 + .../petstoremobile/models/Message.java | 8 +++ .../viewmodels/ChatListViewModel.java | 2 + .../main/res/layout/item_message_received.xml | 17 +++++++ .../src/main/res/layout/item_message_sent.xml | 18 +++++++ 7 files changed, 101 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/adapters/MessageAdapter.java b/android/app/src/main/java/com/example/petstoremobile/adapters/MessageAdapter.java index eb9d9c19..052f34cb 100644 --- a/android/app/src/main/java/com/example/petstoremobile/adapters/MessageAdapter.java +++ b/android/app/src/main/java/com/example/petstoremobile/adapters/MessageAdapter.java @@ -16,7 +16,10 @@ import com.example.petstoremobile.R; import com.example.petstoremobile.databinding.ItemMessageReceivedBinding; import com.example.petstoremobile.databinding.ItemMessageSentBinding; import com.example.petstoremobile.models.Message; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; +import java.util.Locale; public class MessageAdapter extends RecyclerView.Adapter { @@ -29,6 +32,7 @@ public class MessageAdapter extends RecyclerView.Adapter messages; private Long currentUserId; + private Long staffId; private String token; private String baseUrl; private OnAttachmentClickListener attachmentClickListener; @@ -50,6 +54,11 @@ public class MessageAdapter extends RecyclerView.Adapter { if (listener != null) listener.onAttachmentClick(m); }; @@ -124,8 +134,10 @@ public class MessageAdapter extends RecyclerView.Adapter { @@ -144,7 +155,29 @@ public class MessageAdapter extends RecyclerView.Adapter 19 ? timestamp.substring(0, 19) : timestamp; + SimpleDateFormat input = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); + Date date = input.parse(normalized); + return new SimpleDateFormat("MMM d, HH:mm", Locale.getDefault()).format(date); + } catch (Exception e) { + return ""; + } + } + private static void displayAttachment(Message m, ImageView iv, TextView tvName, String token, String baseUrl) { // Check if there's an attachment by looking at name or mime type if (m.getAttachmentName() != null || m.getAttachmentMimeType() != null) { diff --git a/android/app/src/main/java/com/example/petstoremobile/dtos/MessageDTO.java b/android/app/src/main/java/com/example/petstoremobile/dtos/MessageDTO.java index a75d7812..8302ba86 100644 --- a/android/app/src/main/java/com/example/petstoremobile/dtos/MessageDTO.java +++ b/android/app/src/main/java/com/example/petstoremobile/dtos/MessageDTO.java @@ -34,6 +34,12 @@ public class MessageDTO { @SerializedName("attachmentSizeBytes") private Long attachmentSizeBytes; + @SerializedName("senderRole") + private String senderRole; + + @SerializedName("senderDisplayName") + private String senderDisplayName; + public MessageDTO() {} public Long getId() { return id; } @@ -65,4 +71,10 @@ public class MessageDTO { public Long getAttachmentSizeBytes() { return attachmentSizeBytes; } public void setAttachmentSizeBytes(Long attachmentSizeBytes) { this.attachmentSizeBytes = attachmentSizeBytes; } + + public String getSenderRole() { return senderRole; } + public void setSenderRole(String senderRole) { this.senderRole = senderRole; } + + public String getSenderDisplayName() { return senderDisplayName; } + public void setSenderDisplayName(String senderDisplayName) { this.senderDisplayName = senderDisplayName; } } \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java index 0aee1de5..88bb0f09 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java @@ -362,6 +362,8 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis binding.tvChatTitle.setText(chat.getCustomerName()); binding.chatDrawerLayout.closeDrawer(GravityCompat.START); + messageAdapter.setStaffId(chat.getStaffId()); + if (stompChatManager != null) stompChatManager.subscribeToConversation(activeConversationId); viewModel.loadMessageHistory(activeConversationId); } diff --git a/android/app/src/main/java/com/example/petstoremobile/models/Message.java b/android/app/src/main/java/com/example/petstoremobile/models/Message.java index 082baa58..e6547d23 100644 --- a/android/app/src/main/java/com/example/petstoremobile/models/Message.java +++ b/android/app/src/main/java/com/example/petstoremobile/models/Message.java @@ -11,6 +11,8 @@ public class Message { private String attachmentName; private String attachmentMimeType; private Long attachmentSizeBytes; + private String senderRole; + private String senderDisplayName; public Message() {} @@ -49,4 +51,10 @@ public class Message { public Long getAttachmentSizeBytes() { return attachmentSizeBytes; } public void setAttachmentSizeBytes(Long attachmentSizeBytes) { this.attachmentSizeBytes = attachmentSizeBytes; } + + public String getSenderRole() { return senderRole; } + public void setSenderRole(String senderRole) { this.senderRole = senderRole; } + + public String getSenderDisplayName() { return senderDisplayName; } + public void setSenderDisplayName(String senderDisplayName) { this.senderDisplayName = senderDisplayName; } } \ No newline at end of file diff --git a/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java b/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java index f0bad647..4d14a3b4 100644 --- a/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java +++ b/android/app/src/main/java/com/example/petstoremobile/viewmodels/ChatListViewModel.java @@ -173,6 +173,8 @@ public class ChatListViewModel extends ViewModel { m.setIsRead(dto.getIsRead()); m.setAttachmentUrl(dto.getAttachmentUrl()); m.setAttachmentName(dto.getAttachmentName()); + m.setSenderRole(dto.getSenderRole()); + m.setSenderDisplayName(dto.getSenderDisplayName()); m.setAttachmentMimeType(dto.getAttachmentMimeType()); m.setAttachmentSizeBytes(dto.getAttachmentSizeBytes()); return m; diff --git a/android/app/src/main/res/layout/item_message_received.xml b/android/app/src/main/res/layout/item_message_received.xml index b2062b7b..bb3e1deb 100644 --- a/android/app/src/main/res/layout/item_message_received.xml +++ b/android/app/src/main/res/layout/item_message_received.xml @@ -14,6 +14,15 @@ android:padding="8dp" android:maxWidth="300dp"> + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/item_message_sent.xml b/android/app/src/main/res/layout/item_message_sent.xml index 19b221b0..4a55bcd9 100644 --- a/android/app/src/main/res/layout/item_message_sent.xml +++ b/android/app/src/main/res/layout/item_message_sent.xml @@ -14,6 +14,16 @@ android:padding="8dp" android:maxWidth="300dp"> + + + + \ No newline at end of file