From 42d98c53d8ae185c309e17ae996d50e3069c769d Mon Sep 17 00:00:00 2001 From: Partow Roshani Date: Mon, 11 Aug 2025 20:52:39 +0330 Subject: [PATCH] Fixed save message --- .../Client/ActionHandler.java | 549 ++++++++++++------ .../Client/SidebarHandler.java | 327 ++++++++--- .../Database/PrivateChatDatabase.java | 78 ++- .../Models/PrivateChat.java | 8 + .../Server/ClientHandler.java | 104 +++- .../Server/SidebarService.java | 87 ++- 6 files changed, 818 insertions(+), 335 deletions(-) diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index 16f0235..70db64c 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -339,6 +339,7 @@ public class ActionHandler { if (chat.has("other_user_id") && !chat.isNull("other_user_id")) { entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id"))); } + entry.setSavedMessages(chat.optBoolean("is_saved_messages", false)); return entry; } @@ -479,80 +480,96 @@ public class ActionHandler { JSONArray Archived = Session.currentUser.getJSONArray("archived_chat_list"); JSONArray Active = Session.currentUser.getJSONArray("active_chat_list"); JSONArray contactList = Session.currentUser.getJSONArray("contact_list"); +// List chatList = new ArrayList<>(); +// +// +// for (Object obj : chatListJson) { +// JSONObject chat = (JSONObject) obj; +// +// ChatEntry entry = new ChatEntry( +// UUID.fromString(chat.getString("internal_id")), +// chat.getString("id"), +// chat.getString("name"), +// chat.optString("image_url", ""), +// chat.getString("type"), +// 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"))); +// } +// +// if (chat.has("is_saved_messages")) { +// entry.setSavedMessages(chat.getBoolean("is_saved_messages")); +// } +// +// chatList.add(entry); +// +// } +// +// List archivedChats = new ArrayList<>(); +// for (Object obj : Archived){ +// JSONObject chat = (JSONObject) obj; +// +// ChatEntry entry = new ChatEntry( +// UUID.fromString(chat.getString("internal_id")), +// chat.getString("id"), +// chat.getString("name"), +// chat.optString("image_url", ""), +// chat.getString("type"), +// 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"))); +// } +// archivedChats.add(entry); +// +// } +// +// List activeChats = new ArrayList<>(); +// for (Object obj : Active){ +// JSONObject chat = (JSONObject) obj; +// +// ChatEntry entry = new ChatEntry( +// UUID.fromString(chat.getString("internal_id")), +// chat.getString("id"), +// chat.getString("name"), +// chat.optString("image_url", ""), +// chat.getString("type"), +// 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"))); +// } +// +// activeChats.add(entry); +// +// } + List chatList = new ArrayList<>(); - - for (Object obj : chatListJson) { - JSONObject chat = (JSONObject) obj; - - ChatEntry entry = new ChatEntry( - UUID.fromString(chat.getString("internal_id")), - chat.getString("id"), - chat.getString("name"), - chat.optString("image_url", ""), - chat.getString("type"), - 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"))); - } - - if (chat.has("is_saved_messages")) { - entry.setSavedMessages(chat.getBoolean("is_saved_messages")); - } - - chatList.add(entry); - + chatList.add(parseChatEntry((JSONObject) obj)); } List archivedChats = new ArrayList<>(); - for (Object obj : Archived){ - JSONObject chat = (JSONObject) obj; - - ChatEntry entry = new ChatEntry( - UUID.fromString(chat.getString("internal_id")), - chat.getString("id"), - chat.getString("name"), - chat.optString("image_url", ""), - chat.getString("type"), - 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"))); - } - archivedChats.add(entry); - + for (Object obj : Archived) { + archivedChats.add(parseChatEntry((JSONObject) obj)); } List activeChats = new ArrayList<>(); - for (Object obj : Active){ - JSONObject chat = (JSONObject) obj; - - ChatEntry entry = new ChatEntry( - UUID.fromString(chat.getString("internal_id")), - chat.getString("id"), - chat.getString("name"), - chat.optString("image_url", ""), - chat.getString("type"), - 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"))); - } - - activeChats.add(entry); - + for (Object obj : Active) { + activeChats.add(parseChatEntry((JSONObject) obj)); } + Session.contactEntries.clear(); for (Object obj : contactList) { JSONObject c = (JSONObject) obj; @@ -830,6 +847,8 @@ public class ActionHandler { } + + public void userMenu(UUID internal_uuid) throws IOException { while (true) { @@ -1131,86 +1150,158 @@ public class ActionHandler { // } +// public void showChatListAndSelect() { +// if (Session.activeChats == null || Session.activeChats.isEmpty()) { +// System.out.println("No active chats."); +// return; +// } +// +// System.out.println("\nYour Chats:"); +// System.out.println("0. 📦 Archived Chats"); +// +// // Track index dynamically +// int index = 1; +// +// // Check if Saved Messages exists in the list +// int savedMessagesIndex = -1; +// for (int i = 0; i < Session.activeChats.size(); i++) { +// ChatEntry entry = Session.activeChats.get(i); +// +// if (entry.isSavedMessages()) { +// savedMessagesIndex = index; +// System.out.println(index + ". 📦 Saved Messages Chat"); +// index++; +// break; +// } +// } +// +// // Print the rest of the chats +// for (int i = 0; i < Session.activeChats.size(); i++) { +// ChatEntry entry = Session.activeChats.get(i); +// if (entry.isSavedMessages()) { +// continue; // Already printed above +// } +// +// String time = (entry.getLastMessageTime() == null) +// ? "No messages yet" +// : entry.getLastMessageTime().toString(); +// System.out.println(index + ". [" + entry.getType() + "] " + +// entry.getName() + " - Last: " + time); +// index++; +// } +// +// System.out.print("Select a chat by number: "); +// int choice = Integer.parseInt(scanner.nextLine()); +// +// if (choice == 0) { +// showArchivedChats(); +// return; +// } +// +// if (choice == savedMessagesIndex) { +// new SidebarHandler(scanner, this).getSavedMessagesData(Session.getUserUUID()); +// return; +// } +// +// // Adjust for Saved Messages if it was in the list +// int baseIndex = (savedMessagesIndex != -1 && choice > savedMessagesIndex) ? 1 : 0; +// int chatIndex = choice - 1 - baseIndex; +// +// if (chatIndex < 0 || chatIndex >= Session.activeChats.size()) { +// System.out.println("Invalid selection."); +// return; +// } +// +// // Find the actual index of the chat, skipping the saved_messages entry +// int actualIndex = 0; +// for (int i = 0; i < Session.activeChats.size(); i++) { +// if (Session.activeChats.get(i).getType().equalsIgnoreCase("saved_messages")) { +// continue; // Skip saved_messages +// } +// if (actualIndex == chatIndex) { +// break; +// } +// actualIndex++; +// } +// +// ChatEntry selected = Session.activeChats.get(actualIndex); +// openChat(selected); +// } + + + public void showChatListAndSelect() { if (Session.activeChats == null || Session.activeChats.isEmpty()) { - System.out.println("No active chats."); + System.out.println("📭 You have no chats."); return; } System.out.println("\nYour Chats:"); System.out.println("0. 📦 Archived Chats"); - // Track index dynamically - int index = 1; - - // Check if Saved Messages exists in the list - int savedMessagesIndex = -1; + // پیدا کردن Saved در لیست + Integer savedIdxInActive = null; for (int i = 0; i < Session.activeChats.size(); i++) { - ChatEntry entry = Session.activeChats.get(i); - - if (entry.isSavedMessages()) { - savedMessagesIndex = index; - System.out.println(index + ". 📦 Saved Messages Chat"); - index++; + if (Session.activeChats.get(i).isSavedMessages()) { + savedIdxInActive = i; break; } } - // Print the rest of the chats - for (int i = 0; i < Session.activeChats.size(); i++) { - ChatEntry entry = Session.activeChats.get(i); - if (entry.isSavedMessages()) { - continue; // Already printed above - } + // مپ شمارهٔ نمایش → ایندکس واقعی در activeChats + Map displayToActive = new HashMap<>(); - String time = (entry.getLastMessageTime() == null) - ? "No messages yet" - : entry.getLastMessageTime().toString(); - System.out.println(index + ". [" + entry.getType() + "] " + - entry.getName() + " - Last: " + time); - index++; + int displayIndex = 1; + + // چاپ Saved (در صورت وجود) و ثبت در مپ + if (savedIdxInActive != null) { + ChatEntry saved = Session.activeChats.get(savedIdxInActive); + String last = (saved.getLastMessageTime() == null) ? "No messages yet" : saved.getLastMessageTime().toString(); + System.out.println(displayIndex + ". 💾 Saved Messages - Last: " + last); + + // نکتهٔ کلیدی: مپ کن تا مثل بقیه با openChat باز شود + displayToActive.put(displayIndex, savedIdxInActive); + displayIndex++; } + // چاپ بقیهٔ چت‌ها + ثبت مپ + for (int i = 0; i < Session.activeChats.size(); i++) { + if (savedIdxInActive != null && i == savedIdxInActive) continue; + + ChatEntry e = Session.activeChats.get(i); + String last = (e.getLastMessageTime() == null) ? "No messages yet" : e.getLastMessageTime().toString(); + System.out.println(displayIndex + ". [" + e.getType() + "] " + e.getName() + " - Last: " + last); + + displayToActive.put(displayIndex, i); + displayIndex++; + } + + // انتخاب System.out.print("Select a chat by number: "); - int choice = Integer.parseInt(scanner.nextLine()); + int choice; + try { + choice = Integer.parseInt(scanner.nextLine().trim()); + } catch (Exception e) { + System.out.println("❌ Invalid selection."); + return; + } if (choice == 0) { showArchivedChats(); return; } - if (choice == savedMessagesIndex) { - new SidebarHandler(scanner, this).getSavedMessagesData(Session.getUserUUID()); + Integer activeIdx = displayToActive.get(choice); + if (activeIdx == null) { + System.out.println("❌ Invalid selection."); return; } - // Adjust for Saved Messages if it was in the list - int baseIndex = (savedMessagesIndex != -1 && choice > savedMessagesIndex) ? 1 : 0; - int chatIndex = choice - 1 - baseIndex; - - if (chatIndex < 0 || chatIndex >= Session.activeChats.size()) { - System.out.println("Invalid selection."); - return; - } - - // Find the actual index of the chat, skipping the saved_messages entry - int actualIndex = 0; - for (int i = 0; i < Session.activeChats.size(); i++) { - if (Session.activeChats.get(i).getType().equalsIgnoreCase("saved_messages")) { - continue; // Skip saved_messages - } - if (actualIndex == chatIndex) { - break; - } - actualIndex++; - } - - ChatEntry selected = Session.activeChats.get(actualIndex); - openChat(selected); + ChatEntry selected = Session.activeChats.get(activeIdx); + openChat(selected); // برای Saved هم همین مسیر اجرا می‌شود } - private void showArchivedChats() { if (Session.archivedChats == null || Session.archivedChats.isEmpty()) { System.out.println("📭 No archived chats."); @@ -1371,6 +1462,116 @@ public class ActionHandler { +// +// private boolean showPrivateChatMenu(ChatEntry chat) { +// +// if (forceExitChat) { +// forceExitChat = false; +// System.out.println("🚪 Exiting chat due to real-time update."); +// return false; +// } +// +// +// 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 false; +// } +// +// String otherUserId = resTarget.getJSONObject("data").getString("target_id"); +// chat.setOtherUserId(UUID.fromString(otherUserId)); +// +// +// JSONObject req = new JSONObject(); +// req.put("action", "view_profile"); +// req.put("target_id", chat.getOtherUserId()); // internal UUID +// +// JSONObject res = sendWithResponse(req); +// +// if (res.getString("status").equals("success")) { +// JSONObject data = res.getJSONObject("data"); +// +// System.out.println("\n💬 Private Chat with: " + data.getString("profile_name")); +// +// if (data.getBoolean("is_online")) { +// System.out.println("✅ Status: Online"); +// } else { +// System.out.println("📅 Last seen: " + data.getString("last_seen")); +// } +// +// System.out.println("────────────────────────────────────────────"); +// } +// +// +// System.out.println("1. Send message"); +// System.out.println("2. Block/Unblock"); +// System.out.println("3. Delete chat (one-sided)"); +// System.out.println("4. Delete chat (both sides)"); +// System.out.println("5. View profile"); +// System.out.println("6. Archive/Unarchived"); +// System.out.println("7. View messages"); +// System.out.println("8. Back"); +// +// +// String input = scanner.nextLine(); +// switch (input) { +// case "1" -> sendMessage(chat.getId(), "private"); +// case "2" -> toggleBlock(chat.getOtherUserId()); +// case "3" -> { +// deleteChat(chat.getId(), false); +// return true; +// } +// case "4" -> { +// deleteChat(chat.getId(), true); +// return true; +// } +// case "5" -> { +// JSONObject reqProfile = new JSONObject(); +// reqProfile.put("action", "view_profile"); +// reqProfile.put("target_id", chat.getOtherUserId()); +// +// JSONObject resProfile = sendWithResponse(reqProfile); +// +// if (resProfile.getString("status").equals("success")) { +// JSONObject profile = resProfile.getJSONObject("data"); +// System.out.println("\n👤 Profile Info:"); +// System.out.println("🔷 Name: " + profile.optString("profile_name", "Unknown")); +// System.out.println("📄 Bio: " + profile.optString("bio", "No bio set")); +// System.out.println("🖼️ Image URL: " + profile.optString("image_url", "N/A")); +// +// if (profile.getBoolean("is_online")) { +// System.out.println("✅ Status: Online"); +// } else { +// System.out.println("📅 Last seen: " + profile.optString("last_seen", "Unknown")); +// } +// System.out.println("────────────────────────────────────────────"); +// } else { +// System.out.println("❌ Could not load profile."); +// } +// return true; +// } +// +// case "6" ->{ +// toggleArchive(chat.getId() , "private"); +// +// return true; +// } +// case "7" ->{ +// viewMessagesInChat(chat); +// } +// +// case "8" -> { +// return false; +// } +// default -> System.out.println("Invalid choice."); +// } +// return true; +// } + private boolean showPrivateChatMenu(ChatEntry chat) { @@ -1380,41 +1581,57 @@ public class ActionHandler { return false; } + UUID chatId = chat.getId(); - JSONObject reqTarget = new JSONObject(); - reqTarget.put("action", "get_private_chat_target"); - reqTarget.put("chat_id", chat.getId()); + if (chat.getOtherUserId() == null) { + JSONObject reqTarget = new JSONObject() + .put("action", "get_private_chat_target") + .put("chat_id", chatId.toString()); JSONObject resTarget = sendWithResponse(reqTarget); - if (resTarget == null || !resTarget.getString("status").equals("success")) { + if (resTarget == null || !"success".equals(resTarget.optString("status"))) { System.out.println("❌ Failed to fetch target user for private chat."); return false; } - - String otherUserId = resTarget.getJSONObject("data").getString("target_id"); - chat.setOtherUserId(UUID.fromString(otherUserId)); - - - JSONObject req = new JSONObject(); - req.put("action", "view_profile"); - req.put("target_id", chat.getOtherUserId()); // internal UUID - - JSONObject res = sendWithResponse(req); - - if (res.getString("status").equals("success")) { - JSONObject data = res.getJSONObject("data"); - - System.out.println("\n💬 Private Chat with: " + data.getString("profile_name")); - - if (data.getBoolean("is_online")) { - System.out.println("✅ Status: Online"); - } else { - System.out.println("📅 Last seen: " + data.getString("last_seen")); - } - - System.out.println("────────────────────────────────────────────"); + chat.setOtherUserId(UUID.fromString(resTarget.getJSONObject("data").getString("target_id"))); } + UUID me = UUID.fromString(Session.currentUser.getString("internal_uuid")); + boolean isSaved = chat.isSavedMessages() || me.equals(chat.getOtherUserId()); + + System.out.println(); + if (isSaved) { + System.out.println("💬 Private Chat with: Saved Messages"); + System.out.println("────────────────────────────────────────────"); + } else { + JSONObject req = new JSONObject() + .put("action", "view_profile") + .put("target_id", chat.getOtherUserId()); + + JSONObject res = sendWithResponse(req); + if (res != null && "success".equals(res.optString("status"))) { + JSONObject data = res.getJSONObject("data"); + System.out.println("\n💬 Private Chat with: " + data.optString("profile_name", chat.getName())); + if (data.optBoolean("is_online", false)) System.out.println("✅ Status: Online"); + else System.out.println("📅 Last seen: " + data.optString("last_seen", "Unknown")); + System.out.println("────────────────────────────────────────────"); + } + } + + if (isSaved) { + System.out.println("1. Send message"); + System.out.println("2. View messages"); + System.out.println("3. Back"); + + String input = scanner.nextLine().trim(); + switch (input) { + case "1" -> sendMessage(chatId, "private"); + case "2" -> { viewMessagesInChat(chat); } + case "3" -> { return false; } + default -> System.out.println("Invalid choice."); + } + return true; + } System.out.println("1. Send message"); System.out.println("2. Block/Unblock"); @@ -1425,57 +1642,35 @@ public class ActionHandler { System.out.println("7. View messages"); System.out.println("8. Back"); - - String input = scanner.nextLine(); + String input = scanner.nextLine().trim(); switch (input) { - case "1" -> sendMessage(chat.getId(), "private"); + case "1" -> sendMessage(chatId, "private"); case "2" -> toggleBlock(chat.getOtherUserId()); - case "3" -> { - deleteChat(chat.getId(), false); - return true; - } - case "4" -> { - deleteChat(chat.getId(), true); - return true; - } + case "3" -> { deleteChat(chatId, false); return true; } + case "4" -> { deleteChat(chatId, true); return true; } case "5" -> { - JSONObject reqProfile = new JSONObject(); - reqProfile.put("action", "view_profile"); - reqProfile.put("target_id", chat.getOtherUserId()); + JSONObject reqProfile = new JSONObject() + .put("action", "view_profile") + .put("target_id", chat.getOtherUserId()); JSONObject resProfile = sendWithResponse(reqProfile); - - if (resProfile.getString("status").equals("success")) { + if (resProfile != null && "success".equals(resProfile.optString("status"))) { JSONObject profile = resProfile.getJSONObject("data"); System.out.println("\n👤 Profile Info:"); System.out.println("🔷 Name: " + profile.optString("profile_name", "Unknown")); System.out.println("📄 Bio: " + profile.optString("bio", "No bio set")); System.out.println("🖼️ Image URL: " + profile.optString("image_url", "N/A")); - - if (profile.getBoolean("is_online")) { - System.out.println("✅ Status: Online"); - } else { - System.out.println("📅 Last seen: " + profile.optString("last_seen", "Unknown")); - } + if (profile.optBoolean("is_online", false)) System.out.println("✅ Status: Online"); + else System.out.println("📅 Last seen: " + profile.optString("last_seen", "Unknown")); System.out.println("────────────────────────────────────────────"); } else { System.out.println("❌ Could not load profile."); } return true; } - - case "6" ->{ - toggleArchive(chat.getId() , "private"); - - return true; - } - case "7" ->{ - viewMessagesInChat(chat); - } - - case "8" -> { - return false; - } + case "6" -> { toggleArchive(chatId, "private"); return true; } + case "7" -> { viewMessagesInChat(chat); break; } + case "8" -> { return false; } default -> System.out.println("Invalid choice."); } return true; diff --git a/src/main/java/org/to/telegramfinalproject/Client/SidebarHandler.java b/src/main/java/org/to/telegramfinalproject/Client/SidebarHandler.java index 652b8b7..302289b 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/SidebarHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/SidebarHandler.java @@ -4,6 +4,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.to.telegramfinalproject.Models.ChatEntry; +import org.to.telegramfinalproject.Models.FileAttachment; import org.to.telegramfinalproject.Models.Message; import java.time.LocalDateTime; @@ -13,9 +14,16 @@ import static org.to.telegramfinalproject.Client.ActionHandler.sendWithResponse; public class SidebarHandler { private final Scanner scanner; - private final ActionHandler actionHandler; + private ActionHandler actionHandler ; private final String userUUID; + public SidebarHandler(Scanner scanner, ActionHandler actionHandler, ActionHandler action) { + this.scanner = scanner; + this.actionHandler = action; + this.userUUID = Session.getUserUUID(); + + } + public SidebarHandler(Scanner scanner, ActionHandler actionHandler) { this.scanner = scanner; this.actionHandler = actionHandler; @@ -38,7 +46,7 @@ public class SidebarHandler { showContacts(); break; case SAVED_MESSAGES: - getSavedMessagesData(userUUID); + ensureSavedMessagesCreated(); break; case SETTINGS: openSettings(); @@ -347,110 +355,183 @@ public class SidebarHandler { } private void showSavedMessages(UUID chatId, List messages) { - Scanner scanner = new Scanner(System.in); +// Scanner scanner = new Scanner(System.in); +// +// System.out.println("==== Saved Messages ===="); +// if (messages == null || messages.isEmpty()) { +// System.out.println("No messages yet."); +// } else { +// for (Message msg : messages) { +// System.out.println("[" + msg.getSend_at() + "] " + msg.getContent()); +// } +// } +// +// System.out.println("\n(Type your message below, or type 0 to exit)"); +// +// while (true) { +// System.out.print("You: "); +// String content = scanner.nextLine().trim(); +// if (content.equals("0")) { +// System.out.println("Exiting Saved Messages."); +// break; +// } +// +// System.out.print("Enter message type (TEXT / IMAGE / VIDEO / FILE / AUDIO): "); +// String messageType = scanner.nextLine().trim().toUpperCase(); +// Set allowedTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE", "AUDIO"); +// while (!allowedTypes.contains(messageType)) { +// System.out.print("Invalid type. Try again (TEXT / IMAGE / VIDEO / FILE / AUDIO): "); +// messageType = scanner.nextLine().trim().toUpperCase(); +// } +// +// // Attachments (اختیاری) +// 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().trim(); +// +// if (fileUrl.isEmpty()) { +// System.out.println("URL can not be empty. Try again."); +// continue; +// } +// if (fileUrl.contains(" ")) { +// System.out.println("URL cannot contain spaces. Try again."); +// continue; +// } +// if (!fileUrl.matches("^(http|https)://.*$")) { +// System.out.println("Invalid URL format. Please enter a valid HTTP/HTTPS link."); +// continue; +// } +// +// System.out.print("File Type (IMAGE / VIDEO / FILE / AUDIO): "); +// String fileType = scanner.nextLine().trim().toUpperCase(); +// Set allowedFileTypes = Set.of("IMAGE", "VIDEO", "FILE", "AUDIO"); +// while (!allowedFileTypes.contains(fileType)) { +// System.out.print("Invalid type. Try again (IMAGE / VIDEO / FILE / AUDIO): "); +// fileType = scanner.nextLine().trim().toUpperCase(); +// } +// +// 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; +// } +// } +// +// // درخواست مطابق هندلر send_message +// JSONObject request = new JSONObject(); +// request.put("action", "send_message"); +// request.put("receiver_type", "private"); +// request.put("receiver_id", chatId.toString()); // chat_id +// request.put("content", content); +// request.put("message_type", messageType); +// if (attachmentsArray.length() > 0) { +// request.put("attachments", attachmentsArray); +// } +// +// JSONObject response = ActionHandler.sendWithResponse(request); +// if (!"success".equalsIgnoreCase(response.optString("status"))) { +// System.out.println("Failed to send message: " + response.optString("message", "Unknown error")); +// } else { +// System.out.println("Message sent."); +// +// Message justSent = new Message( +// UUID.fromString(response.getJSONObject("data").getString("message_id")), +// /* senderId */ userUUID, +// /* receiverId */ chatId, +// /* type */ "private", +// /* content */ content, +// /* msgType */ messageType, +// /* send_at */ java.time.LocalDateTime.now() +// ); +// messages.add(justSent); +// System.out.println("[" + justSent.getSend_at() + "] " + justSent.getContent()); +// } +// } + } - System.out.println("==== Saved Messages ===="); - // Show previous messages - if (messages.isEmpty()) { - System.out.println("No messages yet."); - } else { - for (Message msg : messages) { - System.out.println("[" + msg.getSend_at() + "] " + msg.getContent()); +// public void openSavedMessages() { +// JSONObject req = new JSONObject().put("action", "get_or_create_saved_messages"); +// JSONObject res = sendWithResponse(req); +// if (res == null || !"success".equals(res.optString("status"))) { +// System.out.println("❌ Could not open Saved Messages: " + res.optString("message","")); +// return; +// } +// +// ActionHandler.requestChatList(); +// String chatId = res.getJSONObject("data").getString("chat_id"); +// +// JSONObject mreq = new JSONObject() +// .put("action", "get_messages") +// .put("receiver_type", "private") +// .put("receiver_id", chatId) +// .put("offset", 0) +// .put("limit", 50); +// +// JSONObject mres = sendWithResponse(mreq); +// JSONArray msgs = (mres != null && mres.has("data")) +// ? mres.getJSONObject("data").optJSONArray("messages") +// : new JSONArray(); +// +// List messages = parseMessages(msgs); // تبدیل JSON → Message +// showSavedMessages(UUID.fromString(chatId), messages); +// } +// + + + + public void ensureSavedMessagesCreated() { + // 1) Client-side quick check + boolean alreadyExists = false; + if (Session.activeChats != null) { + for (ChatEntry e : Session.activeChats) { + if (e != null && e.isSavedMessages()) { alreadyExists = true; break; } } } + if (alreadyExists) { + System.out.println("ℹ️ Saved Messages already exists."); + return; + } - System.out.println("\n(Type your message below, or type 0 to exit)"); + // 2) Ask server to get or create the self-chat + JSONObject res = sendWithResponse(new JSONObject().put("action", "get_or_create_saved_messages")); + if (res == null || !"success".equals(res.optString("status"))) { + System.out.println("❌ Failed to create/open Saved Messages: " + (res != null ? res.optString("message","") : "")); + return; + } - while (true) { - System.out.print("You: "); - String content = scanner.nextLine().trim(); + JSONObject data = res.optJSONObject("data"); + if (data == null) { + System.out.println("❌ Invalid server response for Saved Messages."); + return; + } - if (content.equals("0")) { - System.out.println("Exiting Saved Messages."); - break; - } + boolean created = data.optBoolean("created", true); // if server sends it + String chatId = data.optString("chat_id", null); - System.out.print("Enter message type (TEXT / IMAGE / VIDEO / FILE / AUDIO): "); - String messageType = scanner.nextLine().toUpperCase(); - Set allowedTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE"); - while (!allowedTypes.contains(messageType)) { - System.out.print("Invalid type. Try again (TEXT / IMAGE / VIDEO / FILE / AUDIO): "); - messageType = scanner.nextLine().toUpperCase(); - } - - // Attaching Files - 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(); - - // URL validation - if (fileUrl.isEmpty()) { - System.out.print("URL can not be empty. Try again."); - continue; - } - - if (fileUrl.contains(" ")) { - System.out.println("URL cannot contain spaces. Try again."); - continue; - } - - if (!fileUrl.isEmpty() && !fileUrl.matches("^(http|https)://.*$")) { - System.out.println("Invalid URL format. Please enter a valid HTTP/HTTPS link."); - continue; - } - - System.out.print("File Type (IMAGE / VIDEO / FILE / AUDIO): "); - String fileType = scanner.nextLine().toUpperCase(); - - Set allowedFileTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE"); - while (!allowedFileTypes.contains(fileType)) { - System.out.print("Invalid type. Try again (IMAGE / VIDEO / FILE / AUDIO): "); - fileType = scanner.nextLine().toUpperCase(); - } - - 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; - } - } - - // Prepare the request JSON - JSONObject request = new JSONObject(); - request.put("action", "send_saved_messages"); - request.put("message_id", UUID.randomUUID().toString()); - request.put("sender_id", userUUID); - request.put("receiver_type", "private"); - request.put("receiver_id", userUUID); // saved messages = to yourself - request.put("content", content); - request.put("message_type", "TEXT"); - request.put("status", "READ"); - request.put("reply_to_id", JSONObject.NULL); - request.put("is_edited", false); - request.put("original_message_id", JSONObject.NULL); - request.put("forwarded_by", JSONObject.NULL); - request.put("forwarded_from", JSONObject.NULL); - request.put("is_deleted_globally", JSONObject.NULL); - request.put("edited_at", JSONObject.NULL); - - // Send the message and wait for response - JSONObject response = ActionHandler.sendWithResponse(request); - - if (!response.optString("status", "fail").equals("success")) { - System.out.println("Failed to send message: " + response.optString("message", "Unknown error")); - } else { - System.out.println("Message sent."); - } + if (created) { + System.out.println("✅ Saved Messages created." + (chatId != null ? " chat_id=" + chatId : "")); + try { + UUID cid = UUID.fromString(chatId); + ChatEntry saved = new ChatEntry(cid, "Saved Messages", "Saved Messages", "", "private", null, true, false); + saved.setSavedMessages(true); + if (Session.activeChats == null) Session.activeChats = new ArrayList<>(); + Session.activeChats.add(0, saved); + } catch (Exception ignore) {} + } else { + System.out.println("ℹ️ Saved Messages already exists." + (chatId != null ? " chat_id=" + chatId : "")); } } + + + private void openSettings() { System.out.println("⚙️ Opening settings..."); } @@ -462,4 +543,60 @@ public class SidebarHandler { private void showTelegramQA() { System.out.println("❓ Showing Q&A..."); } + + + private List parseMessages(JSONArray msgs) { + List list = new ArrayList<>(); + if (msgs == null) return list; + + for (int i = 0; i < msgs.length(); i++) { + try { + JSONObject obj = msgs.getJSONObject(i); + + UUID messageId = UUID.fromString(obj.getString("message_id")); + UUID senderId = UUID.fromString(obj.getString("sender_id")); + String receiverType= obj.getString("receiver_type"); // "private" | "group" | "channel" + UUID receiverId = UUID.fromString(obj.getString("receiver_id")); // برای private = chat_id + String content = obj.optString("content", ""); + String messageType = obj.optString("message_type", "TEXT"); + LocalDateTime sent = LocalDateTime.parse(obj.getString("send_at")); + + Message m = new Message( + messageId, senderId, receiverId, receiverType, content, messageType, sent + ); + + if (obj.has("status") && !obj.isNull("status")) { + try { m.setStatus(obj.getString("status")); } catch (Exception ignore) {} + } + if (obj.has("reply_to_id") && !obj.isNull("reply_to_id")) { + try { m.setReply_to_id(UUID.fromString(obj.getString("reply_to_id"))); } catch (Exception ignore) {} + } + if (obj.has("forwarded_by") && !obj.isNull("forwarded_by")) { + try { m.setForwarded_by(UUID.fromString(obj.getString("forwarded_by"))); } catch (Exception ignore) {} + } + if (obj.has("forwarded_from") && !obj.isNull("forwarded_from")) { + try { m.setForwarded_from(UUID.fromString(obj.getString("forwarded_from"))); } catch (Exception ignore) {} + } + +// // ضمیمه‌ها (اگر در مدل Message متد addAttachment داری) +// if (obj.has("attachments") && !obj.isNull("attachments")) { +// try { +// JSONArray atts = obj.getJSONArray("attachments"); +// for (int j = 0; j < atts.length(); j++) { +// JSONObject a = atts.getJSONObject(j); +// String fileUrl = a.getString("file_url"); +// String fileType = a.getString("file_type"); +// FileAttachment fa = new FileAttachment(fileUrl, fileType); +// try { m.addAttachment(fa); } catch (Exception ignore) {} +// } +// } catch (Exception ignore) {} +// } + + list.add(m); + } catch (Exception perItem) { + perItem.printStackTrace(); + } + } + return list; + } } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java index 6b2ab1f..a666c34 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java @@ -307,12 +307,86 @@ public class PrivateChatDatabase { return null; } +// public static UUID getOtherParticipant(UUID chatId, UUID me) { +// List members = getMembers(chatId); +// for (UUID u : members) { +// if (!u.equals(me)) return u; +// } +// return null; +// } + public static UUID getOtherParticipant(UUID chatId, UUID me) { - List members = getMembers(chatId); + List members = getMembers(chatId); // باید [user1_id, user2_id] بده + if (members == null || members.isEmpty()) return null; + + boolean isMember = false; + UUID other = null; + for (UUID u : members) { - if (!u.equals(me)) return u; + if (u == null) continue; + if (u.equals(me)) { + isMember = true; + } else { + other = u; + } } + + if (!isMember) return null; + return (other != null) ? other + : me; + } + + + + + + public static PrivateChat findSelfChat(UUID userId) { + String sql = """ + SELECT chat_id, user1_id, user2_id, user1_deleted, user2_deleted + FROM private_chat + WHERE user1_id = ? AND user2_id = ? + LIMIT 1 + """; + try (Connection c = ConnectionDb.connect(); + PreparedStatement ps = c.prepareStatement(sql)) { + ps.setObject(1, userId); + ps.setObject(2, userId); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) return map(rs); + } + } catch (SQLException e) { e.printStackTrace(); } return null; } + public static UUID createSelfChat(UUID userId) { + String sql = """ + INSERT INTO private_chat (chat_id, user1_id, user2_id, user1_deleted, user2_deleted, created_at) + VALUES (gen_random_uuid(), ?, ?, FALSE, FALSE, NOW()) + RETURNING chat_id + """; + try (Connection c = ConnectionDb.connect(); + PreparedStatement ps = c.prepareStatement(sql)) { + ps.setObject(1, userId); + ps.setObject(2, userId); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) return (UUID) rs.getObject("chat_id"); + } + } catch (SQLException e) { e.printStackTrace(); } + return null; + } + + + + + + private static PrivateChat map(ResultSet rs) throws SQLException { + return new PrivateChat( + (UUID) rs.getObject("chat_id"), + (UUID) rs.getObject("user1_id"), + (UUID) rs.getObject("user2_id"), + rs.getBoolean("user1_deleted"), + rs.getBoolean("user2_deleted") + ); + } + } diff --git a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java index 17ca2a9..79751f5 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java +++ b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java @@ -27,6 +27,14 @@ public class PrivateChat { this.created_at = createdAt; } + public PrivateChat(UUID chatId, UUID user1, UUID user2, boolean user1Deleted, boolean user2Deleted) { + this.chat_id = chatId; + this.user1_id = user1; + this.user2_id = user2; + this.user1_deleted = user1Deleted; + this.user2_deleted = user2Deleted; + } + public void setUser1_id(UUID user1_id){this.user1_id =user1_id;} public void setUser2_id(UUID user2_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 44cfaac..4a10df4 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -159,45 +159,91 @@ public class ClientHandler implements Runnable { // } +// +// 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.getLastMessageTime(chat.getChat_id(), "private"); +// +// +// ChatEntry entry = new ChatEntry( +// chat.getChat_id(), +// otherUser.getUser_id(), +// otherUser.getProfile_name(), +// otherUser.getImage_url(), +// "private", +// lastMessageTime, +// false, +// false +// ); +// entry.setOtherUserId(otherId); +// +// if (currentUser.getInternal_uuid() == otherId) { +// entry.setSavedMessages(true); +// } +// +// if (archivedChatIds.contains(chat.getChat_id())) { +// archivedChatList.add(entry); +// chatList.add(entry); +// } else { +// activeChatList.add(entry); +// chatList.add(entry); +// } +// } List privateChats = PrivateChatDatabase.findChatsOfUser(currentUser.getInternal_uuid()); + + ChatEntry savedEntry = null; + for (PrivateChat chat : privateChats) { - UUID otherId = chat.getUser1_id().equals(currentUser.getInternal_uuid()) ? - chat.getUser2_id() : chat.getUser1_id(); + boolean isSelf = + chat.getUser1_id().equals(chat.getUser2_id()) && + chat.getUser1_id().equals(currentUser.getInternal_uuid()); + + // در غیر self، otherId = طرف مقابل + UUID otherId = isSelf + ? currentUser.getInternal_uuid() + : (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.getLastMessageTime(chat.getChat_id(), "private"); - - ChatEntry entry = new ChatEntry( - chat.getChat_id(), - otherUser.getUser_id(), - otherUser.getProfile_name(), - otherUser.getImage_url(), + ChatEntry entry = new ChatEntry( + chat.getChat_id(), // internal_id = chat_id + isSelf ? "Saved Messages" : otherUser.getUser_id(), // id/display + isSelf ? "Saved Messages" : otherUser.getProfile_name(), // name + isSelf ? null : otherUser.getImage_url(), "private", lastMessageTime, - false, - false + false, // isOwner + false // isAdmin ); entry.setOtherUserId(otherId); - - if (currentUser.getInternal_uuid() == otherId) { - entry.setSavedMessages(true); + if (isSelf) { + entry.setSavedMessages(true); // فلگ مهم + savedEntry = entry; // برای بردن به اول لیست } - if (archivedChatIds.contains(chat.getChat_id())) { + if (!isSelf && archivedChatIds.contains(chat.getChat_id())) { archivedChatList.add(entry); - chatList.add(entry); } else { activeChatList.add(entry); - chatList.add(entry); } + chatList.add(entry); } + for (Group group : groups) { LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group"); boolean isOwner = GroupDatabase.isOwner(group.getInternal_uuid(), user.getInternal_uuid()); @@ -2497,7 +2543,7 @@ public class ClientHandler implements Runnable { case "get_saved_messages": { UUID user_Id = UUID.fromString(requestJson.getString("user_id")); -// response = SidebarService.handleGetSavedMessages(user_Id); + response = SidebarService.handleGetSavedMessages(user_Id); break; } @@ -2522,6 +2568,12 @@ public class ClientHandler implements Runnable { break; } + case "get_or_create_saved_messages": { + response = handleGetOrCreateSavedMessages(requestJson); + } + break; + + default: response = new ResponseModel("error", "Unknown action: " + action); } @@ -2603,6 +2655,7 @@ public class ClientHandler implements Runnable { } + String content = json.optString("content", ""); String messageType = json.optString("message_type", "TEXT"); @@ -2716,5 +2769,22 @@ public class ClientHandler implements Runnable { + private ResponseModel handleGetOrCreateSavedMessages(JSONObject req) { + if (currentUser == null) return new ResponseModel("error", "Unauthorized."); + + UUID uid = currentUser.getInternal_uuid(); + PrivateChat chat = PrivateChatDatabase.findSelfChat(uid); + UUID chatId = (chat != null) ? chat.getChat_id() : PrivateChatDatabase.createSelfChat(uid); + if (chatId == null) return new ResponseModel("error", "Failed to create Saved Messages."); + + JSONObject data = new JSONObject(); + data.put("chat_id", chatId.toString()); + data.put("name", "Saved Messages"); + data.put("is_saved_messages", true); + data.put("chat_type", "private"); + return new ResponseModel("success", "Saved Messages ready.", data); + } + + } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java b/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java index a10035e..94fb542 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java +++ b/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java @@ -211,50 +211,49 @@ public class SidebarService { } } - // Get saved messages data -// public static ResponseModel handleGetSavedMessages(UUID userId) { -// try { -// -// UUID chatId = PrivateChatDatabase.getOrCreateSavedMessagesChat(userId); -// if (chatId == null) { -// return new ResponseModel("error", "Failed to create or find saved messages chat."); -// } -// -// List messages = MessageDatabase.privateChatHistory(chatId); -// -// JSONArray messageArray = new JSONArray(); -// if (!messages.isEmpty()) { -// for (Message msg : messages) { -// JSONObject msgJson = new JSONObject(); -// msgJson.put("message_id", msg.getMessage_id().toString()); -// msgJson.put("sender_id", msg.getSender_id().toString()); -// msgJson.put("receiver_type", msg.getReceiver_type()); -// msgJson.put("receiver_id", msg.getReceiver_id().toString()); -// msgJson.put("content", msg.getContent()); -// msgJson.put("message_type", msg.getMessage_type()); -// msgJson.put("send_at", msg.getSend_at().toString()); // LocalDateTime -// msgJson.put("status", msg.getStatus()); -// msgJson.put("reply_to_id", msg.getReply_to_id() != null ? msg.getReply_to_id().toString() : JSONObject.NULL); -// msgJson.put("is_edited", msg.isIs_edited()); -// msgJson.put("original_message_id", msg.getOriginal_message_id() != null ? msg.getOriginal_message_id().toString() : JSONObject.NULL); -// msgJson.put("forwarded_by", msg.getForwarded_by() != null ? msg.getForwarded_by().toString() : JSONObject.NULL); -// msgJson.put("forwarded_from", msg.getForwarded_from() != null ? msg.getForwarded_from().toString() : JSONObject.NULL); -// -// messageArray.put(msgJson); -// } -// } -// -// JSONObject data = new JSONObject(); -// data.put("chat_id", chatId.toString()); -// data.put("messages", messageArray); -// -// return new ResponseModel("success", "Saved messages retrieved successfully", data); -// -// } catch (Exception e) { -// e.printStackTrace(); -// return new ResponseModel("error", "Unexpected server error."); -// } -// } + public static ResponseModel handleGetSavedMessages(UUID userId) { + try { + + UUID chatId = PrivateChatDatabase.getOrCreateSavedMessagesChat(userId); + if (chatId == null) { + return new ResponseModel("error", "Failed to create or find saved messages chat."); + } + + List messages = MessageDatabase.privateChatHistory(chatId, userId); + + JSONArray messageArray = new JSONArray(); + if (!messages.isEmpty()) { + for (Message msg : messages) { + JSONObject msgJson = new JSONObject(); + msgJson.put("message_id", msg.getMessage_id().toString()); + msgJson.put("sender_id", msg.getSender_id().toString()); + msgJson.put("receiver_type", msg.getReceiver_type()); + msgJson.put("receiver_id", msg.getReceiver_id().toString()); + msgJson.put("content", msg.getContent()); + msgJson.put("message_type", msg.getMessage_type()); + msgJson.put("send_at", msg.getSend_at().toString()); // LocalDateTime + msgJson.put("status", msg.getStatus()); + msgJson.put("reply_to_id", msg.getReply_to_id() != null ? msg.getReply_to_id().toString() : JSONObject.NULL); + msgJson.put("is_edited", msg.isIs_edited()); + msgJson.put("original_message_id", msg.getOriginal_message_id() != null ? msg.getOriginal_message_id().toString() : JSONObject.NULL); + msgJson.put("forwarded_by", msg.getForwarded_by() != null ? msg.getForwarded_by().toString() : JSONObject.NULL); + msgJson.put("forwarded_from", msg.getForwarded_from() != null ? msg.getForwarded_from().toString() : JSONObject.NULL); + + messageArray.put(msgJson); + } + } + + JSONObject data = new JSONObject(); + data.put("chat_id", chatId.toString()); + data.put("messages", messageArray); + + return new ResponseModel("success", "Saved messages retrieved successfully", data); + + } catch (Exception e) { + e.printStackTrace(); + return new ResponseModel("error", "Unexpected server error."); + } + } // Save messages to DB public static ResponseModel handleSendMessage(JSONObject requestJson) {