Merge remote-tracking branch 'origin/Main-UI' into Main-UI
# Conflicts: # src/main/java/org/to/telegramfinalproject/UI/UserInfoController.java
This commit is contained in:
@@ -14,7 +14,7 @@ public final class AvatarFX {
|
||||
|
||||
Circle c = new Circle();
|
||||
c.radiusProperty().bind(Bindings.min(iv.fitWidthProperty(), iv.fitHeightProperty()).divide(2));
|
||||
c.centerXProperty().bind(iv.fitWidthProperty().divide(2));
|
||||
c.centerXProperty().bind(iv.fitWidthProperty().divide(3));
|
||||
c.centerYProperty().bind(iv.fitHeightProperty().divide(2));
|
||||
iv.setClip(c);
|
||||
}
|
||||
|
||||
@@ -842,7 +842,7 @@ public class ChatPageController {
|
||||
} else {
|
||||
setDefaultHeaderAvatarByType(entry.getType());
|
||||
}
|
||||
AvatarFX.circleClip(userAvatar, 36);
|
||||
AvatarFX.circleClip(userAvatar, 40);
|
||||
|
||||
|
||||
// حالت اولیه (بدون انتظار هدر)
|
||||
@@ -863,11 +863,13 @@ public class ChatPageController {
|
||||
|
||||
// === (3-dot menu + header click) ===
|
||||
configureHeaderActions(entry);
|
||||
|
||||
requestBlockStatusByChat(entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void requestBlockStatusByChat(ChatEntry entry) {
|
||||
public void requestBlockStatusByChat(ChatEntry entry) {
|
||||
if (entry == null || !"private".equalsIgnoreCase(entry.getType())) return;
|
||||
|
||||
String viewerId = Session.getUserUUID(); // internal_uuid کاربر فعلی
|
||||
@@ -890,7 +892,7 @@ public class ChatPageController {
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void applyBlockUi(boolean blockedByMe, boolean blockedMe) {
|
||||
public void applyBlockUi(boolean blockedByMe, boolean blockedMe) {
|
||||
this.blockedByMeFlag = blockedByMe;
|
||||
this.blockedMeFlag = blockedMe;
|
||||
|
||||
@@ -945,6 +947,8 @@ public class ChatPageController {
|
||||
|
||||
// Wire up menu + header click
|
||||
configureHeaderActions(entry);
|
||||
|
||||
requestBlockStatusByChat(entry);
|
||||
}
|
||||
|
||||
private void configureHeaderActions(ChatEntry entry) {
|
||||
@@ -983,6 +987,7 @@ public class ChatPageController {
|
||||
|
||||
private void openInfoScene(ChatEntry entry) {
|
||||
JSONObject req = new JSONObject();
|
||||
String targetId = null; // 👈 capture it here for the private case
|
||||
|
||||
switch (entry.getType().toLowerCase()) {
|
||||
case "private" -> {
|
||||
@@ -1003,7 +1008,7 @@ public class ChatPageController {
|
||||
return;
|
||||
}
|
||||
|
||||
String targetId = targetResp.optJSONObject("data").optString("target_id", null);
|
||||
targetId = targetResp.optJSONObject("data").optString("target_id", null);
|
||||
if (targetId == null || targetId.isBlank()) {
|
||||
Platform.runLater(() ->
|
||||
MainController.getInstance().showAlert(
|
||||
@@ -1038,6 +1043,8 @@ public class ChatPageController {
|
||||
}
|
||||
}
|
||||
|
||||
final String finalTargetId = targetId; // 👈 capture for use in the FX thread
|
||||
|
||||
new Thread(() -> {
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
@@ -1074,7 +1081,8 @@ public class ChatPageController {
|
||||
"/org/to/telegramfinalproject/Fxml/user_info.fxml"));
|
||||
overlay = loader.load();
|
||||
UserInfoController c = loader.getController();
|
||||
c.setProfileDataFromJson(entry, data);
|
||||
// 👇 pass targetId so UserInfoController gets the real UUID
|
||||
c.setProfileDataFromJson(entry, data, finalTargetId);
|
||||
}
|
||||
case "group" -> {
|
||||
loader = new FXMLLoader(getClass().getResource(
|
||||
|
||||
@@ -44,6 +44,7 @@ public class UserInfoController {
|
||||
|
||||
|
||||
private String otherUserId; // internal_uuid of the other user
|
||||
private ChatEntry entry;
|
||||
|
||||
private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/";
|
||||
|
||||
@@ -77,8 +78,8 @@ public class UserInfoController {
|
||||
});
|
||||
|
||||
deleteChatItem.setOnAction(e -> onDeleteChatClicked());
|
||||
blockItem.setOnAction(e -> handleBlock());
|
||||
unblockItem.setOnAction(e -> handleUnblock());
|
||||
blockItem.setOnAction(e -> handleToggleBlock(entry));
|
||||
unblockItem.setOnAction(e -> handleToggleBlock(entry));
|
||||
|
||||
// Register scene for ThemeManager → stylesheet swap will handle colors/icons
|
||||
Platform.runLater(() -> {
|
||||
@@ -97,7 +98,11 @@ public class UserInfoController {
|
||||
}
|
||||
|
||||
/** Backend JSON → UI */
|
||||
public void setProfileDataFromJson(ChatEntry entry, JSONObject data) {
|
||||
public void setProfileDataFromJson(ChatEntry entry, JSONObject data, String targetUuid) {
|
||||
// save the correct UUID
|
||||
this.otherUserId = targetUuid;
|
||||
this.entry = entry;
|
||||
|
||||
// --- Profile name ---
|
||||
String name = data.optString("profile_name", entry.getName());
|
||||
profileName.setText(name);
|
||||
@@ -145,9 +150,6 @@ public class UserInfoController {
|
||||
);
|
||||
}
|
||||
|
||||
// --- Other user ID (needed for block/delete) ---
|
||||
this.otherUserId = data.optString("other_user_id", entry.getId().toString());
|
||||
|
||||
// --- Block/Unblock ---
|
||||
boolean blocked = data.optBoolean("blocked", false)
|
||||
|| data.optBoolean("is_blocked", false)
|
||||
@@ -167,128 +169,55 @@ public class UserInfoController {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleToggleBlock(ChatEntry entry) {
|
||||
if (otherUserId == null) return;
|
||||
|
||||
// 1) Send toggle request
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "toggle_block")
|
||||
.put("user_id", Session.getUserUUID())
|
||||
.put("target_id", otherUserId);
|
||||
|
||||
@FXML
|
||||
private void onDeleteChatClicked() {
|
||||
// if (!"private".equalsIgnoreCase(Session.currentChatType)) {
|
||||
// new Alert(Alert.AlertType.INFORMATION, "This action is available only for private chats.").showAndWait();
|
||||
// return;
|
||||
// }
|
||||
|
||||
UUID chatId = Session.currentChatEntry != null
|
||||
? Session.currentChatEntry.getId()
|
||||
: (Session.currentChatId != null ? UUID.fromString(Session.currentChatId) : null);
|
||||
|
||||
if (chatId == null) {
|
||||
new Alert(Alert.AlertType.ERROR, "Invalid chat id.").showAndWait();
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
if (resp == null) {
|
||||
MainController.getInstance().showAlert("Error", "No response from server.", Alert.AlertType.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ButtonType oneSide = new ButtonType("Delete one-sided", ButtonBar.ButtonData.LEFT);
|
||||
ButtonType bothSide = new ButtonType("Delete both-sided", ButtonBar.ButtonData.OK_DONE);
|
||||
ButtonType cancel = ButtonType.CANCEL;
|
||||
|
||||
Alert dlg = new Alert(
|
||||
Alert.AlertType.CONFIRMATION,
|
||||
"Choose how you want to delete this private chat:",
|
||||
oneSide, bothSide, cancel
|
||||
);
|
||||
dlg.setHeaderText("Delete Private Chat");
|
||||
|
||||
Optional<ButtonType> res = dlg.showAndWait();
|
||||
if (res.isEmpty() || res.get() == cancel) return;
|
||||
|
||||
boolean both = (res.get() == bothSide);
|
||||
performDeletePrivateChat(chatId, both);
|
||||
}
|
||||
|
||||
|
||||
private void performDeletePrivateChat(UUID chatId, boolean both) {
|
||||
// نالسیف: هر کدوم هست disable کن
|
||||
if (deleteChatButton != null) deleteChatButton.setDisable(true);
|
||||
if (deleteChatItem != null) deleteChatItem.setDisable(true);
|
||||
if (infoMoreMenu != null && infoMoreMenu.isShowing()) infoMoreMenu.hide();
|
||||
if (infoMoreButton != null) infoMoreButton.setDisable(true);
|
||||
|
||||
new Thread(() -> {
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "delete_private_chat")
|
||||
.put("chat_id", chatId.toString())
|
||||
.put("both", both);
|
||||
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
// re-enable نالسیف
|
||||
if (deleteChatButton != null) deleteChatButton.setDisable(false);
|
||||
if (deleteChatItem != null) deleteChatItem.setDisable(false);
|
||||
if (infoMoreButton != null) infoMoreButton.setDisable(false);
|
||||
|
||||
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
removeChatFromSessionAndGoBack(chatId);
|
||||
MainController.getInstance().closeOverlay(profileCard.getParent());
|
||||
|
||||
// new Alert(Alert.AlertType.INFORMATION,
|
||||
// "Chat deleted " + (both ? "for both sides." : "only for you.")
|
||||
// ).showAndWait();
|
||||
} else {
|
||||
String msg = (resp != null ? resp.optString("message", "Unknown error")
|
||||
: "No response from server.");
|
||||
new Alert(Alert.AlertType.ERROR, "Failed to delete chat: " + msg).showAndWait();
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
private void removeChatFromSessionAndGoBack(UUID chatId) {
|
||||
// if (Session.chatList != null) Session.chatList.removeIf(e -> chatId.equals(e.getId()));
|
||||
// if (Session.activeChats != null) Session.activeChats.removeIf(e -> chatId.equals(e.getId()));
|
||||
// if (Session.archivedChats != null) Session.archivedChats.removeIf(e -> chatId.equals(e.getId()));
|
||||
|
||||
if (Session.currentChatId != null && Session.currentChatId.equals(chatId.toString())) {
|
||||
Session.currentChatId = null;
|
||||
Session.currentChatType = null;
|
||||
Session.currentChatEntry = null;
|
||||
Session.inChatMenu = false;
|
||||
|
||||
MainController.getInstance().closeOverlay(profileCard.getParent());
|
||||
|
||||
AppRouter.showMain();
|
||||
if (!"success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
MainController.getInstance().showAlert("Error",
|
||||
resp.optString("message", "Failed to toggle block"),
|
||||
Alert.AlertType.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
MainController.getInstance().refreshChatListUI();
|
||||
} catch (Exception ignore) {
|
||||
AppRouter.showMain();
|
||||
// 2) Immediately re-check block state from server
|
||||
JSONObject checkReq = new JSONObject()
|
||||
.put("action", "check_block_status_by_chat")
|
||||
.put("viewer_id", Session.getUserUUID())
|
||||
.put("chat_id", entry.getId().toString());
|
||||
|
||||
JSONObject checkResp = ActionHandler.sendWithResponse(checkReq);
|
||||
if (checkResp == null || !"success".equalsIgnoreCase(checkResp.optString("status"))) {
|
||||
System.err.println("⚠️ Failed to refresh block status after toggle.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject data = checkResp.optJSONObject("data");
|
||||
boolean blockedByMe = data != null && data.optBoolean("blocked_by_me", false);
|
||||
boolean blockedMe = data != null && data.optBoolean("blocked_me", false);
|
||||
|
||||
private void handleBlock() {
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "block_user")
|
||||
.put("target_id", otherUserId)
|
||||
.put("viewer_id", Session.getUserUUID());
|
||||
// 3) Update UI on FX thread
|
||||
Platform.runLater(() -> {
|
||||
// update overlay (block/unblock menu)
|
||||
updateBlockMenu(blockedByMe);
|
||||
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
updateBlockMenu(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnblock() {
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "unblock_user")
|
||||
.put("target_id", otherUserId)
|
||||
.put("viewer_id", Session.getUserUUID());
|
||||
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
updateBlockMenu(false);
|
||||
}
|
||||
// update chat page input bar
|
||||
ChatPageController c = ChatPageController.getInstance();
|
||||
if (c != null) {
|
||||
c.applyBlockUi(blockedByMe, blockedMe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateBlockMenu(boolean isBlocked) {
|
||||
@@ -314,4 +243,101 @@ public class UserInfoController {
|
||||
}
|
||||
return new Image(res.toExternalForm());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onDeleteChatClicked() {
|
||||
// if (!"private".equalsIgnoreCase(Session.currentChatType)) {
|
||||
// new Alert(Alert.AlertType.INFORMATION, "This action is available only for private chats.").showAndWait();
|
||||
// return;
|
||||
// }
|
||||
|
||||
UUID chatId = Session.currentChatEntry != null
|
||||
? Session.currentChatEntry.getId()
|
||||
: (Session.currentChatId != null ? UUID.fromString(Session.currentChatId) : null);
|
||||
|
||||
if (chatId == null) {
|
||||
new Alert(Alert.AlertType.ERROR, "Invalid chat id.").showAndWait();
|
||||
return;
|
||||
}
|
||||
|
||||
ButtonType oneSide = new ButtonType("Delete one-sided", ButtonBar.ButtonData.LEFT);
|
||||
ButtonType bothSide = new ButtonType("Delete both-sided", ButtonBar.ButtonData.OK_DONE);
|
||||
ButtonType cancel = ButtonType.CANCEL;
|
||||
|
||||
Alert dlg = new Alert(
|
||||
Alert.AlertType.CONFIRMATION,
|
||||
"Choose how you want to delete this private chat:",
|
||||
oneSide, bothSide, cancel
|
||||
);
|
||||
dlg.setHeaderText("Delete Private Chat");
|
||||
|
||||
Optional<ButtonType> res = dlg.showAndWait();
|
||||
if (res.isEmpty() || res.get() == cancel) return;
|
||||
|
||||
boolean both = (res.get() == bothSide);
|
||||
performDeletePrivateChat(chatId, both);
|
||||
}
|
||||
|
||||
|
||||
private void performDeletePrivateChat(UUID chatId, boolean both) {
|
||||
// نالسیف: هر کدوم هست disable کن
|
||||
if (deleteChatButton != null) deleteChatButton.setDisable(true);
|
||||
if (deleteChatItem != null) deleteChatItem.setDisable(true);
|
||||
if (infoMoreMenu != null && infoMoreMenu.isShowing()) infoMoreMenu.hide();
|
||||
if (infoMoreButton != null) infoMoreButton.setDisable(true);
|
||||
|
||||
new Thread(() -> {
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "delete_private_chat")
|
||||
.put("chat_id", chatId.toString())
|
||||
.put("both", both);
|
||||
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
// re-enable نالسیف
|
||||
if (deleteChatButton != null) deleteChatButton.setDisable(false);
|
||||
if (deleteChatItem != null) deleteChatItem.setDisable(false);
|
||||
if (infoMoreButton != null) infoMoreButton.setDisable(false);
|
||||
|
||||
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
removeChatFromSessionAndGoBack(chatId);
|
||||
MainController.getInstance().closeOverlay(profileCard.getParent());
|
||||
|
||||
// new Alert(Alert.AlertType.INFORMATION,
|
||||
// "Chat deleted " + (both ? "for both sides." : "only for you.")
|
||||
// ).showAndWait();
|
||||
} else {
|
||||
String msg = (resp != null ? resp.optString("message", "Unknown error")
|
||||
: "No response from server.");
|
||||
new Alert(Alert.AlertType.ERROR, "Failed to delete chat: " + msg).showAndWait();
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
private void removeChatFromSessionAndGoBack(UUID chatId) {
|
||||
// if (Session.chatList != null) Session.chatList.removeIf(e -> chatId.equals(e.getId()));
|
||||
// if (Session.activeChats != null) Session.activeChats.removeIf(e -> chatId.equals(e.getId()));
|
||||
// if (Session.archivedChats != null) Session.archivedChats.removeIf(e -> chatId.equals(e.getId()));
|
||||
|
||||
if (Session.currentChatId != null && Session.currentChatId.equals(chatId.toString())) {
|
||||
Session.currentChatId = null;
|
||||
Session.currentChatType = null;
|
||||
Session.currentChatEntry = null;
|
||||
Session.inChatMenu = false;
|
||||
|
||||
MainController.getInstance().closeOverlay(profileCard.getParent());
|
||||
|
||||
AppRouter.showMain();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
MainController.getInstance().refreshChatListUI();
|
||||
} catch (Exception ignore) {
|
||||
AppRouter.showMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,6 @@
|
||||
<contextMenu>
|
||||
<ContextMenu fx:id="infoMoreMenu">
|
||||
<items>
|
||||
<MenuItem fx:id="deleteChatItem" text="Delete chat" style="-fx-text-fill: red;">
|
||||
<graphic>
|
||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@/org/to/telegramfinalproject/Icons/delete_red.png"/>
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
<MenuItem fx:id="blockItem" text="Block" style="-fx-text-fill: red;">
|
||||
<graphic>
|
||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||
@@ -62,6 +53,15 @@
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
<MenuItem fx:id="deleteChatItem" text="Delete chat" style="-fx-text-fill: red;">
|
||||
<graphic>
|
||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@/org/to/telegramfinalproject/Icons/delete_red.png"/>
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</MenuItem>
|
||||
</items>
|
||||
</ContextMenu>
|
||||
</contextMenu>
|
||||
|
||||
Reference in New Issue
Block a user