work on real time for message actions

This commit is contained in:
2025-08-08 12:04:28 +03:30
parent e2e4bab029
commit a900c66eac
8 changed files with 264 additions and 29 deletions
@@ -2134,10 +2134,14 @@ public class ClientHandler implements Runnable {
yield c != null ? c.getChannel_name() : "Unknown channel";
}
case "private" -> {
UUID otherId = m.getSender_id().equals(currentUser.getInternal_uuid()) ? m.getReceiver_id() : m.getSender_id();
UUID otherId = PrivateChatDatabase.getOtherParticipant(
m.getReceiver_id(), // chat_id
currentUser.getInternal_uuid() // my user uuid
);
User other = userDatabase.findByInternalUUID(otherId);
yield other != null ? other.getProfile_name() : "Unknown user";
}
default -> "Unknown";
};
obj.put("receiver_name", receiverName);
@@ -2228,10 +2232,16 @@ public class ClientHandler implements Runnable {
}
boolean success = MessageDatabase.markAsGloballyDeleted(messageId);
if (success)
if (success) {
response = new ResponseModel("success", "Message deleted globally.");
else
Message updated = MessageDatabase.findById(messageId);
List<UUID> receivers = Receivers.resolveFor(updated.getReceiver_type(), updated.getReceiver_id(), null);
RealTimeEventDispatcher.notifyMessageDeletedGlobal(updated.getReceiver_id(), messageId, receivers);
}
else{
response = new ResponseModel("error", "Failed to delete message globally.");
}
break;
}
@@ -2260,6 +2270,10 @@ public class ClientHandler implements Runnable {
response = new ResponseModel("error", "Failed to update message.");
} else {
response = new ResponseModel("success", "Message edited.");
Message updated = MessageDatabase.findById(msgId);
List<UUID> receivers = Receivers.resolveFor(updated.getReceiver_type(), updated.getReceiver_id(), null);
RealTimeEventDispatcher.notifyMessageEdited(updated.getReceiver_id(), updated.getMessage_id(), newContent, LocalDateTime.now(), receivers);
}
break;
}
@@ -2284,6 +2298,15 @@ public class ClientHandler implements Runnable {
);
boolean saved = MessageDatabase.saveReplyMessage(message);
String excerpt = MessageDatabase.getExcerpt(replyToId);
JSONObject meta = new JSONObject().put("reply_to", new JSONObject()
.put("id", replyToId.toString())
.put("excerpt", excerpt));
List<UUID> receivers = Receivers.resolveFor(receiverType, receiverId, senderId);
RealTimeEventDispatcher.sendNewMessage(message, receivers, "reply", meta);
response = saved ?
new ResponseModel("success", "Reply sent") :
new ResponseModel("error", "Failed to send reply");
@@ -2322,9 +2345,17 @@ public class ClientHandler implements Runnable {
);
boolean success = MessageDatabase.saveForwardedMessage(forwarded);
if (success) {
response = new ResponseModel("success", "Message forwarded.");
// (اختیاری) ارسال ریل تایم به اعضای چت مقصد
JSONObject meta = new JSONObject().put("forwarded_from", new JSONObject()
.put("chat_id", original.getReceiver_id().toString())
.put("message_id", original.getMessage_id().toString())
.put("sender_id", original.getSender_id().toString())
.put("sender_name", userDatabase.findByInternalUUID(original.getSender_id()).getProfile_name()));
List<UUID> receivers = Receivers.resolveFor(targetChatType, targetChatId, currentUser.getInternal_uuid());
RealTimeEventDispatcher.sendNewMessage(forwarded, receivers, "forward", meta);
} else {
response = new ResponseModel("error", "Failed to forward message.");
}
@@ -2336,9 +2367,28 @@ public class ClientHandler implements Runnable {
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.");
if (success) {
Message msg = MessageDatabase.findById(messageId);
JSONObject counts = MessageReactionDatabase.getCountsAsJson(messageId);
List<UUID> receivers = Receivers.resolveFor(msg.getReceiver_type(), msg.getReceiver_id(), null);
RealTimeEventDispatcher.notifyReactionAdded(
msg.getReceiver_id(), // chatId
messageId, // messageId
reaction, // emoji
counts.optInt(reaction, 0),
counts,
receivers
);
response = new ResponseModel("success", "Reaction saved.");
} else {
response = new ResponseModel("error", "Failed to save reaction.");
}
break;
}
@@ -81,33 +81,94 @@ public class RealTimeEventDispatcher {
public static void notifyMessageEdited(UUID messageId, String newContent, List<UUID> receivers) {
String editTime = LocalDateTime.now().toString();
public static void notifyMessageEdited(UUID chatId, UUID messageId, String newContent, LocalDateTime editedAt, List<UUID> receivers) {
JSONObject data = new JSONObject();
data.put("chat_id", chatId.toString());
data.put("message_id", messageId.toString());
data.put("new_content", newContent);
data.put("edited_at", editTime);
data.put("edited_at", editedAt.toString());
JSONObject event = new JSONObject();
event.put("action", "edit_message");
event.put("action", "message_edited");
event.put("data", data);
broadcastToUsers(receivers, event);
}
public static void notifyMessageDeleted(UUID messageId, List<UUID> receivers) {
public static void sendNewMessage(Message message, List<UUID> receivers, String kind, JSONObject meta) {
JSONObject payload = new JSONObject();
payload.put("action", "new_message");
JSONObject data = new JSONObject();
data.put("id", message.getMessage_id().toString());
data.put("chat_id", message.getReceiver_id().toString());
data.put("sender_id", message.getSender_id().toString());
data.put("receiver_id", message.getReceiver_id().toString());
data.put("receiver_type", message.getReceiver_type());
data.put("content", message.getContent());
data.put("message_type", message.getMessage_type());
data.put("send_at", message.getSend_at().toString());
User sender = userDatabase.findByInternalUUID(message.getSender_id());
if (sender != null) data.put("sender_name", sender.getProfile_name());
data.put("kind", kind == null ? "plain" : kind); // plain|reply|forward
if (meta != null) data.put("meta", meta); // reply_to {...} | forwarded_from {...}
payload.put("data", data);
for (UUID userId : receivers) sendToUser(userId, payload);
}
public static void notifyMessageDeletedGlobal(UUID chatId, UUID messageId, List<UUID> receivers) {
JSONObject data = new JSONObject();
data.put("chat_id", chatId.toString());
data.put("message_id", messageId.toString());
JSONObject event = new JSONObject();
event.put("action", "delete_message");
event.put("action", "message_deleted_global");
event.put("data", data);
broadcastToUsers(receivers, event);
}
public static void notifyReactionAdded(UUID chatId, UUID messageId, String emoji,
int totalForEmoji, JSONObject countsAll, List<UUID> receivers) {
JSONObject data = new JSONObject();
data.put("chat_id", chatId.toString());
data.put("message_id", messageId.toString());
data.put("emoji", emoji);
data.put("counts", countsAll); // {"❤️":3,"👍":1,...}
data.put("count_for_emoji", totalForEmoji);
JSONObject event = new JSONObject();
event.put("action", "message_reacted");
event.put("data", data);
broadcastToUsers(receivers, event);
}
public static void notifyReactionRemoved(UUID chatId, UUID messageId, String emoji,
int totalForEmoji, JSONObject countsAll, List<UUID> receivers) {
JSONObject data = new JSONObject();
data.put("chat_id", chatId.toString());
data.put("message_id", messageId.toString());
data.put("emoji", emoji);
data.put("counts", countsAll);
data.put("count_for_emoji", totalForEmoji);
JSONObject event = new JSONObject();
event.put("action", "message_unreacted");
event.put("data", data);
broadcastToUsers(receivers, event);
}
public static void notifyUserUpdated(UUID userId, String newProfileName, String newImageUrl, List<UUID> contactIds) {
JSONObject data = new JSONObject();
data.put("user_id", userId.toString());
@@ -0,0 +1,21 @@
package org.to.telegramfinalproject.Server;
import org.to.telegramfinalproject.Database.ChannelDatabase;
import org.to.telegramfinalproject.Database.GroupDatabase;
import org.to.telegramfinalproject.Database.PrivateChatDatabase;
import java.util.List;
import java.util.UUID;
public class Receivers {
public static List<UUID> resolveFor(String type, UUID chatId, UUID exclude) {
List<UUID> ids = switch (type) {
case "private" -> PrivateChatDatabase.getMembers(chatId);
case "group" -> GroupDatabase.getMemberUUIDs(chatId);
case "channel" -> ChannelDatabase.getSubscriberUUIDs(chatId);
default -> List.of();
};
if (exclude != null) ids.remove(exclude);
return ids;
}
}