Merge branch 'develop' into SendMessage

# Conflicts:
#	src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java
#	src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java
#	src/main/java/org/to/telegramfinalproject/Models/ContactEntry.java
#	src/main/java/org/to/telegramfinalproject/Models/Message.java
This commit is contained in:
2025-08-11 17:06:57 +03:30
22 changed files with 1523 additions and 213 deletions
@@ -20,7 +20,7 @@ public class AuthService {
} else {
UUID uuid = UUID.randomUUID();
password = PasswordHashing.hash(password);
User user = new User(user_id, uuid, username, password, profile_name);
User user = new User(user_id, uuid, username, password, profile_name, "", "");
return this.userDb.save(user);
}
} else {
@@ -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.*;
@@ -118,6 +119,7 @@ public class ClientHandler implements Runnable {
c.put("profile_name", target.getProfile_name());
c.put("image_url", target.getImage_url());
c.put("last_seen", Contact.getLast_seen());
contactList.put(c);
}
@@ -182,6 +184,10 @@ public class ClientHandler implements Runnable {
);
entry.setOtherUserId(otherId);
if (currentUser.getInternal_uuid() == otherId) {
entry.setSavedMessages(true);
}
if (archivedChatIds.contains(chat.getChat_id())) {
archivedChatList.add(entry);
chatList.add(entry);
@@ -718,6 +724,8 @@ public class ClientHandler implements Runnable {
LocalDateTime lastMessageTime = MessageDatabase.getLastMessageTime(chat.getChat_id(), "private");
boolean isSavedMessages = chat.getUser1_id().equals(currentUser.getInternal_uuid())
&& chat.getUser2_id().equals(currentUser.getInternal_uuid());
ChatEntry entry = new ChatEntry(
chat.getChat_id(),
@@ -731,6 +739,11 @@ public class ClientHandler implements Runnable {
);
entry.setOtherUserId(otherId);
// Mark it as saved messages if it's the special self-chat
if (isSavedMessages) {
entry.setSavedMessages(true);
}
if (archivedChatIds.contains(chat.getChat_id())) {
archivedChatList.add(entry);
chatList.add(entry);
@@ -2412,8 +2425,103 @@ public class ClientHandler implements Runnable {
break;
}
case "get_user_profile": {
// Get the current user's UUID
UUID user_UUID = this.currentUser.getInternal_uuid();
// Get the profile JSON from SidebarService
JSONObject data = SidebarService.getUserProfile(user_UUID);
if (data == null) {
response = new ResponseModel("error", "Failed to retrieve user profile.");
} else {
response = new ResponseModel("success", "User profile retrieved successfully.", data);
}
break;
}
case "edit_profile_name": {
// Validate input
if (!requestJson.has("new_profile_name")) {
response = new ResponseModel("error", "Missing new profile name.");
break;
}
UUID user_UUID = this.currentUser.getInternal_uuid();
String newProfileName = requestJson.getString("new_profile_name");
response = SidebarService.updateProfileName(user_UUID, newProfileName);
break;
}
case "edit_user_id": {
// Validate input
if (!requestJson.has("new_user_id")) {
response = new ResponseModel("error", "Missing new user ID.");
break;
}
UUID user_UUID = this.currentUser.getInternal_uuid();
String newUserId = requestJson.getString("new_user_id");
response = SidebarService.updateUserId(user_UUID, newUserId);
break;
}
case "edit_bio": {
// Validate input
if (!requestJson.has("new_bio")) {
response = new ResponseModel("error", "Missing new bio.");
break;
}
UUID user_UUID = this.currentUser.getInternal_uuid();
String newBio = requestJson.getString("new_bio").trim();
response = SidebarService.updateBio(user_UUID, newBio);
break;
}
case "edit_profile_picture": {
// Validate input
if (!requestJson.has("new_image_url")) {
response = new ResponseModel("error", "Missing new image url.");
break;
}
UUID user_UUID = this.currentUser.getInternal_uuid();
String newImageUrl = requestJson.getString("new_image_url").trim();
response = SidebarService.updateProfilePicture(user_UUID, newImageUrl);
break;
}
case "get_saved_messages": {
UUID user_Id = UUID.fromString(requestJson.getString("user_id"));
// response = SidebarService.handleGetSavedMessages(user_Id);
break;
}
case "send_saved_messages": {
response = SidebarService.handleSendMessage(requestJson);
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);
@@ -0,0 +1,297 @@
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;
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;
public class SidebarService {
private static userDatabase userDB = new userDatabase();
// Returns current user's profile data (name, bio, status, etc.)
public static JSONObject getUserProfile(UUID userUUID) {
User user = userDB.findByInternalUUID(userUUID);
if (user == null) {
return null;
}
JSONObject profile = new JSONObject();
profile.put("user_id", user.getUser_id());
profile.put("profile_name", user.getProfile_name());
profile.put("bio", user.getBio() != null ? user.getBio() : "");
profile.put("status", "ONLINE");
profile.put("profile_picture_url", user.getImage_url());
return profile;
}
// Changes the user's profile picture
public static ResponseModel updateProfilePicture(UUID userUUID, String newImageUrl) {
User user = userDB.findByInternalUUID(userUUID);
if (user == null) {
return new ResponseModel("error", "User not found.");
}
String currentUrl = user.getImage_url();
if (Objects.equals(currentUrl, newImageUrl)) {
return new ResponseModel("error", "No changes made. Profile picture is the same.");
}
user.setImage_url(newImageUrl); // Can be null
boolean success = userDB.updateByUUID(userUUID, user);
if (success) {
return new ResponseModel("success", "Profile picture updated successfully.");
} else {
user.setImage_url(currentUrl);
return new ResponseModel("error", "Failed to update profile picture.");
}
}
// Changes user's bio
public static ResponseModel updateBio(UUID userUUID, String newBio) {
if (newBio.length() > 70) {
return new ResponseModel("error", "Bio is too long.");
}
User user = userDB.findByInternalUUID(userUUID);
if (user == null) {
return new ResponseModel("error", "User not found.");
}
String currentBio = user.getBio();
if (Objects.equals(currentBio, newBio)) {
return new ResponseModel("error", "No changes made. Bio is the same.");
}
user.setBio(newBio);
boolean success = userDB.updateByUUID(userUUID, user);
if (success) {
return new ResponseModel("success", "Bio updated successfully.");
} else {
user.setBio(currentBio);
return new ResponseModel("error", "Failed to update bio.");
}
}
// Changes user-id
public static ResponseModel updateUserId(UUID userUUID, String newUserId) {
if (newUserId == null || newUserId.trim().isEmpty()) {
return new ResponseModel("error", "User ID cannot be empty.");
}
newUserId = newUserId.trim();
if (newUserId.contains(" ")) {
return new ResponseModel("error", "User ID cannot contain spaces.");
}
if (!newUserId.matches("^[a-zA-Z0-9_]+$")) {
return new ResponseModel("error", "User ID can only contain letters, digits, and underscores.");
}
if (userDB.findByUserId(newUserId) != null) {
return new ResponseModel("error", "This user ID is already taken.");
}
User user = userDB.findByInternalUUID(userUUID);
if (user == null) {
return new ResponseModel("error", "User not found.");
}
String currentUserId = user.getUser_id();
if (Objects.equals(currentUserId, newUserId)) {
return new ResponseModel("error", "No changes made. User ID is the same.");
}
user.setUser_id(newUserId);
boolean saved = userDB.updateByUUID(userUUID, user);
if (saved) {
return new ResponseModel("success", "User ID updated successfully.");
} else {
user.setUser_id(currentUserId);
return new ResponseModel("error", "Failed to update user ID due to server error.");
}
}
// Changes user's profile name
public static ResponseModel updateProfileName(UUID userUUID, String newProfileName) {
if (newProfileName == null || newProfileName.trim().isEmpty()) {
return new ResponseModel("error", "Invalid input."); // Invalid input (empty or just spaces)
}
// Fetch the user from database
User user = userDB.findByInternalUUID(userUUID);
if (user == null) {
return new ResponseModel("error", "User not found."); // User doesn't exist
}
String oldName = user.getProfile_name();
if (Objects.equals(oldName, newProfileName)) {
return new ResponseModel("error", "No changes made. Profile name is the same.");
}
// Only set the profile name *after* successful DB update
user.setProfile_name(newProfileName);
boolean saved = userDB.updateByUUID(userUUID, user);
if (saved) {
return new ResponseModel("success", "Profile name updated successfully.");
} else {
user.setProfile_name(oldName);
return new ResponseModel("error", "Failed to update profile name.");
}
}
// 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<ContactEntry> 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 {
//
// UUID chatId = PrivateChatDatabase.getOrCreateSavedMessagesChat(userId);
// if (chatId == null) {
// return new ResponseModel("error", "Failed to create or find saved messages chat.");
// }
//
// List<Message> 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.");
// }
// }
// Save messages to DB
public static ResponseModel handleSendMessage(JSONObject requestJson) {
try {
Message message = new Message(
UUID.fromString(requestJson.getString("message_id")),
UUID.fromString(requestJson.getString("sender_id")),
requestJson.getString("receiver_type"),
UUID.fromString(requestJson.getString("receiver_id")),
requestJson.optString("content", null),
requestJson.optString("message_type", "TEXT"),
LocalDateTime.now(), // send_at
requestJson.optString("status", "SEND"),
requestJson.isNull("reply_to_id") ? null : UUID.fromString(requestJson.getString("reply_to_id")),
requestJson.optBoolean("is_edited", false),
requestJson.isNull("original_message_id") ? null : UUID.fromString(requestJson.getString("original_message_id")),
requestJson.isNull("forwarded_by") ? null : UUID.fromString(requestJson.getString("forwarded_by")),
requestJson.isNull("forwarded_from") ? null : UUID.fromString(requestJson.getString("forwarded_from")),
requestJson.optBoolean("is_deleted_globally", false),
requestJson.isNull("edited_at") ? null :
LocalDateTime.ofInstant(
Instant.ofEpochMilli(requestJson.getLong("edited_at")),
ZoneId.systemDefault()
)
);
MessageDatabase.insertSavedMessage(message);
return new ResponseModel("success", "Message saved successfully.");
} catch (Exception e) {
e.printStackTrace();
return new ResponseModel("error", "Unexpected server error.");
}
}
// Changes username
public static boolean updateUserName(String userId, String newUserName) {
return false;
}
}