diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index aa9264e..f2605f4 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -3,6 +3,7 @@ package org.to.telegramfinalproject.Client; import org.json.JSONArray; import org.json.JSONObject; import org.to.telegramfinalproject.Database.PrivateChatDatabase; +import org.to.telegramfinalproject.Database.ContactDatabase; import org.to.telegramfinalproject.Models.ChatEntry; import org.to.telegramfinalproject.Models.ContactEntry; import org.to.telegramfinalproject.Models.SearchRequestModel; @@ -558,9 +559,13 @@ public class ActionHandler { ContactEntry entry = new ContactEntry( UUID.fromString(c.getString("contact_id")), c.getString("user_id"), + c.getString("contact_displayId"), c.getString("profile_name"), c.optString("image_url", ""), - c.optBoolean("is_blocked", false) + c.optBoolean("is_blocked", false), + c.isNull("last_seen") + ? null + : LocalDateTime.parse(c.getString("last_seen")) ); Session.contactEntries.add(entry); } @@ -838,10 +843,118 @@ 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."); +// } +// } + public void showContactList() { - List contacts = Session.contactEntries; + System.out.println("1. View All Contacts"); + System.out.println("2. Search Contacts"); + System.out.println("Choose an option: (0 to go back)"); + int option = scanner.nextInt(); + scanner.nextLine(); + + // Handle invalid input + while (option < 0 || option > 2) { + System.out.println("Invalid choice. Try again: "); + option = scanner.nextInt(); + scanner.nextLine(); + } + + List contacts; + + if (option == 0) { + return; + } + else if (option == 1) { + contacts = Session.contactEntries; + + } else if (option == 2) { + contacts = new ArrayList<>(); + + System.out.print("Enter name or user ID to search: "); + String searchTerm = scanner.nextLine(); + + // Handle invalid input + while (searchTerm.isEmpty()) { + System.out.print("Search key can not be empty. Try again: "); + searchTerm = scanner.nextLine(); + } + + // Send a request to server + JSONObject request = new JSONObject(); + request.put("action", "search_contacts"); + request.put("user_id", Session.getUserUUID()); + request.put("search_term", searchTerm); + + JSONObject response = ActionHandler.sendWithResponse(request); + + if (!response.optString("status", "fail").equals("success")) { + System.out.println("Failed to search contacts: " + response.optString("message", "Unknown error")); + return; + } + + JSONObject data = response.getJSONObject("data"); + JSONArray contactsJson = data.getJSONArray("contacts"); + + for (int i = 0; i < contactsJson.length(); i++) { + JSONObject contact = contactsJson.getJSONObject(i); + + UUID contactId = UUID.fromString(contact.getString("contact_id")); + String userId = contact.getString("user_id"); + String contact_displayId = contact.getString("contact_display_id"); + String profileName = contact.getString("profile_name"); + String imageUrl = contact.optString("image_url", ""); + boolean isBlocked = contact.getBoolean("is_blocked"); + String lastSeenString = contact.getString("last_seen"); + LocalDateTime lastSeen = null; + if (lastSeenString != null) { + lastSeen = LocalDateTime.parse(lastSeenString); + } + + contacts.add(new ContactEntry(contactId, userId, contact_displayId, profileName, imageUrl, isBlocked, lastSeen)); + } + + } else { + System.out.println("āŒ Invalid choice."); + return; + } + if (contacts.isEmpty()) { - System.out.println("šŸ“­ You have no contacts."); + System.out.println("šŸ“­ No contacts found."); return; } @@ -864,6 +977,7 @@ public class ActionHandler { 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.println("3. Remove Contact"); System.out.print("Enter your choice: "); int action = scanner.nextInt(); scanner.nextLine(); @@ -871,10 +985,29 @@ public class ActionHandler { switch (action) { case 1 -> viewProfile(selected.getContactId()); case 2 -> startPrivateChat(selected); + case 3 -> { + // Send a request to server + JSONObject request = new JSONObject(); + request.put("action", "remove_contact"); + request.put("user_id", Session.getUserUUID()); // Current user + request.put("contact_id", selected.getContactId().toString()); // Contact to remove + + JSONObject response = ActionHandler.sendWithResponse(request); + + if ("success".equals(response.optString("status"))) { + System.out.println("āœ… Contact removed successfully."); + Session.contactEntries.remove(selected); // Remove from local session list + } else { + System.out.println("āŒ Failed to remove contact: " + + response.optString("message", "Unknown error")); + } + } + default -> System.out.println("āŒ Invalid option."); } } + private void viewProfile(UUID targetId) { JSONObject req = new JSONObject(); req.put("action", "view_profile"); diff --git a/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java b/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java index c0e4341..d77b632 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java +++ b/src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java @@ -1,5 +1,6 @@ package org.to.telegramfinalproject.Models; +import java.time.LocalDateTime; import java.util.UUID; public class ContactEntry { @@ -8,6 +9,9 @@ public class ContactEntry { private String profileName; private String imageUrl; private boolean isBlocked; + private LocalDateTime lastSeenTime; + private String contact_displayId; + public ContactEntry(UUID contactId, String userId, String profileName, String imageUrl, boolean isBlocked) { this.contactId = contactId; @@ -17,6 +21,16 @@ public class ContactEntry { this.isBlocked = isBlocked; } + public ContactEntry(UUID contactId, String userId,String contact_displayId , String profileName, String imageUrl, boolean isBlocked, LocalDateTime lastSeenTime){ + this.contactId = contactId; + this.userId = userId; + this.contact_displayId = contact_displayId; + this.profileName = profileName; + this.imageUrl = imageUrl; + this.isBlocked = isBlocked; + this.lastSeenTime = lastSeenTime; + } + public UUID getContactId() { return contactId; } @@ -37,8 +51,24 @@ public class ContactEntry { return isBlocked; } + public LocalDateTime getLastSeenTime() { + return lastSeenTime; + } + + public void setLastSeenTime(LocalDateTime lastSeenTime) { + this.lastSeenTime = lastSeenTime; + } + + public String getContact_displayId() { + return contact_displayId; + } + + public void setContact_displayId(String contact_displayId) { + this.contact_displayId = contact_displayId; + } + @Override public String toString() { - return profileName + " (" + userId + ")" + (isBlocked ? " [Blocked]" : ""); + return profileName + " (@" + contact_displayId + ")" + (isBlocked ? " [Blocked]" : "") + " Last Seen:" + (lastSeenTime != null ? " " + lastSeenTime.toString() : ""); } } diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index e1b8628..57f680f 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -1,5 +1,6 @@ package org.to.telegramfinalproject.Server; +import javafx.geometry.Side; import org.json.JSONArray; import org.json.JSONObject; import org.to.telegramfinalproject.Database.*; @@ -111,10 +112,13 @@ public class ClientHandler implements Runnable { JSONObject c = new JSONObject(); c.put("user_id", contact.getUser_id().toString()); c.put("contact_id", contact.getContact_id().toString()); + User Contact = userDatabase.findByInternalUUID(contact.getContact_id()); + c.put("contact_displayId", Contact.getUser_id()); c.put("is_blocked", contact.getIs_blocked()); c.put("profile_name", target.getProfile_name()); c.put("image_url", target.getImage_url()); + c.put("last_seen", Contact.getLast_seen()); contactList.put(c); } @@ -2122,6 +2126,22 @@ public class ClientHandler implements Runnable { break; } + case "search_contacts": { + String user_id = requestJson.getString("user_id"); + String search_term = requestJson.getString("search_term"); + + response = SidebarService.handleSearchContacts(user_id, search_term); + break; + } + + case "remove_contact": { + UUID user_id = UUID.fromString(requestJson.getString("user_id")); + UUID contactId = UUID.fromString(requestJson.getString("contact_id")); + + response = SidebarService.handleRemoveContact(user_id, contactId); + break; + } + default: response = new ResponseModel("error", "Unknown action: " + action); } diff --git a/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java b/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java index ce07eb2..24ddc08 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java +++ b/src/main/java/org/to/telegramfinalproject/Server/SidebarService.java @@ -2,9 +2,11 @@ package org.to.telegramfinalproject.Server; import org.json.JSONArray; import org.json.JSONObject; +import org.to.telegramfinalproject.Database.ContactDatabase; import org.to.telegramfinalproject.Database.MessageDatabase; import org.to.telegramfinalproject.Database.PrivateChatDatabase; import org.to.telegramfinalproject.Database.userDatabase; +import org.to.telegramfinalproject.Models.ContactEntry; import org.to.telegramfinalproject.Models.Message; import org.to.telegramfinalproject.Models.ResponseModel; import org.to.telegramfinalproject.Models.User; @@ -12,6 +14,7 @@ import org.to.telegramfinalproject.Models.User; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -158,6 +161,56 @@ public class SidebarService { } } + // Search in user's contact list + public static ResponseModel handleSearchContacts(String userUUID, String searchTerm) { + if (searchTerm == null || searchTerm.trim().isEmpty()) { + return new ResponseModel("error", "Search term cannot be empty."); + } + + List searchResult = ContactDatabase.searchContacts(UUID.fromString(userUUID), searchTerm); + + if (searchResult.isEmpty()) { + return new ResponseModel("error", "No contacts found."); + } + + JSONObject data = new JSONObject(); + JSONArray contacts = new JSONArray(); + + for (ContactEntry entry : searchResult) { + contacts.put(new JSONObject() + .put("contact_id", entry.getContactId()) + .put("user_id", entry.getUserId()) + .put("contact_display_id", entry.getContact_displayId()) + .put("profile_name", entry.getProfileName()) + .put("image_url", entry.getImageUrl()) + .put("is_blocked", entry.isBlocked()) + .put("last_seen", entry.getLastSeenTime()) + ); + } + data.put("contacts", contacts); + + if (searchResult.isEmpty()) { + return new ResponseModel("error", "No contacts found."); + } + + return new ResponseModel("success", "Search contacts successfully.", data); + } + + // Remove a contact in user's contact list + public static ResponseModel handleRemoveContact(UUID userUUID, UUID contactId) { + if (userUUID == null || contactId == null) { + return new ResponseModel("error", "Invalid input."); + } + + boolean removed = ContactDatabase.removeContact(userUUID, contactId); + + if (removed) { + return new ResponseModel("success", "Contact removed successfully."); + } else { + return new ResponseModel("error", "Contact not found."); + } + } + // Get saved messages data public static ResponseModel handleGetSavedMessages(UUID userId) { try {