From 6fc2b2e2ed4af228e553a1c9e21afb1cc2bf8e80 Mon Sep 17 00:00:00 2001 From: Partow Roshani Date: Mon, 8 Sep 2025 00:50:48 +0330 Subject: [PATCH] Fix bugs in add contact to chat --- .../UI/ChatItemController.java | 7 ++ .../UI/ContactsController.java | 28 +++-- .../UI/MainController.java | 112 +++++++++++++++--- 3 files changed, 122 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java b/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java index fc734c2..273798a 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java @@ -7,6 +7,7 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.StackPane; import javafx.scene.shape.Circle; import org.to.telegramfinalproject.Client.AvatarLocalResolver; +import org.to.telegramfinalproject.Models.ChatEntry; import java.util.Objects; @@ -141,4 +142,10 @@ public class ChatItemController { } } + private String safeTitle(ChatEntry e) { + if (e.getName() != null && !e.getName().isBlank()) return e.getName(); + if (e.getDisplayId() != null && !e.getDisplayId().isBlank()) return e.getDisplayId(); + return "Unknown"; + } + } diff --git a/src/main/java/org/to/telegramfinalproject/UI/ContactsController.java b/src/main/java/org/to/telegramfinalproject/UI/ContactsController.java index 21b017f..316db81 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ContactsController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ContactsController.java @@ -90,10 +90,9 @@ public class ContactsController { return new ArrayList<>(Session.contactEntries); } - // در غیر این صورت از سرور می‌گیریم (اختیاری) - // اگر API «view_contacts» داری، اینجا بفرست: + JSONObject req = new JSONObject() - .put("action", "view_contacts") // 🔧 اگر نام اکشن‌ات فرق دارد، تغییر بده + .put("action", "view_contacts") .put("user_id", Session.getUserUUID()); JSONObject res = ActionHandler.sendWithResponse(req); @@ -122,7 +121,6 @@ public class ContactsController { fetched.add(new ContactEntry(contactId, userId, contactDisplay, profileName, imageUrl, isBlocked, lastSeen)); } - // اگر می‌خواهی تو سشن هم نگه داری: if (Session.contactEntries == null) Session.contactEntries = new ArrayList<>(); Session.contactEntries.clear(); Session.contactEntries.addAll(fetched); @@ -161,7 +159,6 @@ public class ContactsController { item.getStyleClass().add("contact-item"); item.setCursor(Cursor.HAND); - // آواتار ImageView avatar = new ImageView(loadAvatarSafe(c.imageUrl)); avatar.setFitWidth(58); avatar.setFitHeight(58); @@ -183,13 +180,24 @@ public class ContactsController { private Image loadAvatarSafe(String urlOrResource) { try { - if (urlOrResource != null && urlOrResource.startsWith("/")) { - return new Image(Objects.requireNonNull(getClass().getResourceAsStream(urlOrResource))); + if (urlOrResource != null) { + // حالت Resource داخلی + if (urlOrResource.startsWith("/")) { + return new Image(Objects.requireNonNull( + getClass().getResourceAsStream(urlOrResource))); + } + // حالت URL وب یا مسیر فایل + if (urlOrResource.startsWith("http://") || + urlOrResource.startsWith("https://") || + urlOrResource.startsWith("file:")) { + return new Image(urlOrResource, true); // true = لود async + } } - // اگر URL وب هم داری، می‌تونی مستقیم Image(url) بسازی + // fallback به پیش‌فرض return new Image(Objects.requireNonNull( getClass().getResourceAsStream("/org/to/telegramfinalproject/Avatars/default_user_profile.png"))); - } catch (Exception ignore) { + } catch (Exception e) { + // هر مشکلی → پیش‌فرض return new Image(Objects.requireNonNull( getClass().getResourceAsStream("/org/to/telegramfinalproject/Avatars/default_user_profile.png"))); } @@ -305,4 +313,4 @@ public class ContactsController { } -} +} \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/UI/MainController.java b/src/main/java/org/to/telegramfinalproject/UI/MainController.java index 5189b2c..c970969 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/MainController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/MainController.java @@ -13,6 +13,7 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.*; import javafx.scene.shape.Circle; import javafx.util.Duration; +import org.json.JSONObject; import org.to.telegramfinalproject.Client.Session; import org.to.telegramfinalproject.Models.ChatEntry; import org.to.telegramfinalproject.Client.ActionHandler; @@ -96,8 +97,12 @@ public class MainController { // Keep track of the scene user comes from private final Deque navigationStack = new ArrayDeque<>(); private final Map itemControllers = new HashMap<>(); + private final java.util.Set enrichInFlight = java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap<>()); + private static final String SAVED_TITLE = "Saved Messages"; + private static final String SAVED_AVATAR = "/org/to/telegramfinalproject/Avatars/saved_messages.png"; + //For realtime handling public void onChatUpdated(UUID chatId, String chatType, LocalDateTime lastTs, boolean isIncoming, String lastPreview) { @@ -351,10 +356,8 @@ public class MainController { } } - // اگر “Archived Chats” یا هدر دیگری داری، قبلش اضافه کن (اختیاری) - // addArchivedHeaderIfYouHaveOne(); +// addArchivedHeaderIfYouHaveOne(); - // 1) همیشه Saved اول بیاد (اگر وجود داشت) if (saved != null) { addChatNode(saved); } @@ -408,33 +411,98 @@ public class MainController { // } // } - private void addChatNode(ChatEntry chat) { try { FXMLLoader fx = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/chat_item.fxml")); Node item = fx.load(); ChatItemController cc = fx.getController(); - String preview = chat.getLastMessagePreview() == null ? "" : chat.getLastMessagePreview(); + // --- saved detection + boolean saved = isSaved(chat); - String timeText = chat.getLastMessageTime() == null - ? "" - : formatChatTime(chat.getLastMessageTime()); + String title = safeTitle(chat); + String preview = chat.getLastMessagePreview() == null ? "" : chat.getLastMessagePreview(); + String timeText = chat.getLastMessageTime() == null ? "" : formatChatTime(chat.getLastMessageTime()); + String imageUrl = safeImage(chat.getImageUrl()); - // If chat has a profile picture, pass it; otherwise null - String imageUrl = (chat.getImageUrl() != null && !chat.getImageUrl().isEmpty()) - ? chat.getImageUrl() - : null; + // --- force Saved Messages title & avatar + if (saved) { + title = SAVED_TITLE; + imageUrl = SAVED_AVATAR; + } - cc.setChatData(chat.getName(), preview, timeText, chat.getUnreadCount(), imageUrl, chat.getType()); + cc.setChatData(title, preview, timeText, chat.getUnreadCount(), imageUrl, chat.getType()); item.setOnMouseClicked(e -> openChat(chat)); chatListContainer.getChildren().add(item); itemControllers.put(chat.getId(), cc); + + // --- no enrich for Saved + boolean needName = (chat.getName() == null || chat.getName().isBlank()); + boolean needImage = (chat.getImageUrl() == null || chat.getImageUrl().isBlank()); + if (!saved && (needName || needImage)) { + enrichChatEntryAsync(chat); + } } catch (Exception ex) { ex.printStackTrace(); } } + private boolean isSaved(ChatEntry e) { + if (e == null) return false; + if (e.isSavedMessages()) return true; + return "saved".equalsIgnoreCase(e.getType()); // اگر type اختصاصی داری + } + + + public void updateSingleChatCell(ChatEntry entry) { + ChatItemController cc = itemControllers.get(entry.getId()); + if (cc != null) { + Platform.runLater(() -> cc.setChatData( + safeTitle(entry), + entry.getLastMessagePreview() == null ? "" : entry.getLastMessagePreview(), + entry.getLastMessageTime() == null ? "" : formatChatTime(entry.getLastMessageTime()), + entry.getUnreadCount(), + safeImage(entry.getImageUrl()), + entry.getType() + )); + } else { + refreshChatListUI(); // اگر پیدا نشد، کل لیست را رفرش کن + } + } + + public void enrichChatEntryAsync(ChatEntry entry) { + if (entry == null) return; + // اگر در حال دریافت هستیم، تکراری نفرست + if (!enrichInFlight.add(entry.getId())) return; + + new Thread(() -> { + try { + JSONObject req = new JSONObject() + .put("action", "get_header_info") + .put("receiver_id", entry.getId().toString()) + .put("receiver_type", entry.getType()) + .put("viewer_id", Session.getUserUUID()); + + JSONObject res = ActionHandler.sendWithResponse(req); + if (res != null && "success".equalsIgnoreCase(res.optString("status"))) { + JSONObject d = res.optJSONObject("data"); + if (d != null) { + String name = d.optString("name", ""); + String image = d.optString("image_url", ""); + Platform.runLater(() -> { + if (!name.isBlank()) entry.setName(name); + if (!image.isBlank()) entry.setImageUrl(image); + updateSingleChatCell(entry); // فقط همان آیتم را نوسازی کن + }); + } + } + } catch (Exception ignored) { + } finally { + enrichInFlight.remove(entry.getId()); + } + }).start(); + } + private String mapTypeToLabel(String t) { switch (t.toUpperCase()) { @@ -1201,13 +1269,11 @@ public class MainController { if (Session.chatList == null) Session.chatList = new ArrayList<>(); Session.chatList.add(ce); } - // اگر activeChats استفاده می‌کنی: if (Session.activeChats != null && Session.activeChats.stream().noneMatch(c -> id.equals(c.getId()))) { Session.activeChats.add(ce); } } catch (Exception ignore) {} - // (اختیاری) مرتب‌سازی بر اساس زمان آخرین پیام Comparator byTimeDesc = (a,b) -> { LocalDateTime t1 = a.getLastMessageTime(), t2 = b.getLastMessageTime(); if (t1 == null && t2 == null) return 0; @@ -1340,5 +1406,21 @@ public class MainController { } + // title fallback: اول name بعد displayId، در نهایت پیش‌فرض + private String safeTitle(ChatEntry e) { + if (e.getName() != null && !e.getName().isBlank()) return e.getName(); + if (e.getDisplayId() != null && !e.getDisplayId().isBlank()) return e.getDisplayId(); + if ("saved".equalsIgnoreCase(e.getType()) || e.isSavedMessages()) return "Saved Messages"; + return "Unknown"; + } + + // image fallback: اگر خالی بود، آواتار پیش‌فرض + private String safeImage(String img) { + return (img != null && !img.isBlank()) + ? img + : "/org/to/telegramfinalproject/Avatars/default_user_profile.png"; + } + + } \ No newline at end of file