diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index 81ed81a..3e74765 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -3378,16 +3378,37 @@ public class ActionHandler { String editedAt = msg.optString("edited_at", ""); String label = isEdited ? "šŸ–Šļø (edited at: " + editedAt + ")" : ""; - //for reply - if (msg.has("reply_to_sender") && msg.has("reply_to_content")) { - String replySender = msg.optString("reply_to_sender", "Unknown"); - String replyContent = msg.optString("reply_to_content", "(no content)"); - System.out.printf(" ā†Ŗļø Replying to [%s]: %s\n", replySender, replyContent); + // āœ… Forwarded info + String forwardLabel = ""; + if (msg.optBoolean("is_forwarded", false)) { + String forwardFrom = msg.optString("forwarded_from_name", "Unknown"); + forwardLabel = "šŸ” Forwarded from " + forwardFrom; } - System.out.printf("[%d] [%s] %s: %s %s\n", i + 1, time, senderName, content, label); + //for reply + String replyLabel = ""; + if (msg.has("reply_to_id")) { + String repliedSender = msg.optString("reply_to_sender", "Unknown"); + String repliedContent = msg.optString("reply_to_content", "..."); + replyLabel = "ā†Ŗļø Reply to " + repliedSender + ": \"" + repliedContent + "\""; + } + + JSONArray reactions = msg.optJSONArray("reactions"); + if (reactions != null && !reactions.isEmpty()) { + System.out.print(" šŸ’¬ Reactions: "); + for (int j = 0; j < reactions.length(); j++) { + System.out.print(reactions.getString(j) + " "); + } + System.out.println(); + } + + + System.out.printf("[%d] [%s] %s: \n", i + 1, time, senderName); + if (!forwardLabel.isEmpty()) System.out.println(forwardLabel); + if (!replyLabel.isEmpty()) System.out.println(replyLabel); + System.out.printf("%s %s\n", content, label); + System.out.println("─────────────────────────────────────────────"); } - System.out.println("─────────────────────────────────────────────"); System.out.print(""" šŸ’¬ Options: @@ -3507,15 +3528,73 @@ public class ActionHandler { } - private String reactToMessage(UUID messageId) { - return "not ready"; + private void reactToMessage(UUID messageId) { + System.out.print("😊 Enter your reaction (e.g., ā¤ļø, šŸ‘, šŸ˜‚): "); + String reaction = scanner.nextLine().trim(); + if (reaction.isEmpty()) { + System.out.println("āš ļø Empty reaction discarded."); + return; + } + + JSONObject req = new JSONObject(); + req.put("action", "react_to_message"); + req.put("message_id", messageId.toString()); + req.put("reaction", reaction); + + JSONObject res = sendWithResponse(req); + if (res != null && res.getString("status").equals("success")) { + System.out.println("āœ… Reaction sent."); + } else { + System.out.println("āŒ Failed to react to message."); + } } - private String forwardMessage(UUID messageId) { - return "not ready"; + + private void forwardMessage(UUID originalMessageId) { + System.out.println("\nšŸ“¤ Select a chat to forward this message to:"); + + List chatList = Session.chatList; + for (int i = 0; i < chatList.size(); i++) { + ChatEntry c = chatList.get(i); + System.out.printf("%d. [%s] %s\n", i + 1, c.getType(), c.getName()); + } + System.out.println("0. Cancel"); + + System.out.print("āž¤ Enter chat number: "); + String input = scanner.nextLine().trim(); + + if (input.equals("0")) return; + + try { + int choice = Integer.parseInt(input); + if (choice < 1 || choice > chatList.size()) { + System.out.println("āŒ Invalid choice."); + return; + } + + ChatEntry target = chatList.get(choice - 1); + + JSONObject req = new JSONObject(); + req.put("action", "forward_message"); + req.put("target_chat_id", target.getId()); + req.put("target_chat_type", target.getType()); + req.put("original_message_id", originalMessageId.toString()); + req.put("forwarded_by", Session.currentUser.getString("internal_uuid")); + + JSONObject res = sendWithResponse(req); + if (res != null && res.getString("status").equals("success")) { + System.out.println("āœ… Message forwarded successfully."); + } else { + System.out.println("āŒ Failed to forward the message."); + } + + } catch (NumberFormatException e) { + System.out.println("āŒ Invalid input."); + } } + private void replyToMessage(UUID messageId) { System.out.print("šŸ’¬ Enter your reply: "); String content = scanner.nextLine().trim(); diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java index e3f4293..404f2dc 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java @@ -282,6 +282,42 @@ public class MessageDatabase { } } + public static boolean saveForwardedMessage(Message message) { + String sql = """ + INSERT INTO messages ( + message_id, sender_id, receiver_type, receiver_id, + content, message_type, send_at, status, + reply_to_id, is_edited, is_deleted_globally, + original_message_id, forwarded_by, forwarded_from + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + + ps.setObject(1, message.getMessage_id()); + ps.setObject(2, message.getSender_id()); + ps.setString(3, message.getReceiver_type()); + ps.setObject(4, message.getReceiver_id()); + ps.setString(5, message.getContent()); + ps.setString(6, message.getMessage_type()); + ps.setTimestamp(7, Timestamp.valueOf(message.getSend_at())); + ps.setString(8, message.getStatus()); + ps.setObject(9, message.getReply_to_id()); + ps.setBoolean(10, message.isIs_edited()); + ps.setBoolean(11, message.isIs_deleted_globally()); + ps.setObject(12, message.getOriginal_message_id()); //original id + ps.setObject(13, message.getForwarded_by()); //forwarded by + ps.setObject(14, message.getForwarded_from()); //forwarded from + + return ps.executeUpdate() > 0; + + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + public void markMessageAsRead(UUID messageId, UUID userId) { diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageReactionDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageReactionDatabase.java new file mode 100644 index 0000000..35fdb5a --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageReactionDatabase.java @@ -0,0 +1,47 @@ +package org.to.telegramfinalproject.Database; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class MessageReactionDatabase { + + public static boolean saveOrUpdateReaction(UUID messageId, UUID userId, String reaction) { + String sql = """ + INSERT INTO message_reactions (message_id, user_id, emoji) + VALUES (?, ?, ?) + ON CONFLICT (message_id, user_id) + DO UPDATE SET emoji = EXCLUDED.emoji, reacted_at = CURRENT_TIMESTAMP + """; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, messageId); + ps.setObject(2, userId); + ps.setString(3, reaction); + return ps.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public static List getReactions(UUID messageId) { + String sql = "SELECT emoji FROM message_reactions WHERE message_id = ?"; + List reactions = new ArrayList<>(); + try (Connection conn = ConnectionDb.connect(); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, messageId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + reactions.add(rs.getString("emoji")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return reactions; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index e8b63da..3a0e510 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -2149,7 +2149,7 @@ public class ClientHandler implements Runnable { obj.put("is_deleted_globally", m.isIs_deleted_globally()); obj.put("edited_at", m.getEdited_at() != null ? m.getEdited_at().toString() : JSONObject.NULL); - //for reply + //For reply messages if (m.getReply_to_id() != null) { Message replied = MessageDatabase.findById(m.getReply_to_id()); if (replied != null) { @@ -2160,9 +2160,26 @@ public class ClientHandler implements Runnable { } } + // For forwarded messages + if (m.getOriginal_message_id() != null && m.getForwarded_from() != null) { + User originalSender = userDatabase.findByInternalUUID(m.getForwarded_from()); + obj.put("is_forwarded", true); + obj.put("forwarded_from_id", m.getForwarded_from().toString()); + obj.put("forwarded_from_name", originalSender != null ? originalSender.getProfile_name() : "Unknown"); + } else { + obj.put("is_forwarded", false); + } + + //For reactions + List reactions = MessageReactionDatabase.getReactions(m.getMessage_id()); + obj.put("reactions", new JSONArray(reactions)); + + result.put(obj); } + + JSONObject data = new JSONObject(); data.put("get_chat_messages", result); @@ -2273,6 +2290,58 @@ public class ClientHandler implements Runnable { break; } + case "forward_message" : { + UUID originalMessageId = UUID.fromString(requestJson.getString("original_message_id")); + UUID targetChatId = UUID.fromString(requestJson.getString("target_chat_id")); + String targetChatType = requestJson.getString("target_chat_type"); + + // original message + Message original = MessageDatabase.findById(originalMessageId); + if (original == null) { + response = new ResponseModel("error", "Original message not found."); + break; + } + + //make forwarded message + Message forwarded = new Message( + UUID.randomUUID(), + currentUser.getInternal_uuid(), + targetChatType, + targetChatId, + original.getContent(), + original.getMessage_type(), + LocalDateTime.now(), + "SEND", + null, // reply_to_id + false, // is_edited + false, // is_deleted_globally + original.getMessage_id(), //original message id + currentUser.getInternal_uuid(), // forwarded_by + original.getSender_id(), // forwarded_from + null // edited_at + ); + + boolean success = MessageDatabase.saveForwardedMessage(forwarded); + if (success) { + response = new ResponseModel("success", "Message forwarded."); + // (اختیاری) Ų§Ų±Ų³Ų§Ł„ Ų±ŪŒŁ„ ŲŖŲ§ŪŒŁ… به اعضای چت مقصد + } else { + response = new ResponseModel("error", "Failed to forward message."); + } + break; + } + + case "react_to_message": { + UUID messageId = UUID.fromString(requestJson.getString("message_id")); + String reaction = requestJson.getString("reaction"); + boolean success = MessageReactionDatabase.saveOrUpdateReaction(messageId, currentUser.getInternal_uuid(), reaction); + + response = success + ? new ResponseModel("success", "Reaction saved.") + : new ResponseModel("error", "Failed to save reaction."); + break; + } +