Fix bugs in add contact to chat

This commit is contained in:
2025-09-08 00:50:48 +03:30
parent 1ce577e39e
commit 6fc2b2e2ed
3 changed files with 122 additions and 25 deletions
@@ -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";
}
}
@@ -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 وب هم داری، می‌تونی مستقیم Image(url) بسازی
// حالت URL وب یا مسیر فایل
if (urlOrResource.startsWith("http://") ||
urlOrResource.startsWith("https://") ||
urlOrResource.startsWith("file:")) {
return new Image(urlOrResource, true); // true = لود async
}
}
// 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")));
}
@@ -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<Node> navigationStack = new ArrayDeque<>();
private final Map<UUID, ChatItemController> itemControllers = new HashMap<>();
private final java.util.Set<UUID> 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();
// 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();
// --- saved detection
boolean saved = isSaved(chat);
String title = safeTitle(chat);
String preview = chat.getLastMessagePreview() == null ? "" : chat.getLastMessagePreview();
String timeText = chat.getLastMessageTime() == null ? "" : formatChatTime(chat.getLastMessageTime());
String imageUrl = safeImage(chat.getImageUrl());
String timeText = chat.getLastMessageTime() == null
? ""
: formatChatTime(chat.getLastMessageTime());
// --- force Saved Messages title & avatar
if (saved) {
title = SAVED_TITLE;
imageUrl = SAVED_AVATAR;
}
// If chat has a profile picture, pass it; otherwise null
String imageUrl = (chat.getImageUrl() != null && !chat.getImageUrl().isEmpty())
? chat.getImageUrl()
: null;
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<ChatEntry> 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";
}
}