diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index 62d30bc..1d4643d 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -3375,7 +3375,8 @@ public class ActionHandler { String content = msg.optString("content", "(no content)"); String time = msg.optString("time", ""); boolean isEdited = msg.optBoolean("is_edited", false); - String label = isEdited ? "🖊️ (edited)" : ""; + String editedAt = msg.optString("edited_at", ""); + String label = isEdited ? "🖊️ (edited at: " + editedAt + ")" : ""; System.out.printf("[%d] [%s] %s: %s %s\n", i + 1, time, senderName, content, label); } System.out.println("─────────────────────────────────────────────"); @@ -3474,11 +3475,30 @@ public class ActionHandler { } } - private String editMessage(UUID messageId) { - return "not ready"; + private void editMessage(UUID messageId) { + System.out.print("📝 Enter new content: "); + String newContent = scanner.nextLine().trim(); + if (newContent.isEmpty()) { + System.out.println("❌ Content cannot be empty."); + return; + } + + JSONObject req = new JSONObject(); + req.put("action", "edit_message"); + req.put("message_id", messageId.toString()); + req.put("new_content", newContent); + + JSONObject res = sendWithResponse(req); + if (res == null || !res.getString("status").equals("success")) { + System.out.println("❌ Failed to edit message."); + return; + } + + System.out.println("✅ Message updated successfully."); } + private String reactToMessage(UUID messageId) { return "not ready"; diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java index ec185e9..260a526 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java @@ -153,7 +153,12 @@ public class MessageDatabase { rs.getBoolean("is_deleted_globally"), rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null, rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null, - rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null + rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null, + rs.getTimestamp("edited_at") != null + ? rs.getTimestamp("edited_at").toLocalDateTime() + : null + + ); messages.add(msg); @@ -222,6 +227,26 @@ public class MessageDatabase { } } + public static boolean updateContentAndMarkEdited(UUID messageId, String newContent) { + String sql = """ + UPDATE messages + SET content = ?, is_edited = TRUE, edited_at = CURRENT_TIMESTAMP + WHERE message_id = ? + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, newContent); + ps.setObject(2, messageId); + + return ps.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public void markMessageAsRead(UUID messageId, UUID userId) { String sql = "INSERT INTO message_receipts (message_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING"; diff --git a/src/main/java/org/to/telegramfinalproject/Models/Message.java b/src/main/java/org/to/telegramfinalproject/Models/Message.java index b7dcca8..663537d 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/Message.java +++ b/src/main/java/org/to/telegramfinalproject/Models/Message.java @@ -1,6 +1,7 @@ package org.to.telegramfinalproject.Models; import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.util.List; import java.util.UUID; @@ -22,6 +23,8 @@ public class Message { private List attachments; private transient String sender_name; private transient String receiver_name; + private LocalDateTime edited_at; + // ✅ Full Constructor @@ -57,6 +60,33 @@ public class Message { this.send_at = sendAt; } + + + public Message(UUID messageId, UUID senderId, String receiverType, UUID receiverId, + String content, String messageType, LocalDateTime sendAt, String status, + UUID replyToId, boolean isEdited, boolean isDeletedGlobally, + UUID originalMessageId, UUID forwardedBy, UUID forwardedFrom, + LocalDateTime editedAt) { + + this.message_id = messageId; + this.sender_id = senderId; + this.receiver_type = receiverType; + this.receiver_id = receiverId; + this.content = content; + this.message_type = messageType; + this.send_at = sendAt; + this.status = status; + this.reply_to_id = replyToId; + this.is_edited = isEdited; + this.is_deleted_globally = isDeletedGlobally; + this.original_message_id = originalMessageId; + this.forwarded_by = forwardedBy; + this.forwarded_from = forwardedFrom; + this.edited_at = editedAt; + } + + + // ✅ Getters & Setters public UUID getMessage_id() { return message_id; } public void setMessage_id(UUID messageId) { this.message_id = messageId; } @@ -120,4 +150,13 @@ public class Message { this.receiver_name = receiver_name; } + public LocalDateTime getEdited_at() { + return edited_at; + } + + public void setEdited_at(LocalDateTime edited_at) { + this.edited_at = edited_at; + } + + } diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index 3a6e57c..461598d 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -2107,8 +2107,8 @@ public class ClientHandler implements Runnable { case "get_chat_messages" : { UUID chatId = UUID.fromString(requestJson.getString("chat_id")); String chatType = requestJson.getString("chat_type"); - int offset = requestJson.optInt("offset", 0); // پیش‌فرض 0 - int limit = requestJson.optInt("limit", 10); // پیش‌فرض 10 + int offset = requestJson.optInt("offset", 0); + int limit = requestJson.optInt("limit", 10); List messages = MessageDatabase.getMessagesForChat(chatId, chatType, currentUser.getInternal_uuid(), offset, limit); @@ -2147,6 +2147,8 @@ public class ClientHandler implements Runnable { obj.put("time", m.getSend_at().toString()); obj.put("is_edited", m.isIs_edited()); obj.put("is_deleted_globally", m.isIs_deleted_globally()); + obj.put("edited_at", m.getEdited_at() != null ? m.getEdited_at().toString() : JSONObject.NULL); + result.put(obj); } @@ -2210,6 +2212,30 @@ public class ClientHandler implements Runnable { break; } + case "edit_message" : { + UUID msgId = UUID.fromString(requestJson.getString("message_id")); + String newContent = requestJson.getString("new_content"); + + Message msg = MessageDatabase.findById(msgId); + if (msg == null) { + response = new ResponseModel("error", "Message not found."); + break; + } + + //Only sender can edit + if (!msg.getSender_id().equals(currentUser.getInternal_uuid())) { + response = new ResponseModel("error", "You can only edit your own messages."); + break; + } + + boolean success = MessageDatabase.updateContentAndMarkEdited(msgId, newContent); + if (!success) { + response = new ResponseModel("error", "Failed to update message."); + } else { + response = new ResponseModel("success", "Message edited."); + } + break; + }