From a2816972d1a7e9529b533b561e23ba484840c64b Mon Sep 17 00:00:00 2001 From: Partow Roshani Date: Fri, 1 Aug 2025 14:10:09 +0330 Subject: [PATCH] fix open private chats --- .../Client/ActionHandler.java | 308 ++++++++++++++++-- .../telegramfinalproject/Client/Session.java | 5 +- .../Database/MessageDatabase.java | 38 +++ .../Database/PrivateChatDatabase.java | 112 ++++++- .../Models/ChatEntry.java | 7 + .../Models/ContactEntry.java | 44 +++ .../Models/PrivateChat.java | 2 +- .../Server/ClientHandler.java | 267 ++++++++++++--- 8 files changed, 718 insertions(+), 65 deletions(-) create mode 100644 src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index 4d5ee15..ddfd379 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -2,7 +2,9 @@ package org.to.telegramfinalproject.Client; import org.json.JSONArray; import org.json.JSONObject; +import org.to.telegramfinalproject.Database.PrivateChatDatabase; import org.to.telegramfinalproject.Models.ChatEntry; +import org.to.telegramfinalproject.Models.ContactEntry; import org.to.telegramfinalproject.Models.SearchRequestModel; import org.to.telegramfinalproject.Models.SearchResultModel; @@ -443,7 +445,8 @@ public class ActionHandler { JSONArray chatListJson = Session.currentUser.getJSONArray("chat_list"); JSONArray Archived = Session.currentUser.getJSONArray("archived_chat_list"); - JSONArray Active = Session.currentUser.getJSONArray("active_chat_lis"); + JSONArray Active = Session.currentUser.getJSONArray("active_chat_list"); + JSONArray contactList = Session.currentUser.getJSONArray("contact_list"); List chatList = new ArrayList<>(); @@ -459,9 +462,13 @@ public class ActionHandler { chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")), chat.optBoolean("is_owner", false), chat.optBoolean("is_admin", false) + + ); - + if (chat.has("other_user_id")) { + entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id"))); + } chatList.add(entry); @@ -481,6 +488,9 @@ public class ActionHandler { chat.optBoolean("is_owner", false), chat.optBoolean("is_admin", false) ); + if (chat.has("other_user_id")) { + entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id"))); // šŸ‘ˆ اضافه کردن برای private chat + } archivedChats.add(entry); } @@ -499,11 +509,28 @@ public class ActionHandler { chat.optBoolean("is_owner", false), chat.optBoolean("is_admin", false) ); + if (chat.has("other_user_id")) { + entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id"))); // šŸ‘ˆ اضافه کردن برای private chat + } activeChats.add(entry); } + Session.contactEntries.clear(); + for (Object obj : contactList) { + JSONObject c = (JSONObject) obj; + + ContactEntry entry = new ContactEntry( + UUID.fromString(c.getString("contact_id")), + c.getString("user_id"), + c.getString("profile_name"), + c.optString("image_url", ""), + c.optBoolean("is_blocked", false) + ); + Session.contactEntries.add(entry); + } + @@ -751,7 +778,8 @@ public class ActionHandler { System.out.println("2. Search"); System.out.println("3. Create Channel"); System.out.println("4. Create group"); - System.out.println("5. Logout"); + System.out.println("5. View contacts"); + System.out.println("6. Logout"); System.out.print("Choose an option: "); String choice = scanner.nextLine(); @@ -764,7 +792,8 @@ public class ActionHandler { case "2" -> search(); case "3" -> createChannel(); case "4" -> createGroup(); - case "5" -> { + case "5" -> showContactList(); + case "6" -> { logout(); return; } @@ -773,6 +802,109 @@ public class ActionHandler { } } + public void showContactList() { + List contacts = Session.contactEntries; + if (contacts.isEmpty()) { + System.out.println("šŸ“­ You have no contacts."); + return; + } + + System.out.println("šŸ‘„ Your Contacts:"); + for (int i = 0; i < contacts.size(); i++) { + System.out.println((i + 1) + ". " + contacts.get(i)); + } + + System.out.print("Select a contact (0 to go back): "); + int choice = scanner.nextInt(); + scanner.nextLine(); + + if (choice == 0) return; + if (choice < 1 || choice > contacts.size()) { + System.out.println("āŒ Invalid choice."); + return; + } + + ContactEntry selected = contacts.get(choice - 1); + System.out.println("\nšŸ“‡ What do you want to do with " + selected.getProfileName() + "?"); + System.out.println("1. View Profile"); + System.out.println("2. Send Message"); + System.out.print("Enter your choice: "); + int action = scanner.nextInt(); + scanner.nextLine(); + + switch (action) { + case 1 -> viewProfile(selected.getContactId()); + case 2 -> startPrivateChat(selected); + default -> System.out.println("āŒ Invalid option."); + } + } + + private void viewProfile(UUID targetId) { + JSONObject req = new JSONObject(); + req.put("action", "view_profile"); + req.put("target_id", targetId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null || !res.getString("status").equals("success")) { + System.out.println("āŒ Failed to fetch profile."); + return; + } + + JSONObject data = res.getJSONObject("data"); + String profileName = data.getString("profile_name"); + String userId = data.getString("user_id"); + String bio = data.optString("bio", "(no bio)"); + String imageUrl = data.optString("image_url", "(no image)"); + boolean isOnline = data.getBoolean("is_online"); + String lastSeen = data.getString("last_seen"); + + System.out.println("\nšŸ“„ Profile Info:"); + System.out.println("Name: " + profileName); + System.out.println("User ID: " + userId); + System.out.println("Bio: " + bio); + System.out.println("Image: " + imageUrl); + System.out.println("Status: " + (isOnline ? "🟢 Online" : "šŸ”˜ Last seen at " + lastSeen)); + } + + + private void startPrivateChat(ContactEntry contact) { + UUID myId = UUID.fromString(Session.currentUser.getString("internal_uuid")); + UUID contactId = contact.getContactId(); + + JSONObject req = new JSONObject(); + req.put("action", "get_or_create_private_chat"); + req.put("user1", myId.toString()); + req.put("user2", contactId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null || !res.getString("status").equals("success")) { + System.out.println("āŒ Failed to create or fetch private chat."); + return; + } + + JSONObject data = res.getJSONObject("data"); + UUID chatId = UUID.fromString(data.getString("chat_id")); + + ChatEntry entry = new ChatEntry( + chatId, + contact.getUserId(), + contact.getProfileName(), + contact.getImageUrl(), + "private", + null, + false, + false + ); + entry.setOtherUserId(contactId); + + Session.chatList.add(0, entry); // اضافه به Ų§ŁˆŁ„ Ł„ŪŒŲ³ŲŖ + System.out.println("āœ… Chat with " + contact.getProfileName() + " started."); + + openChat(entry); // šŸ‘ˆ Ł…Ų³ŲŖŁ‚ŪŒŁ… وارد چت ؓو (اختیاری) + } + + + // public void showChatListAndSelect() { // // @@ -878,10 +1010,54 @@ public class ActionHandler { private void openChat(ChatEntry chat) { + + //for private chats only + if (chat.getType().equalsIgnoreCase("private")) { + JSONObject reqTarget = new JSONObject(); + reqTarget.put("action", "get_private_chat_target"); + reqTarget.put("chat_id", chat.getId()); + + JSONObject resTarget = sendWithResponse(reqTarget); + if (resTarget == null || !resTarget.getString("status").equals("success")) { + System.out.println("āŒ Failed to fetch target user for private chat."); + return; + } + String otherUserId = resTarget.getJSONObject("data").getString("target_id"); + chat.setOtherUserId(UUID.fromString(otherUserId)); + } + + + JSONObject req = new JSONObject(); req.put("action", "get_messages"); - req.put("receiver_id", chat.getId()); + req.put("receiver_id",chat.getId()); req.put("receiver_type", chat.getType()); +// String type = chat.getType(); +// UUID chatId = chat.getId(); +// if (type.equals("private")) { +// UUID myId = UUID.fromString(Session.currentUser.getString("internal_uuid")); +// UUID otherUserId = chat.getId(); +// +// JSONObject getChatIdReq = new JSONObject(); +// getChatIdReq.put("action", "get_or_create_private_chat"); +// getChatIdReq.put("user1", myId.toString()); +// getChatIdReq.put("user2", otherUserId.toString()); +// +// JSONObject chatIdRes = sendWithResponse(getChatIdReq); +// if (chatIdRes == null || !chatIdRes.getString("status").equals("success")) { +// System.out.println("āŒ Failed to fetch private chat ID."); +// return; +// } +// +// Session.currentPrivateChatUserId = otherUserId; +// chatId = UUID.fromString(chatIdRes.getJSONObject("data").getString("chat_id")); +// chat.setId(String.valueOf(chatId)); +// +// } + + + + JSONObject res = sendWithResponse(req); if (res == null || !res.getString("status").equals("success")) { @@ -904,6 +1080,15 @@ public class ActionHandler { } System.out.println("─────────────────────────────────────────────"); + if (chat.getType().equals("private") && chat.getOtherUserId() == null) { + for (ContactEntry contact : Session.contactEntries) { + if (contact.getContactId().equals(chat.getId())) { + chat.setOtherUserId(contact.getContactId()); + break; + } + } + } + boolean stayInChat = true; @@ -957,7 +1142,7 @@ public class ActionHandler { JSONObject req = new JSONObject(); req.put("action", "view_profile"); - req.put("target_id", chat.getId()); // internal UUID + req.put("target_id", chat.getOtherUserId()); // internal UUID JSONObject res = sendWithResponse(req); @@ -1000,7 +1185,7 @@ public class ActionHandler { case "5" -> { JSONObject reqProfile = new JSONObject(); reqProfile.put("action", "view_profile"); - reqProfile.put("target_id", chat.getId()); + reqProfile.put("target_id", chat.getOtherUserId()); JSONObject resProfile = sendWithResponse(reqProfile); @@ -2445,6 +2630,9 @@ public class ActionHandler { System.err.println("āŒ Invalid request: missing action."); return null; } + System.out.println("šŸ“¤ Sending request to server: " + request.toString(2)); + System.out.println("šŸ“¤ [sendWithResponse] Action: " + request.optString("action", "unknown") + ", Full: " + request.toString(2)); + String requestId = UUID.randomUUID().toString(); request.put("request_id", requestId); @@ -2888,6 +3076,72 @@ public class ActionHandler { } +// public void sendMessage(UUID receiverId, String receiverType) { +// Scanner scanner = new Scanner(System.in); +// +// System.out.print("Enter your message: "); +// String content = scanner.nextLine(); +// +// System.out.print("Enter message type (TEXT / IMAGE / VIDEO / FILE): "); +// String messageType = scanner.nextLine(); +// Set allowedTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE"); +// while (!allowedTypes.contains(messageType.toUpperCase())) { +// System.out.println("āŒ Invalid message type. Try again (TEXT / IMAGE / VIDEO / FILE): "); +// messageType = scanner.nextLine(); +// } +// messageType = messageType.toUpperCase(); +// +// JSONArray attachmentsArray = new JSONArray(); +// +// System.out.print("Do you want to attach files? (yes/no): "); +// if (scanner.nextLine().equalsIgnoreCase("yes")) { +// while (true) { +// System.out.print("File URL: "); +// String fileUrl = scanner.nextLine(); +// +// System.out.print("File Type (IMAGE / VIDEO / FILE): "); +// String fileType = scanner.nextLine(); +// +// JSONObject fileJson = new JSONObject(); +// fileJson.put("file_url", fileUrl); +// fileJson.put("file_type", fileType); +// attachmentsArray.put(fileJson); +// +// System.out.print("Add another file? (yes/no): "); +// if (!scanner.nextLine().equalsIgnoreCase("yes")) { +// break; +// } +// } +// } +// +// +// +// JSONObject messageJson = new JSONObject(); +// messageJson.put("action", "send_message"); +// messageJson.put("receiver_type", receiverType); +// messageJson.put("content", content); +// messageJson.put("message_type", messageType); +// if (receiverType.equals("private")) { +// messageJson.put("receiver_user_id", receiverId.toString()); +// } else { +// messageJson.put("receiver_id", receiverId.toString()); +// } +// +// if (!attachmentsArray.isEmpty()) { +// messageJson.put("attachments", attachmentsArray); +// } +// +// JSONObject response = sendWithResponse(messageJson); +// if (response != null && response.getString("status").equals("success")) { +// System.out.println("āœ… Message sent successfully! ID: " + response.getJSONObject("data").getString("message_id")); +// } else { +// System.out.println("āŒ Failed to send message: " + (response != null ? response.getString("message") : "no response")); +// } +// +// } + + + public void sendMessage(UUID receiverId, String receiverType) { Scanner scanner = new Scanner(System.in); @@ -2895,40 +3149,53 @@ public class ActionHandler { String content = scanner.nextLine(); System.out.print("Enter message type (TEXT / IMAGE / VIDEO / FILE): "); - String messageType = scanner.nextLine(); + String messageType = scanner.nextLine().toUpperCase(); Set allowedTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE"); - while (!allowedTypes.contains(messageType.toUpperCase())) { - System.out.println("āŒ Invalid message type. Try again (TEXT / IMAGE / VIDEO / FILE): "); - messageType = scanner.nextLine(); + while (!allowedTypes.contains(messageType)) { + System.out.print("āŒ Invalid type. Try again (TEXT / IMAGE / VIDEO / FILE): "); + messageType = scanner.nextLine().toUpperCase(); } JSONArray attachmentsArray = new JSONArray(); - System.out.print("Do you want to attach files? (yes/no): "); if (scanner.nextLine().equalsIgnoreCase("yes")) { while (true) { System.out.print("File URL: "); String fileUrl = scanner.nextLine(); - System.out.print("File Type (IMAGE / VIDEO / FILE): "); String fileType = scanner.nextLine(); JSONObject fileJson = new JSONObject(); fileJson.put("file_url", fileUrl); - fileJson.put("file_type", fileType); + fileJson.put("file_type", fileType.toUpperCase()); attachmentsArray.put(fileJson); System.out.print("Add another file? (yes/no): "); - if (!scanner.nextLine().equalsIgnoreCase("yes")) { - break; - } + if (!scanner.nextLine().equalsIgnoreCase("yes")) break; } } + // ------------------------ GET receiver_id if private ------------------------ + UUID actualReceiverId = receiverId; + if (receiverType.equals("private")) { + JSONObject req = new JSONObject(); + req.put("action", "get_or_create_private_chat"); + req.put("user1", Session.currentPrivateChatUserId.toString()); + req.put("user2", receiverId.toString()); + + JSONObject chatResponse = sendWithResponse(req); + if (chatResponse == null || !chatResponse.getString("status").equals("success")) { + System.out.println("āŒ Failed to fetch private chat ID."); + return; + } + actualReceiverId = UUID.fromString(chatResponse.getJSONObject("data").getString("chat_id")); + } + + // ------------------------ SEND MESSAGE ------------------------ JSONObject messageJson = new JSONObject(); messageJson.put("action", "send_message"); - messageJson.put("receiver_id", receiverId.toString()); messageJson.put("receiver_type", receiverType); + messageJson.put("receiver_id", actualReceiverId.toString()); messageJson.put("content", content); messageJson.put("message_type", messageType); @@ -2938,11 +3205,10 @@ public class ActionHandler { JSONObject response = sendWithResponse(messageJson); if (response != null && response.getString("status").equals("success")) { - System.out.println("āœ… Message sent successfully! ID: " + response.getJSONObject("data").getString("message_id")); + System.out.println("āœ… Message sent! ID: " + response.getJSONObject("data").getString("message_id")); } else { - System.out.println("āŒ Failed to send message: " + (response != null ? response.getString("message") : "no response")); + System.out.println("āŒ Failed to send message: " + (response != null ? response.getString("message") : "No response")); } - } diff --git a/src/main/java/org/to/telegramfinalproject/Client/Session.java b/src/main/java/org/to/telegramfinalproject/Client/Session.java index 11b11d9..6e5f658 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/Session.java +++ b/src/main/java/org/to/telegramfinalproject/Client/Session.java @@ -3,6 +3,7 @@ package org.to.telegramfinalproject.Client; import org.json.JSONArray; import org.json.JSONObject; import org.to.telegramfinalproject.Models.ChatEntry; +import org.to.telegramfinalproject.Models.ContactEntry; import java.time.LocalDateTime; import java.util.ArrayList; @@ -16,7 +17,7 @@ public class Session { public static List chatList = new ArrayList<>(); public static List archivedChats = new ArrayList<>(); public static List activeChats = new ArrayList<>(); - + public static UUID currentPrivateChatUserId = null; public static volatile boolean forceRefreshChatList = false; public static volatile boolean backToChatList = false; public static boolean inChatListMenu = false; @@ -25,7 +26,7 @@ public class Session { public static volatile boolean refreshCurrentChatMenu = false; public static String currentChatId = null; public static ChatEntry currentChatEntry = null; - + public static List contactEntries = new ArrayList<>(); diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java index 54d307a..1a97247 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java @@ -401,4 +401,42 @@ public class MessageDatabase { } + public List getMessagesForPrivateChat(UUID user1, UUID user2) { + UUID chatId = PrivateChatDatabase.findChatIdByUsers(user1, user2); + if (chatId == null) return new ArrayList<>(); + return findByReceiver("private", chatId); + } + + public static List findByReceiver(String receiverType, UUID receiverId) { + List messages = new ArrayList<>(); + String sql = "SELECT * FROM messages WHERE receiver_type = ? AND receiver_id = ? ORDER BY send_at"; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + + ps.setString(1, receiverType); + ps.setObject(2, receiverId); + + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + UUID messageId = (UUID) rs.getObject("message_id"); + UUID senderId = (UUID) rs.getObject("sender_id"); + UUID recId = (UUID) rs.getObject("receiver_id"); + String type = rs.getString("receiver_type"); + String content = rs.getString("content"); + String messageType = rs.getString("message_type"); + LocalDateTime sendAt = rs.getTimestamp("send_at").toLocalDateTime(); + + Message message = new Message(messageId, senderId, recId, type, content, messageType, sendAt); + messages.add(message); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return messages; + } + + } diff --git a/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java index e13a2e5..d96258f 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java @@ -1,9 +1,12 @@ package org.to.telegramfinalproject.Database; +import org.to.telegramfinalproject.Models.PrivateChat; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -13,15 +16,15 @@ public class PrivateChatDatabase { public static List getMembers(UUID privateChatId) { - String sql = "SELECT user1, user2 FROM private_chats WHERE chat_id = ?"; + String sql = "SELECT user1_id, user2_id FROM private_chat WHERE chat_id = ?"; try (Connection conn = ConnectionDb.connect(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setObject(1, privateChatId); ResultSet rs = ps.executeQuery(); if (rs.next()) { - UUID user1 = (UUID) rs.getObject("user1"); - UUID user2 = (UUID) rs.getObject("user2"); - return Arrays.asList(user1, user2); + UUID user1 = (UUID) rs.getObject("user1_id"); + UUID user2 = (UUID) rs.getObject("user2_id"); + return new ArrayList<>(Arrays.asList(user1, user2)); } } catch (SQLException e) { e.printStackTrace(); @@ -29,4 +32,105 @@ public class PrivateChatDatabase { return new ArrayList<>(); } + + public static UUID findChatIdByUsers(UUID user1, UUID user2) { + UUID u1 = user1.compareTo(user2) < 0 ? user1 : user2; + UUID u2 = user1.compareTo(user2) < 0 ? user2 : user1; + + String sql = "SELECT chat_id FROM private_chat WHERE user1_id = ? AND user2_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + + ps.setObject(1, u1); + ps.setObject(2, u2); + + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return (UUID) rs.getObject("chat_id"); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + + public static UUID getOrCreateChat(UUID user1, UUID user2) { + UUID u1 = user1.compareTo(user2) < 0 ? user1 : user2; + UUID u2 = user1.compareTo(user2) < 0 ? user2 : user1; + + String select = "SELECT chat_id FROM private_chat WHERE user1_id = ? AND user2_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(select)) { + ps.setObject(1, u1); + ps.setObject(2, u2); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + return UUID.fromString(rs.getString("chat_id")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + UUID newChatId = UUID.randomUUID(); + String insert = "INSERT INTO private_chat(chat_id, user1_id, user2_id) VALUES (?, ?, ?)"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(insert)) { + ps.setObject(1, newChatId); + ps.setObject(2, u1); + ps.setObject(3, u2); + ps.executeUpdate(); + return newChatId; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + + public static List findChatsOfUser(UUID userId) { + List chats = new ArrayList<>(); + String sql = "SELECT * FROM private_chat WHERE user1_id = ? OR user2_id = ?"; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + + ps.setObject(1, userId); + ps.setObject(2, userId); + + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + UUID chatId = (UUID) rs.getObject("chat_id"); + UUID user1 = (UUID) rs.getObject("user1_id"); + UUID user2 = (UUID) rs.getObject("user2_id"); + + chats.add(new PrivateChat(chatId, user1, user2)); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return chats; + } + + + public static UUID getOtherUserInChat(UUID chatId, UUID currentUserId) { + String sql = "SELECT user1_id, user2_id FROM private_chat WHERE chat_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, chatId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + UUID user1 = (UUID) rs.getObject("user1_id"); + UUID user2 = (UUID) rs.getObject("user2_id"); + return currentUserId.equals(user1) ? user2 : user1; + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + } diff --git a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java index aa98d0b..8176576 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java +++ b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java @@ -13,6 +13,7 @@ public class ChatEntry { private String type; private LocalDateTime lastMessageTime; private boolean archived = false; + private UUID otherUser; private boolean isOwner = false; @@ -133,4 +134,10 @@ public class ChatEntry { } } + public void setOtherUserId(UUID otherId) {this.otherUser = otherId; + } + + public UUID getOtherUserId(){ + return otherUser; + } } diff --git a/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java b/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java new file mode 100644 index 0000000..c0e4341 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java @@ -0,0 +1,44 @@ +package org.to.telegramfinalproject.Models; + +import java.util.UUID; + +public class ContactEntry { + private UUID contactId; // internal UUID + private String userId; // public ID + private String profileName; + private String imageUrl; + private boolean isBlocked; + + public ContactEntry(UUID contactId, String userId, String profileName, String imageUrl, boolean isBlocked) { + this.contactId = contactId; + this.userId = userId; + this.profileName = profileName; + this.imageUrl = imageUrl; + this.isBlocked = isBlocked; + } + + public UUID getContactId() { + return contactId; + } + + public String getUserId() { + return userId; + } + + public String getProfileName() { + return profileName; + } + + public String getImageUrl() { + return imageUrl; + } + + public boolean isBlocked() { + return isBlocked; + } + + @Override + public String toString() { + return profileName + " (" + userId + ")" + (isBlocked ? " [Blocked]" : ""); + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java index 372600b..20caa6d 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java +++ b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java @@ -9,7 +9,7 @@ public class PrivateChat { private UUID user2_id; private LocalDateTime created_at; - public PrivateChat(UUID chat_id, UUID user1_id, UUID user2_id, LocalDateTime created_at){ + public PrivateChat(UUID chat_id, UUID user1_id, UUID user2_id){ this.chat_id = chat_id; this.user1_id =user1_id; this.user2_id =user2_id; diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index 9596845..3b82293 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -103,38 +103,84 @@ public class ClientHandler implements Runnable { List activeChatList = new ArrayList<>(); + JSONArray contactList = new JSONArray(); for (Contact contact : contacts) { User target = userDatabase.findByInternalUUID(contact.getContact_id()); if (target == null) continue; - LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(user.getInternal_uuid(), target.getInternal_uuid(), "private"); -// chatList.add(new ChatEntry( -// target.getInternal_uuid(), // internal UUID -// target.getUser_id(), // public display ID + JSONObject c = new JSONObject(); + c.put("user_id", contact.getUser_id().toString()); + c.put("contact_id", contact.getContact_id().toString()); + c.put("is_blocked", contact.getIs_blocked()); + + c.put("profile_name", target.getProfile_name()); + c.put("image_url", target.getImage_url()); + + contactList.put(c); + } + + + +// for (Contact contact : contacts) { +// User target = userDatabase.findByInternalUUID(contact.getContact_id()); +// if (target == null) continue; +// LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(user.getInternal_uuid(), target.getInternal_uuid(), "private"); +// +//// chatList.add(new ChatEntry( +//// target.getInternal_uuid(), // internal UUID +//// target.getUser_id(), // public display ID +//// target.getProfile_name(), +//// target.getImage_url(), +//// "private", +//// last, +//// false, +//// false +//// )); +// +// UUID targetId = target.getInternal_uuid(); +// +// ChatEntry entry = new ChatEntry( +// targetId, +// target.getUser_id(), // target.getProfile_name(), // target.getImage_url(), // "private", // last, // false, // false -// )); +// ); +// +// +// +// } - UUID targetId = target.getInternal_uuid(); + + + List privateChats = PrivateChatDatabase.findChatsOfUser(currentUser.getInternal_uuid()); + for (PrivateChat chat : privateChats) { + UUID otherId = chat.getUser1_id().equals(currentUser.getInternal_uuid()) ? + chat.getUser2_id() : chat.getUser1_id(); + + User otherUser = userDatabase.findByInternalUUID(otherId); + if (otherUser == null) continue; + + LocalDateTime lastMessageTime = MessageDatabase.getLastMessageTimeBetween( + currentUser.getInternal_uuid(), otherId, "private" + ); ChatEntry entry = new ChatEntry( - targetId, - target.getUser_id(), - target.getProfile_name(), - target.getImage_url(), + chat.getChat_id(), // šŸ”¹ real private chat_id + otherUser.getUser_id(), // Ł†Ł…Ų§ŪŒŲ“ Ų¹Ł…ŁˆŁ…ŪŒ طرف مقابل + otherUser.getProfile_name(), + otherUser.getImage_url(), "private", - last, + lastMessageTime, false, false ); + entry.setOtherUserId(otherId); - - - if (archivedChatIds.contains(targetId)) { + if (archivedChatIds.contains(chat.getChat_id())) { archivedChatList.add(entry); chatList.add(entry); } else { @@ -144,6 +190,7 @@ public class ClientHandler implements Runnable { } + for (Group group : groups) { LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group"); boolean isOwner = GroupDatabase.isOwner(group.getInternal_uuid(), user.getInternal_uuid()); @@ -240,7 +287,8 @@ public class ClientHandler implements Runnable { JSONObject userData = JsonUtil.userToJson(user); userData.put("chat_list", JsonUtil.chatListToJson(chatList)); userData.put("archived_chat_list", JsonUtil.chatListToJson(archivedChatList)); - userData.put("active_chat_lis", JsonUtil.chatListToJson(activeChatList)); + userData.put("active_chat_list", JsonUtil.chatListToJson(activeChatList)); + userData.put("contact_list", contactList); response = new ResponseModel("success", "Welcome " + user.getProfile_name(), userData); } break; @@ -605,33 +653,86 @@ public class ClientHandler implements Runnable { List contacts = ContactDatabase.getContacts(currentUser.getInternal_uuid()); List groups = GroupDatabase.getGroupsByUser(currentUser.getInternal_uuid()); List channels = ChannelDatabase.getChannelsByUser(currentUser.getInternal_uuid()); - + List archivedChatIds = ArchivedChatDatabase.getArchivedChats(currentUser.getInternal_uuid()); List chatList = new ArrayList<>(); + List archivedChatList = new ArrayList<>(); + List activeChatList = new ArrayList<>(); - for (Contact contact : contacts) { - User target = userDatabase.findByInternalUUID(contact.getContact_id()); - if (target == null) continue; - LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(currentUser.getInternal_uuid(), target.getInternal_uuid(), "private"); - chatList.add(new ChatEntry( - target.getInternal_uuid(), - target.getUser_id(), - target.getProfile_name(), - target.getImage_url(), + + + +// for (Contact contact : contacts) { +// User target = userDatabase.findByInternalUUID(contact.getContact_id()); +// if (target == null) continue; +// +// LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(currentUser.getInternal_uuid(), target.getInternal_uuid(), "private"); +// +// chatList.add(new ChatEntry( +// target.getInternal_uuid(), +// target.getUser_id(), +// target.getProfile_name(), +// target.getImage_url(), +// "private", +// last, +// false, +// false +// )); +// } +// + + List privateChats = PrivateChatDatabase.findChatsOfUser(currentUser.getInternal_uuid()); + for (PrivateChat chat : privateChats) { + UUID otherId = chat.getUser1_id().equals(currentUser.getInternal_uuid()) ? + chat.getUser2_id() : chat.getUser1_id(); + + User otherUser = userDatabase.findByInternalUUID(otherId); + if (otherUser == null) continue; + + LocalDateTime lastMessageTime = MessageDatabase.getLastMessageTimeBetween( + currentUser.getInternal_uuid(), otherId, "private" + ); + + ChatEntry entry = new ChatEntry( + chat.getChat_id(), // šŸ”¹ real private chat_id + otherUser.getUser_id(), // Ł†Ł…Ų§ŪŒŲ“ Ų¹Ł…ŁˆŁ…ŪŒ طرف مقابل + otherUser.getProfile_name(), + otherUser.getImage_url(), "private", - last, + lastMessageTime, false, false - )); + ); + entry.setOtherUserId(otherId); + + if (archivedChatIds.contains(chat.getChat_id())) { + archivedChatList.add(entry); + chatList.add(entry); + } else { + activeChatList.add(entry); + chatList.add(entry); + } } + for (Group group : groups) { LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group"); boolean isOwner = GroupDatabase.isOwner(group.getInternal_uuid(), currentUser.getInternal_uuid()); boolean isAdmin = GroupDatabase.isAdmin(group.getInternal_uuid(), currentUser.getInternal_uuid()); - chatList.add(new ChatEntry( +// chatList.add(new ChatEntry( +// group.getInternal_uuid(), +// group.getGroup_id(), +// group.getGroup_name(), +// group.getImage_url(), +// "group", +// last, +// isOwner, +// isAdmin +// )); + + ChatEntry entry = new ChatEntry( group.getInternal_uuid(), group.getGroup_id(), group.getGroup_name(), @@ -640,7 +741,17 @@ public class ClientHandler implements Runnable { last, isOwner, isAdmin - )); + ); + + if (archivedChatIds.contains(group.getInternal_uuid())) { + archivedChatList.add(entry); + chatList.add(entry); + } else { + activeChatList.add(entry); + chatList.add(entry); + } + + } for (Channel channel : channels) { @@ -648,7 +759,18 @@ public class ClientHandler implements Runnable { boolean isOwner = ChannelDatabase.isOwner(channel.getInternal_uuid(), currentUser.getInternal_uuid()); boolean isAdmin = ChannelDatabase.isAdmin(channel.getInternal_uuid(), currentUser.getInternal_uuid()); - chatList.add(new ChatEntry( +// chatList.add(new ChatEntry( +// channel.getInternal_uuid(), +// channel.getChannel_id(), +// channel.getChannel_name(), +// channel.getImage_url(), +// "channel", +// last, +// isOwner, +// isAdmin +// )); + + ChatEntry entry = new ChatEntry( channel.getInternal_uuid(), channel.getChannel_id(), channel.getChannel_name(), @@ -657,17 +779,39 @@ public class ClientHandler implements Runnable { last, isOwner, isAdmin - )); + ); + + if (archivedChatIds.contains(channel.getInternal_uuid())) { + archivedChatList.add(entry); + chatList.add(entry); + } else { + activeChatList.add(entry); + chatList.add(entry); + } } + activeChatList.sort((a, b) -> { + if (a.getLastMessageTime() == null) return 1; + if (b.getLastMessageTime() == null) return -1; + return b.getLastMessageTime().compareTo(a.getLastMessageTime()); + }); + + archivedChatList.sort((a, b) -> { + if (a.getLastMessageTime() == null) return 1; + if (b.getLastMessageTime() == null) return -1; + return b.getLastMessageTime().compareTo(a.getLastMessageTime()); + }); + + chatList.sort((a, b) -> { if (a.getLastMessageTime() == null) return 1; if (b.getLastMessageTime() == null) return -1; return b.getLastMessageTime().compareTo(a.getLastMessageTime()); }); - JSONObject data = new JSONObject(); data.put("chat_list", JsonUtil.chatListToJson(chatList)); + data.put("archived_chat_list", JsonUtil.chatListToJson(archivedChatList)); + data.put("active_chat_list", JsonUtil.chatListToJson(activeChatList)); response = new ResponseModel("success", "Chat list updated.", data); break; @@ -1116,13 +1260,16 @@ public class ClientHandler implements Runnable { switch (receiverType) { case "private" -> { - User otherUser = new userDatabase().findByInternalUUID(UUID.fromString(receiverId)); - if (otherUser == null) { - response = new ResponseModel("error", "User not found."); + List members = PrivateChatDatabase.getMembers(UUID.fromString(receiverId)); + if (members.size() != 2) { + response = new ResponseModel("error", "Invalid private chat."); break; } - messages = MessageDatabase.privateChatHistory(currentUser.getInternal_uuid(), otherUser.getInternal_uuid()); + + UUID otherUserId = members.get(0).equals(currentUser.getInternal_uuid()) ? members.get(1) : members.get(0); + messages = MessageDatabase.privateChatHistory(currentUser.getInternal_uuid(), otherUserId); } + case "group" -> { Group group = GroupDatabase.findByInternalUUID(UUID.fromString(receiverId)); if (group == null) { @@ -1836,6 +1983,45 @@ public class ClientHandler implements Runnable { break; + case "get_or_create_private_chat": { + if (currentUser == null) { + response = new ResponseModel("error", "Unauthorized. Please login first."); + break; + } + + try { + UUID user1 = UUID.fromString(requestJson.getString("user1")); + UUID user2 = UUID.fromString(requestJson.getString("user2")); + + UUID chatId = PrivateChatDatabase.getOrCreateChat(user1, user2); + + JSONObject data = new JSONObject(); + data.put("chat_id", chatId.toString()); + response = new ResponseModel("success", "Private chat ID retrieved.", data); + } catch (Exception e) { + e.printStackTrace(); + response = new ResponseModel("error", "Invalid data or internal error."); + } + + break; + } + + + + case "get_private_chat_target": { + UUID chatId = UUID.fromString(requestJson.getString("chat_id")); + userId = currentUser.getInternal_uuid(); + + UUID targetId = PrivateChatDatabase.getOtherUserInChat(chatId, userId); + if (targetId == null) { + response = new ResponseModel("error", "Could not find other user."); + } else { + JSONObject data = new JSONObject(); + data.put("target_id", targetId.toString()); + response = new ResponseModel("success", "Target fetched.", data); + } + break; + } @@ -1904,8 +2090,15 @@ public class ClientHandler implements Runnable { UUID messageId = UUID.randomUUID(); UUID senderId = currentUser.getInternal_uuid(); - UUID receiverId = UUID.fromString(json.getString("receiver_id")); String receiverType = json.getString("receiver_type"); + UUID receiverId; + + if (receiverType.equals("private")) { + UUID receiverUserId = UUID.fromString(json.getString("receiver_user_id")); + receiverId = PrivateChatDatabase.getOrCreateChat(senderId, receiverUserId); + } else { + receiverId = UUID.fromString(json.getString("receiver_id")); // group یا channel + } String content = json.optString("content", ""); String messageType = json.optString("message_type", "TEXT");