From 775350f8ce1bd74698a6394e81e8ceb0729d82dc Mon Sep 17 00:00:00 2001 From: Partow Roshani Date: Sun, 27 Jul 2025 16:15:25 +0330 Subject: [PATCH] work on archived chats --- .../Client/ActionHandler.java | 216 ++++++++++++++++-- .../telegramfinalproject/Client/Session.java | 13 ++ .../Models/ChatEntry.java | 13 +- .../Server/ClientHandler.java | 139 ++++++++++- 4 files changed, 356 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index 5d51ac6..6173d93 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -440,8 +440,11 @@ public class ActionHandler { Session.currentUser = response.getJSONObject("data"); JSONArray chatListJson = Session.currentUser.getJSONArray("chat_list"); + JSONArray Archived = Session.currentUser.getJSONArray("archived_chat_list"); + JSONArray Active = Session.currentUser.getJSONArray("active_chat_lis"); List chatList = new ArrayList<>(); + for (Object obj : chatListJson) { JSONObject chat = (JSONObject) obj; @@ -462,7 +465,48 @@ public class ActionHandler { } + 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) + ); + 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) + ); + + activeChats.add(entry); + + } + + + + + Session.activeChats = activeChats; + Session.archivedChats = archivedChats; Session.chatList = chatList; break; @@ -725,34 +769,110 @@ public class ActionHandler { } } +// public void showChatListAndSelect() { +// +// +// List chatList = Session.getChatList(); +// if (chatList.isEmpty()) { +// System.out.println("📭 You have no chats."); +// return; +// } +// +// System.out.println("\nYour Chats:"); +// for (int i = 0; i < chatList.size(); i++) { +// ChatEntry entry = chatList.get(i); +// String last = entry.getLastMessageTime() == null ? "No messages yet" : entry.getLastMessageTime().toString(); +// System.out.printf("%d. [%s] %s - Last: %s\n", i + 1, entry.getType(), entry.getName(), last); +// } +// +// System.out.print("Select a chat by number: "); +// int choice = Integer.parseInt(scanner.nextLine()); +// if (choice < 1 || choice > chatList.size()) { +// System.out.println("❌ Invalid choice."); +// return; +// } +// +// openChat(chatList.get(choice - 1)); +// } + + public void showChatListAndSelect() { - - - List chatList = Session.getChatList(); - if (chatList.isEmpty()) { - System.out.println("📭 You have no chats."); + if (Session.activeChats == null || Session.activeChats.isEmpty()) { + System.out.println("No active chats."); return; } System.out.println("\nYour Chats:"); - for (int i = 0; i < chatList.size(); i++) { - ChatEntry entry = chatList.get(i); - String last = entry.getLastMessageTime() == null ? "No messages yet" : entry.getLastMessageTime().toString(); - System.out.printf("%d. [%s] %s - Last: %s\n", i + 1, entry.getType(), entry.getName(), last); + System.out.println("0. 📦 Archived Chats"); + + for (int i = 0; i < Session.activeChats.size(); i++) { + ChatEntry entry = Session.activeChats.get(i); + String time = (entry.getLastMessageTime() == null) + ? "No messages yet" + : entry.getLastMessageTime().toString(); + System.out.println((i + 1) + ". [" + entry.getType() + "] " + + entry.getName() + " - Last: " + time); } System.out.print("Select a chat by number: "); int choice = Integer.parseInt(scanner.nextLine()); - if (choice < 1 || choice > chatList.size()) { - System.out.println("❌ Invalid choice."); + + if (choice == 0) { + showArchivedChats(); return; } - openChat(chatList.get(choice - 1)); + int index = choice - 1; + + if (index < 0 || index >= Session.activeChats.size()) { + System.out.println("Invalid selection."); + return; + } + + ChatEntry selected = Session.activeChats.get(index); + openChat(selected); } + private void showArchivedChats() { + if (Session.archivedChats == null || Session.archivedChats.isEmpty()) { + System.out.println("📭 No archived chats."); + return; + } + + System.out.println("\n📦 Archived Chats:"); + for (int i = 0; i < Session.archivedChats.size(); i++) { + ChatEntry entry = Session.archivedChats.get(i); + String time = (entry.getLastMessageTime() == null) + ? "No messages yet" + : entry.getLastMessageTime().toString(); + System.out.println((i + 1) + ". [" + entry.getType() + "] " + + entry.getName() + " - Last: " + time); + } + + System.out.print("Select a chat by number (or 0 to return): "); + int choice = Integer.parseInt(scanner.nextLine()); + + if (choice == 0) { + System.out.println("🔙 Returning to main chat list..."); + return; + } + + int index = choice - 1; + + if (index < 0 || index >= Session.archivedChats.size()) { + System.out.println("❌ Invalid selection."); + return; + } + + ChatEntry selected = Session.archivedChats.get(index); + openChat(selected); + } + + + + private void openChat(ChatEntry chat) { JSONObject req = new JSONObject(); req.put("action", "get_messages"); @@ -856,7 +976,8 @@ public class ActionHandler { 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. Back"); + System.out.println("6. Archive/Unarchived"); + System.out.println("7. Back"); String input = scanner.nextLine(); @@ -897,7 +1018,13 @@ public class ActionHandler { return true; } - case "6" -> { + case "6" ->{ + toggleArchive(chat.getId() , "private"); + + return true; + } + + case "7" -> { return false; } default -> System.out.println("Invalid choice."); @@ -961,6 +1088,8 @@ public class ActionHandler { System.out.println("11. Edit Admin Permissions"); } + System.out.println("12. Archive/Unarchived"); + System.out.println("0. Back to Chat List"); String input = scanner.nextLine(); @@ -1010,6 +1139,10 @@ public class ActionHandler { } } + case "12"->{ + toggleArchive(chat.getId() , "group"); + } + case "0" -> { return false; } @@ -1076,6 +1209,7 @@ public class ActionHandler { System.out.println("11. Edit Admin Permissions"); } + System.out.println("12. Archive/Unarchived"); System.out.println("0. Back to Chat List"); String input = scanner.nextLine(); @@ -1155,6 +1289,10 @@ public class ActionHandler { } } + case "12"->{ + toggleArchive(chat.getId() , "channel"); + } + case "0" -> { return false; @@ -2714,6 +2852,56 @@ public class ActionHandler { + public void toggleArchive(UUID chatId, String chatType) { + // پیدا کردن چت از Session.chatList + Optional optional = Session.chatList.stream() + .filter(c -> c.getId().equals(chatId)) + .findFirst(); + + if (optional.isEmpty()) { + System.out.println("❌ Chat not found."); + return; + } + + ChatEntry chat = optional.get(); + + if (chat.isArchived()) { + unarchiveChat(chatId, chatType); + chat.setArchived(false); + System.out.println("✅ Chat unarchived."); + } else { + archiveChat(chatId, chatType); + chat.setArchived(true); + System.out.println("✅ Chat archived."); + } + + Session.refreshChatLists(); + } + + + private void archiveChat(UUID chatId, String chatType) { + JSONObject req = new JSONObject(); + req.put("action", "archive_chat"); + req.put("chat_id", chatId.toString()); + req.put("chat_type", chatType); + JSONObject res = sendWithResponse(req); + if (res != null) System.out.println(res.getString("message")); + } + + private void unarchiveChat(UUID chatId, String chatType) { + JSONObject req = new JSONObject(); + req.put("action", "unarchive_chat"); + req.put("chat_id", chatId.toString()); + req.put("chat_type", chatType); + JSONObject res = sendWithResponse(req); + if (res != null) System.out.println(res.getString("message")); + } + + + + + + } diff --git a/src/main/java/org/to/telegramfinalproject/Client/Session.java b/src/main/java/org/to/telegramfinalproject/Client/Session.java index f849e41..c49bb3d 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/Session.java +++ b/src/main/java/org/to/telegramfinalproject/Client/Session.java @@ -13,6 +13,9 @@ import java.util.UUID; public class Session { public static JSONObject currentUser; public static List chatList = new ArrayList<>(); + public static List archivedChats = new ArrayList<>(); + public static List activeChats = new ArrayList<>(); + public static volatile boolean forceRefreshChatList = false; public static volatile boolean backToChatList = false; public static boolean inChatListMenu = false; @@ -55,4 +58,14 @@ public class Session { return chatList; } + + public static void refreshChatLists() { + Session.activeChats = Session.chatList.stream() + .filter(c -> !c.isArchived()) + .toList(); + + Session.archivedChats = Session.chatList.stream() + .filter(ChatEntry::isArchived) + .toList(); + } } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java index 7491297..b634ce3 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java +++ b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java @@ -12,8 +12,9 @@ public class ChatEntry { private String imageUrl; private String type; private LocalDateTime lastMessageTime; + private boolean archived = false; + - // 🔹 نقش‌ها private boolean isOwner = false; private boolean isAdmin = false; private JSONObject permissions; @@ -28,7 +29,6 @@ public class ChatEntry { this.lastMessageTime = lastMessageTime; } - // ✅ کانستراکتور اضافه‌شده برای پشتیبانی از نقش‌ها (اختیاری، برای استفاده‌های جدید) public ChatEntry(UUID internalId, String displayId, String name, String imageUrl, String type, LocalDateTime lastMessageTime, boolean isOwner, boolean isAdmin) { this(internalId, displayId, name, imageUrl, type, lastMessageTime); this.isOwner = isOwner; @@ -41,7 +41,6 @@ public class ChatEntry { } - // 🟩 گتر و ستر جدید public boolean isOwner() { return isOwner; } @@ -108,4 +107,12 @@ public class ChatEntry { + public boolean isArchived() { + return archived; + } + + public void setArchived(boolean archived) { + this.archived = archived; + } + } diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index e76ce67..28eb1cf 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -87,36 +87,80 @@ public class ClientHandler implements Runnable { List groups = GroupDatabase.getGroupsByUser(user.getInternal_uuid()); List channels = ChannelDatabase.getChannelsByUser(user.getInternal_uuid()); List unreadMessages = MessageDatabase.getUnreadMessages(user.getInternal_uuid()); + List archivedChatIds = ArchivedChatDatabase.getArchivedChats(user.getInternal_uuid()); + + user.setContactList(contacts); user.setGroupList(groups); user.setChannelList(channels); user.setUnreadMessages(unreadMessages); + + 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(user.getInternal_uuid(), target.getInternal_uuid(), "private"); - chatList.add(new ChatEntry( - target.getInternal_uuid(), // internal UUID - target.getUser_id(), // public display ID +// 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 - )); + ); + + + + if (archivedChatIds.contains(targetId)) { + 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(), user.getInternal_uuid()); boolean isAdmin = GroupDatabase.isAdmin(group.getInternal_uuid(), user.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(), @@ -125,7 +169,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) { @@ -133,7 +187,18 @@ public class ClientHandler implements Runnable { boolean isOwner = ChannelDatabase.isOwner(channel.getInternal_uuid(), user.getInternal_uuid()); boolean isAdmin = ChannelDatabase.isAdmin(channel.getInternal_uuid(), user.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(), @@ -142,9 +207,29 @@ 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; @@ -154,6 +239,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)); response = new ResponseModel("success", "Welcome " + user.getProfile_name(), userData); } break; @@ -1708,6 +1795,42 @@ public class ClientHandler implements Runnable { // } // + case "archive_chat": { + if (currentUser == null) { + response = new ResponseModel("error", "Unauthorized. Please login first."); + break; + } + + UUID chatId = UUID.fromString(requestJson.getString("chat_id")); + String chatType = requestJson.getString("chat_type"); + + boolean success = ArchivedChatDatabase.archiveChat(currentUser.getInternal_uuid(), chatId, chatType); + response = success + ? new ResponseModel("success", "Chat archived successfully.") + : new ResponseModel("error", "Failed to archive chat."); + break; + } + + + case "unarchive_chat": { + if (currentUser == null) { + response = new ResponseModel("error", "Unauthorized. Please login first."); + break; + } + + UUID chatId = UUID.fromString(requestJson.getString("chat_id")); + + boolean success = ArchivedChatDatabase.unarchiveChat(currentUser.getInternal_uuid(), chatId); + response = success + ? new ResponseModel("success", "Chat unarchived successfully.") + : new ResponseModel("error", "Failed to unarchive chat."); + break; + } + + + + + default: