Fix bugs in add contact to chat
This commit is contained in:
@@ -7,6 +7,7 @@ import javafx.scene.image.ImageView;
|
|||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||||
|
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||||
|
|
||||||
import java.util.Objects;
|
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);
|
return new ArrayList<>(Session.contactEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
// در غیر این صورت از سرور میگیریم (اختیاری)
|
|
||||||
// اگر API «view_contacts» داری، اینجا بفرست:
|
|
||||||
JSONObject req = new JSONObject()
|
JSONObject req = new JSONObject()
|
||||||
.put("action", "view_contacts") // 🔧 اگر نام اکشنات فرق دارد، تغییر بده
|
.put("action", "view_contacts")
|
||||||
.put("user_id", Session.getUserUUID());
|
.put("user_id", Session.getUserUUID());
|
||||||
|
|
||||||
JSONObject res = ActionHandler.sendWithResponse(req);
|
JSONObject res = ActionHandler.sendWithResponse(req);
|
||||||
@@ -122,7 +121,6 @@ public class ContactsController {
|
|||||||
|
|
||||||
fetched.add(new ContactEntry(contactId, userId, contactDisplay, profileName, imageUrl, isBlocked, lastSeen));
|
fetched.add(new ContactEntry(contactId, userId, contactDisplay, profileName, imageUrl, isBlocked, lastSeen));
|
||||||
}
|
}
|
||||||
// اگر میخواهی تو سشن هم نگه داری:
|
|
||||||
if (Session.contactEntries == null) Session.contactEntries = new ArrayList<>();
|
if (Session.contactEntries == null) Session.contactEntries = new ArrayList<>();
|
||||||
Session.contactEntries.clear();
|
Session.contactEntries.clear();
|
||||||
Session.contactEntries.addAll(fetched);
|
Session.contactEntries.addAll(fetched);
|
||||||
@@ -161,7 +159,6 @@ public class ContactsController {
|
|||||||
item.getStyleClass().add("contact-item");
|
item.getStyleClass().add("contact-item");
|
||||||
item.setCursor(Cursor.HAND);
|
item.setCursor(Cursor.HAND);
|
||||||
|
|
||||||
// آواتار
|
|
||||||
ImageView avatar = new ImageView(loadAvatarSafe(c.imageUrl));
|
ImageView avatar = new ImageView(loadAvatarSafe(c.imageUrl));
|
||||||
avatar.setFitWidth(58);
|
avatar.setFitWidth(58);
|
||||||
avatar.setFitHeight(58);
|
avatar.setFitHeight(58);
|
||||||
@@ -183,13 +180,24 @@ public class ContactsController {
|
|||||||
|
|
||||||
private Image loadAvatarSafe(String urlOrResource) {
|
private Image loadAvatarSafe(String urlOrResource) {
|
||||||
try {
|
try {
|
||||||
if (urlOrResource != null && urlOrResource.startsWith("/")) {
|
if (urlOrResource != null) {
|
||||||
return new Image(Objects.requireNonNull(getClass().getResourceAsStream(urlOrResource)));
|
// حالت 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(
|
return new Image(Objects.requireNonNull(
|
||||||
getClass().getResourceAsStream("/org/to/telegramfinalproject/Avatars/default_user_profile.png")));
|
getClass().getResourceAsStream("/org/to/telegramfinalproject/Avatars/default_user_profile.png")));
|
||||||
} catch (Exception ignore) {
|
} catch (Exception e) {
|
||||||
|
// هر مشکلی → پیشفرض
|
||||||
return new Image(Objects.requireNonNull(
|
return new Image(Objects.requireNonNull(
|
||||||
getClass().getResourceAsStream("/org/to/telegramfinalproject/Avatars/default_user_profile.png")));
|
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.layout.*;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import org.json.JSONObject;
|
||||||
import org.to.telegramfinalproject.Client.Session;
|
import org.to.telegramfinalproject.Client.Session;
|
||||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||||
import org.to.telegramfinalproject.Client.ActionHandler;
|
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||||
@@ -96,8 +97,12 @@ public class MainController {
|
|||||||
// Keep track of the scene user comes from
|
// Keep track of the scene user comes from
|
||||||
private final Deque<Node> navigationStack = new ArrayDeque<>();
|
private final Deque<Node> navigationStack = new ArrayDeque<>();
|
||||||
private final Map<UUID, ChatItemController> itemControllers = new HashMap<>();
|
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
|
//For realtime handling
|
||||||
public void onChatUpdated(UUID chatId, String chatType, LocalDateTime lastTs,
|
public void onChatUpdated(UUID chatId, String chatType, LocalDateTime lastTs,
|
||||||
boolean isIncoming, String lastPreview) {
|
boolean isIncoming, String lastPreview) {
|
||||||
@@ -351,10 +356,8 @@ public class MainController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// اگر “Archived Chats” یا هدر دیگری داری، قبلش اضافه کن (اختیاری)
|
// addArchivedHeaderIfYouHaveOne();
|
||||||
// addArchivedHeaderIfYouHaveOne();
|
|
||||||
|
|
||||||
// 1) همیشه Saved اول بیاد (اگر وجود داشت)
|
|
||||||
if (saved != null) {
|
if (saved != null) {
|
||||||
addChatNode(saved);
|
addChatNode(saved);
|
||||||
}
|
}
|
||||||
@@ -408,33 +411,98 @@ public class MainController {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
private void addChatNode(ChatEntry chat) {
|
private void addChatNode(ChatEntry chat) {
|
||||||
try {
|
try {
|
||||||
FXMLLoader fx = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/chat_item.fxml"));
|
FXMLLoader fx = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/chat_item.fxml"));
|
||||||
Node item = fx.load();
|
Node item = fx.load();
|
||||||
ChatItemController cc = fx.getController();
|
ChatItemController cc = fx.getController();
|
||||||
|
|
||||||
|
// --- saved detection
|
||||||
|
boolean saved = isSaved(chat);
|
||||||
|
|
||||||
|
String title = safeTitle(chat);
|
||||||
String preview = chat.getLastMessagePreview() == null ? "" : chat.getLastMessagePreview();
|
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
|
// --- force Saved Messages title & avatar
|
||||||
? ""
|
if (saved) {
|
||||||
: formatChatTime(chat.getLastMessageTime());
|
title = SAVED_TITLE;
|
||||||
|
imageUrl = SAVED_AVATAR;
|
||||||
|
}
|
||||||
|
|
||||||
// If chat has a profile picture, pass it; otherwise null
|
cc.setChatData(title, preview, timeText, chat.getUnreadCount(), imageUrl, chat.getType());
|
||||||
String imageUrl = (chat.getImageUrl() != null && !chat.getImageUrl().isEmpty())
|
|
||||||
? chat.getImageUrl()
|
|
||||||
: null;
|
|
||||||
|
|
||||||
cc.setChatData(chat.getName(), preview, timeText, chat.getUnreadCount(), imageUrl, chat.getType());
|
|
||||||
item.setOnMouseClicked(e -> openChat(chat));
|
item.setOnMouseClicked(e -> openChat(chat));
|
||||||
chatListContainer.getChildren().add(item);
|
chatListContainer.getChildren().add(item);
|
||||||
|
|
||||||
itemControllers.put(chat.getId(), cc);
|
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) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
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) {
|
private String mapTypeToLabel(String t) {
|
||||||
switch (t.toUpperCase()) {
|
switch (t.toUpperCase()) {
|
||||||
@@ -1201,13 +1269,11 @@ public class MainController {
|
|||||||
if (Session.chatList == null) Session.chatList = new ArrayList<>();
|
if (Session.chatList == null) Session.chatList = new ArrayList<>();
|
||||||
Session.chatList.add(ce);
|
Session.chatList.add(ce);
|
||||||
}
|
}
|
||||||
// اگر activeChats استفاده میکنی:
|
|
||||||
if (Session.activeChats != null && Session.activeChats.stream().noneMatch(c -> id.equals(c.getId()))) {
|
if (Session.activeChats != null && Session.activeChats.stream().noneMatch(c -> id.equals(c.getId()))) {
|
||||||
Session.activeChats.add(ce);
|
Session.activeChats.add(ce);
|
||||||
}
|
}
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
// (اختیاری) مرتبسازی بر اساس زمان آخرین پیام
|
|
||||||
Comparator<ChatEntry> byTimeDesc = (a,b) -> {
|
Comparator<ChatEntry> byTimeDesc = (a,b) -> {
|
||||||
LocalDateTime t1 = a.getLastMessageTime(), t2 = b.getLastMessageTime();
|
LocalDateTime t1 = a.getLastMessageTime(), t2 = b.getLastMessageTime();
|
||||||
if (t1 == null && t2 == null) return 0;
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user