Block backend connected to UI.

This commit is contained in:
Asal Lotfi
2025-09-05 22:51:51 +03:30
parent 7fe4a997fd
commit 195d9306e5
4 changed files with 74 additions and 37 deletions
@@ -14,7 +14,7 @@ public final class AvatarFX {
Circle c = new Circle(); Circle c = new Circle();
c.radiusProperty().bind(Bindings.min(iv.fitWidthProperty(), iv.fitHeightProperty()).divide(2)); 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)); c.centerYProperty().bind(iv.fitHeightProperty().divide(2));
iv.setClip(c); iv.setClip(c);
} }
@@ -842,7 +842,7 @@ public class ChatPageController {
} else { } else {
setDefaultHeaderAvatarByType(entry.getType()); setDefaultHeaderAvatarByType(entry.getType());
} }
AvatarFX.circleClip(userAvatar, 36); AvatarFX.circleClip(userAvatar, 40);
// حالت اولیه (بدون انتظار هدر) // حالت اولیه (بدون انتظار هدر)
@@ -863,11 +863,13 @@ public class ChatPageController {
// === (3-dot menu + header click) === // === (3-dot menu + header click) ===
configureHeaderActions(entry); configureHeaderActions(entry);
requestBlockStatusByChat(entry);
} }
private void requestBlockStatusByChat(ChatEntry entry) { public void requestBlockStatusByChat(ChatEntry entry) {
if (entry == null || !"private".equalsIgnoreCase(entry.getType())) return; if (entry == null || !"private".equalsIgnoreCase(entry.getType())) return;
String viewerId = Session.getUserUUID(); // internal_uuid کاربر فعلی String viewerId = Session.getUserUUID(); // internal_uuid کاربر فعلی
@@ -890,7 +892,7 @@ public class ChatPageController {
}).start(); }).start();
} }
private void applyBlockUi(boolean blockedByMe, boolean blockedMe) { public void applyBlockUi(boolean blockedByMe, boolean blockedMe) {
this.blockedByMeFlag = blockedByMe; this.blockedByMeFlag = blockedByMe;
this.blockedMeFlag = blockedMe; this.blockedMeFlag = blockedMe;
@@ -945,6 +947,8 @@ public class ChatPageController {
// Wire up menu + header click // Wire up menu + header click
configureHeaderActions(entry); configureHeaderActions(entry);
requestBlockStatusByChat(entry);
} }
private void configureHeaderActions(ChatEntry entry) { private void configureHeaderActions(ChatEntry entry) {
@@ -983,6 +987,7 @@ public class ChatPageController {
private void openInfoScene(ChatEntry entry) { private void openInfoScene(ChatEntry entry) {
JSONObject req = new JSONObject(); JSONObject req = new JSONObject();
String targetId = null; // 👈 capture it here for the private case
switch (entry.getType().toLowerCase()) { switch (entry.getType().toLowerCase()) {
case "private" -> { case "private" -> {
@@ -1003,7 +1008,7 @@ public class ChatPageController {
return; return;
} }
String targetId = targetResp.optJSONObject("data").optString("target_id", null); targetId = targetResp.optJSONObject("data").optString("target_id", null);
if (targetId == null || targetId.isBlank()) { if (targetId == null || targetId.isBlank()) {
Platform.runLater(() -> Platform.runLater(() ->
MainController.getInstance().showAlert( MainController.getInstance().showAlert(
@@ -1038,6 +1043,8 @@ public class ChatPageController {
} }
} }
final String finalTargetId = targetId; // 👈 capture for use in the FX thread
new Thread(() -> { new Thread(() -> {
JSONObject resp = ActionHandler.sendWithResponse(req); JSONObject resp = ActionHandler.sendWithResponse(req);
if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) { if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
@@ -1074,7 +1081,8 @@ public class ChatPageController {
"/org/to/telegramfinalproject/Fxml/user_info.fxml")); "/org/to/telegramfinalproject/Fxml/user_info.fxml"));
overlay = loader.load(); overlay = loader.load();
UserInfoController c = loader.getController(); UserInfoController c = loader.getController();
c.setProfileDataFromJson(entry, data); // 👇 pass targetId so UserInfoController gets the real UUID
c.setProfileDataFromJson(entry, data, finalTargetId);
} }
case "group" -> { case "group" -> {
loader = new FXMLLoader(getClass().getResource( loader = new FXMLLoader(getClass().getResource(
@@ -40,6 +40,7 @@ public class UserInfoController {
@FXML private ImageView usernameIcon; @FXML private ImageView usernameIcon;
private String otherUserId; // internal_uuid of the other user private String otherUserId; // internal_uuid of the other user
private ChatEntry entry;
private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/"; private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/";
@@ -73,8 +74,8 @@ public class UserInfoController {
}); });
deleteChatItem.setOnAction(e -> handleDeleteChat()); deleteChatItem.setOnAction(e -> handleDeleteChat());
blockItem.setOnAction(e -> handleBlock()); blockItem.setOnAction(e -> handleToggleBlock(entry));
unblockItem.setOnAction(e -> handleUnblock()); unblockItem.setOnAction(e -> handleToggleBlock(entry));
// Register scene for ThemeManager → stylesheet swap will handle colors/icons // Register scene for ThemeManager → stylesheet swap will handle colors/icons
Platform.runLater(() -> { Platform.runLater(() -> {
@@ -93,7 +94,11 @@ public class UserInfoController {
} }
/** Backend JSON → UI */ /** 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 --- // --- Profile name ---
String name = data.optString("profile_name", entry.getName()); String name = data.optString("profile_name", entry.getName());
profileName.setText(name); profileName.setText(name);
@@ -141,9 +146,6 @@ public class UserInfoController {
); );
} }
// --- Other user ID (needed for block/delete) ---
this.otherUserId = data.optString("other_user_id", entry.getId().toString());
// --- Block/Unblock --- // --- Block/Unblock ---
boolean blocked = data.optBoolean("blocked", false) boolean blocked = data.optBoolean("blocked", false)
|| data.optBoolean("is_blocked", false) || data.optBoolean("is_blocked", false)
@@ -163,28 +165,55 @@ public class UserInfoController {
} }
} }
private void handleBlock() { private void handleToggleBlock(ChatEntry entry) {
if (otherUserId == null) return;
// 1) Send toggle request
JSONObject req = new JSONObject() JSONObject req = new JSONObject()
.put("action", "block_user") .put("action", "toggle_block")
.put("target_id", otherUserId) .put("user_id", Session.getUserUUID())
.put("viewer_id", Session.getUserUUID()); .put("target_id", otherUserId);
JSONObject resp = ActionHandler.sendWithResponse(req); JSONObject resp = ActionHandler.sendWithResponse(req);
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { if (resp == null) {
updateBlockMenu(true); MainController.getInstance().showAlert("Error", "No response from server.", Alert.AlertType.ERROR);
} return;
} }
private void handleUnblock() { if (!"success".equalsIgnoreCase(resp.optString("status"))) {
JSONObject req = new JSONObject() MainController.getInstance().showAlert("Error",
.put("action", "unblock_user") resp.optString("message", "Failed to toggle block"),
.put("target_id", otherUserId) Alert.AlertType.ERROR);
.put("viewer_id", Session.getUserUUID()); return;
JSONObject resp = ActionHandler.sendWithResponse(req);
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
updateBlockMenu(false);
} }
// 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);
// 3) Update UI on FX thread
Platform.runLater(() -> {
// update overlay (block/unblock menu)
updateBlockMenu(blockedByMe);
// update chat page input bar
ChatPageController c = ChatPageController.getInstance();
if (c != null) {
c.applyBlockUi(blockedByMe, blockedMe);
}
});
} }
private void updateBlockMenu(boolean isBlocked) { private void updateBlockMenu(boolean isBlocked) {
@@ -35,15 +35,6 @@
<contextMenu> <contextMenu>
<ContextMenu fx:id="infoMoreMenu"> <ContextMenu fx:id="infoMoreMenu">
<items> <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;"> <MenuItem fx:id="blockItem" text="Block" style="-fx-text-fill: red;">
<graphic> <graphic>
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true"> <ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
@@ -62,6 +53,15 @@
</ImageView> </ImageView>
</graphic> </graphic>
</MenuItem> </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> </items>
</ContextMenu> </ContextMenu>
</contextMenu> </contextMenu>