Work on send message Real time in UI

This commit is contained in:
2025-08-30 19:43:58 +03:30
parent 60338fec52
commit edc61ccb96
7 changed files with 481 additions and 60 deletions
@@ -42,10 +42,21 @@ public class ActionHandler {
// private void handleRealTime(JSONObject json) throws IOException {
// IncomingMessageListener listener = new IncomingMessageListener(this.in);
// listener.handleRealTimeEvent (json);
// }
// ActionHandler.java
private void handleRealTime(JSONObject json) throws IOException {
IncomingMessageListener listener = new IncomingMessageListener(this.in);
listener.handleRealTimeEvent (json);
IncomingMessageListener listener = TelegramClient.getInstance().getListener();
if (listener != null) {
listener.handleRealTimeEvent(json);
} else {
System.err.println("[RT] Listener not ready; dropping RT event: " + json);
}
}
public ActionHandler(PrintWriter out, BufferedReader in, Scanner scanner) {
this.out = out;
this.in = in;
@@ -1,21 +1,36 @@
package org.to.telegramfinalproject.Client;
import javafx.application.Platform;
import org.json.JSONObject;
import org.to.telegramfinalproject.Models.ChatEntry;
import org.to.telegramfinalproject.UI.MainController;
import java.io.BufferedReader;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
public class IncomingMessageListener implements Runnable {
private final BufferedReader in;
public enum UIMode { CONSOLE, UI }
private final UIMode uiMode; // runtime mode
public IncomingMessageListener(BufferedReader in) {
private final java.util.Set<String> seenMessageIds =
java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap<>());
public IncomingMessageListener(BufferedReader in, UIMode uiMode) {
this.in = in;
this.uiMode = uiMode;
}
// public IncomingMessageListener(BufferedReader in) {
// this.in = in;
// }
@Override
public void run() {
try {
@@ -79,7 +94,7 @@ public class IncomingMessageListener implements Runnable {
"update_group_or_channel", "chat_deleted",
"blocked_by_user", "unblocked_by_user", "message_seen",
"removed_from_group", "removed_from_channel",
"became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated","created_private_chat" , "message_reacted" , "message_unreacted" -> true;
"became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated","created_private_chat" , "message_reacted" , "message_unreacted","chat_updated" -> true;
default -> false;
};
}
@@ -88,13 +103,15 @@ public class IncomingMessageListener implements Runnable {
String action = response.getString("action");
JSONObject msg = response.has("data") ? response.getJSONObject("data") : new JSONObject();
JSONObject finalMsg = msg;
JSONObject finalMsg1 = msg;
switch (action) {
case "added_to_group", "added_to_channel",
"removed_from_group", "removed_from_channel", "chat_deleted","created_private_chat" -> {
"removed_from_group", "removed_from_channel",
"chat_deleted", "created_private_chat" -> {
// این قسمت مستقل از UI/کنسول است
System.out.println("🔄 Chat list changed. Updating...");
Session.forceRefreshChatList = true;
System.out.println("🧪 Calling requestChatList() after being added");
String chatId = msg.getString("chat_id");
String chatType = msg.getString("chat_type");
@@ -106,36 +123,67 @@ public class IncomingMessageListener implements Runnable {
}
}
case "chat_updated" -> {
System.out.println("\n🔄 Chat info updated.");
// case "chat_updated" -> {
// if (uiMode == UIMode.UI) {
// bumpChatListFromUpdate(msg); // برای UI (سایدبار و سورت)
// } else {
// updateLastMessageTime(msg); // برای کنسول (لیست‌های Session)
// }
// }
if (msg.has("last_message_time")) {
updateLastMessageTime(msg);
} else {
new Thread(() -> {
try {
handleAdminRoleChanged(msg);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
case "became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated" -> {
case "became_admin", "removed_admin", "ownership_transferred", "admin_permissions_updated" -> {
System.out.println("🧩 Detected admin/owner role change. Calling handler...");
new Thread(() -> {
try {
handleAdminRoleChanged(msg); //new thread
handleAdminRoleChanged(finalMsg1);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
case "new_message" -> {
JSONObject data = response.optJSONObject("data");
if (data == null) break;
default -> displayRealTimeMessage(action, msg);
String mid = data.optString("id", data.optString("message_id",""));
if (mid.isEmpty()) break;
if (!seenMessageIds.add(mid)) break;
bumpChatListFromMessage(data);
JSONObject uiMsg = new JSONObject(data.toString());
if (!uiMsg.has("message_id") && uiMsg.has("id")) {
uiMsg.put("message_id", uiMsg.getString("id"));
}
Platform.runLater(() -> {
var mc = MainController.getInstance();
var chatCtl = (mc != null) ? mc.getChatPageController() : null;
if (chatCtl != null) {
chatCtl.onRealTimeNewMessage(uiMsg);
}
});
}
case "chat_updated" -> {
var data = response.getJSONObject("data");
bumpChatListFromUpdate(data);
}
case "message_edited", "message_deleted_global", "message_reacted", "message_unreacted",
"user_status_changed", "blocked_by_user", "unblocked_by_user", "message_seen" -> {
displayRealTimeMessage(action, msg);
}
default -> {
System.out.println("\n❓ Unknown real-time action: " + action);
System.out.println(msg.toString(2));
}
}
System.out.print(">> ");
@@ -362,4 +410,58 @@ public class IncomingMessageListener implements Runnable {
}
}
}
private static LocalDateTime parseIsoFlexible(String iso) {
if (iso == null || iso.isBlank()) return null;
try { return LocalDateTime.parse(iso); } catch (Exception ignore) {}
try { return OffsetDateTime.parse(iso).toLocalDateTime(); } catch (Exception ignore) {}
return null;
}
private void bumpChatListFromUpdate(JSONObject data) {
try {
UUID chatId = UUID.fromString(data.optString("chat_id",""));
String chatType = data.optString("chat_type","");
LocalDateTime ts = parseIsoFlexible(data.optString("last_message_time", null));
Platform.runLater(() -> {
var mc = MainController.getInstance();
if (mc != null) mc.onChatUpdated(chatId, chatType, ts, /*isIncoming*/ false, null);
});
} catch (Exception e) { System.err.println("[RT] bumpChatListFromUpdate: " + e.getMessage()); }
}
private String previewOf(String type, String content) {
String t = type == null ? "" : type.trim().toUpperCase();
return switch (t) {
case "IMAGE" -> "[Image]";
case "AUDIO" -> "[Audio]";
case "VIDEO" -> "[Video]";
case "FILE" -> "[File]";
default -> (content == null ? "" : content);
};
}
private void bumpChatListFromMessage(JSONObject m) {
try {
UUID chatId = UUID.fromString(m.optString("receiver_id",""));
String chatType = m.optString("receiver_type","");
LocalDateTime ts = parseIsoFlexible(m.optString("send_at", null));
String preview = previewOf(m.optString("message_type","TEXT"),
m.optString("content",""));
Platform.runLater(() -> {
var mc = MainController.getInstance();
if (mc != null) mc.onChatUpdated(chatId, chatType, ts, /*isIncoming*/ true, preview);
});
} catch (Exception e) { System.err.println("[RT] bumpChatListFromMessage: " + e.getMessage()); }
}
}
@@ -171,11 +171,22 @@ public class TelegramClient {
return instance;
}
// public void startConsole() {
// try {
// connectIfNeeded();
// initHandlerIfNeeded();
// startListenerOnce();
// showMainMenu();
// } catch (IOException e) {
// System.err.println("❌ Error connecting to server: " + e.getMessage());
// }
// }
public void startConsole() {
try {
connectIfNeeded();
initHandlerIfNeeded();
startListenerOnce();
startListenerOnce(IncomingMessageListener.UIMode.CONSOLE); // ← کنسول
showMainMenu();
} catch (IOException e) {
System.err.println("❌ Error connecting to server: " + e.getMessage());
@@ -185,14 +196,23 @@ public class TelegramClient {
public void start() { startConsole(); }
//UI only
// public static synchronized TelegramClient getOrInitForUI() throws IOException {
// TelegramClient cli = getInstance();
// cli.connectIfNeeded();
// cli.initHandlerIfNeeded();
// cli.startListenerOnce();
// return cli;
// }
public static synchronized TelegramClient getOrInitForUI() throws IOException {
TelegramClient cli = getInstance();
cli.connectIfNeeded();
cli.initHandlerIfNeeded();
cli.startListenerOnce();
cli.startListenerOnce(IncomingMessageListener.UIMode.UI); // ← UI
return cli;
}
private synchronized void connectIfNeeded() throws IOException {
if (socket != null && socket.isConnected() && !socket.isClosed()) return;
@@ -208,10 +228,23 @@ public class TelegramClient {
}
}
private void startListenerOnce() {
// private void startListenerOnce() {
// if (listenerStarted) return;
// listenerStarted = true;
// Thread listenerThread = new Thread(new IncomingMessageListener(in), "socket-listener");
// listenerThread.setDaemon(true);
// listenerThread.start();
// }
private void startListenerOnce(IncomingMessageListener.UIMode mode) {
if (listenerStarted) return;
listenerStarted = true;
Thread listenerThread = new Thread(new IncomingMessageListener(in), "socket-listener");
Thread listenerThread = new Thread(
new IncomingMessageListener(in, mode),
"socket-listener"
);
listenerThread.setDaemon(true);
listenerThread.start();
}
@@ -269,5 +302,13 @@ public class TelegramClient {
public static void main(String[] args) {
new TelegramClient().startConsole();
}
// TelegramClient.java
private IncomingMessageListener listener;
public IncomingMessageListener getListener() {
return listener;
}
}