Handle send message bar in different situation
This commit is contained in:
@@ -446,7 +446,31 @@ public class ActionHandler {
|
|||||||
send(req);
|
send(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void createGroupUI(String id, String name, String url, String user ){
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "create_group");
|
||||||
|
req.put("user_id", user);
|
||||||
|
req.put("group_id", id);
|
||||||
|
req.put("group_name", name);
|
||||||
|
req.put("image_url", url.isBlank() ? JSONObject.NULL : url);
|
||||||
|
|
||||||
|
send(req);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void createChannelUI(String id, String name, String url , String user){
|
||||||
|
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "create_channel");
|
||||||
|
req.put("user_id", user);
|
||||||
|
req.put("channel_id", id);
|
||||||
|
req.put("channel_name", name);
|
||||||
|
req.put("image_url", url.isBlank() ? JSONObject.NULL : url);
|
||||||
|
|
||||||
|
send(req);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void createChannel() {
|
public void createChannel() {
|
||||||
String channelId = null;
|
String channelId = null;
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||||||
|
|
||||||
public class TelegramClient {
|
public class TelegramClient {
|
||||||
private static final String SERVER_HOST = "localhost";
|
private static final String SERVER_HOST = "localhost";
|
||||||
private static final int SERVER_PORT = 8000;
|
private static final int SERVER_PORT = 8080;
|
||||||
|
|
||||||
private static TelegramClient instance;
|
private static TelegramClient instance;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.to.telegramfinalproject.UI;
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
@@ -75,6 +76,25 @@ public class ChatPageController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ImageView sendIcon;
|
private ImageView sendIcon;
|
||||||
|
|
||||||
|
// ===== For Searching System =====
|
||||||
|
@FXML private VBox composerPane;
|
||||||
|
@FXML private VBox joinPane;
|
||||||
|
@FXML private VBox addContactPane;
|
||||||
|
|
||||||
|
//For search handeling
|
||||||
|
@FXML private Button joinButton;
|
||||||
|
@FXML private Button addContactButton;
|
||||||
|
|
||||||
|
//Handle View chat
|
||||||
|
@FXML private Button unblockBtn;
|
||||||
|
@FXML private VBox readOnlyPane;
|
||||||
|
@FXML private Label readOnlyLabel;
|
||||||
|
|
||||||
|
|
||||||
|
private ChatViewMode currentMode = ChatViewMode.NORMAL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===== Time formatter for messages =====
|
// ===== Time formatter for messages =====
|
||||||
private static final DateTimeFormatter FMT_HHMM = DateTimeFormatter.ofPattern("HH:mm");
|
private static final DateTimeFormatter FMT_HHMM = DateTimeFormatter.ofPattern("HH:mm");
|
||||||
private static final DateTimeFormatter FMT_DATE_TIME = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
|
private static final DateTimeFormatter FMT_DATE_TIME = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
|
||||||
@@ -533,40 +553,127 @@ public class ChatPageController {
|
|||||||
return new Image(url.toExternalForm());
|
return new Image(url.toExternalForm());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// public void showChat(ChatEntry entry) {
|
||||||
|
// this.currentChat = entry;
|
||||||
|
//
|
||||||
|
// chatTitle.setText(entry.getName());
|
||||||
|
//
|
||||||
|
// // آواتار پیشفرض بر اساس نوع
|
||||||
|
// // ChatPageController.showChat(...)
|
||||||
|
// if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
||||||
|
// Image img = AvatarLocalResolver.load(entry.getImageUrl());
|
||||||
|
// if (img != null) {
|
||||||
|
// userAvatar.setImage(img);
|
||||||
|
// } else {
|
||||||
|
// // ⬇️ فالبک بر اساس نوع
|
||||||
|
// setDefaultHeaderAvatarByType(entry.getType());
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// setDefaultHeaderAvatarByType(entry.getType());
|
||||||
|
// }
|
||||||
|
//// userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
|
// AvatarFX.circleClip(userAvatar, 36);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// fetchAndRenderHeader(entry);
|
||||||
|
//
|
||||||
|
// // پیامها
|
||||||
|
// messageContainer.getChildren().clear();
|
||||||
|
// loadMessages(entry);
|
||||||
|
// markAsRead(entry);
|
||||||
|
//
|
||||||
|
// Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
// }
|
||||||
|
|
||||||
|
//
|
||||||
|
// public void showChat(ChatEntry entry) {
|
||||||
|
// this.currentChat = entry;
|
||||||
|
// this.chatName = entry.getName(); // برای لاگ/منو
|
||||||
|
//
|
||||||
|
// chatTitle.setText(entry.getName());
|
||||||
|
// if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
||||||
|
// Image img = AvatarLocalResolver.load(entry.getImageUrl());
|
||||||
|
// if (img != null) userAvatar.setImage(img); else setDefaultHeaderAvatarByType(entry.getType());
|
||||||
|
// } else {
|
||||||
|
// setDefaultHeaderAvatarByType(entry.getType());
|
||||||
|
// }
|
||||||
|
// AvatarFX.circleClip(userAvatar, 36);
|
||||||
|
//
|
||||||
|
// fetchAndRenderHeader(entry);
|
||||||
|
//
|
||||||
|
// messageContainer.getChildren().clear();
|
||||||
|
// loadMessages(entry);
|
||||||
|
// markAsRead(entry);
|
||||||
|
//
|
||||||
|
// applyMode(ChatViewMode.NORMAL);
|
||||||
|
//
|
||||||
|
// Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
public void showChat(ChatEntry entry) {
|
public void showChat(ChatEntry entry) {
|
||||||
this.currentChat = entry;
|
this.currentChat = entry;
|
||||||
|
this.chatName = entry.getName();
|
||||||
|
|
||||||
chatTitle.setText(entry.getName());
|
chatTitle.setText(entry.getName());
|
||||||
|
|
||||||
// آواتار پیشفرض بر اساس نوع
|
|
||||||
// ChatPageController.showChat(...)
|
|
||||||
if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
||||||
Image img = AvatarLocalResolver.load(entry.getImageUrl());
|
Image img = AvatarLocalResolver.load(entry.getImageUrl());
|
||||||
if (img != null) {
|
if (img != null) userAvatar.setImage(img); else setDefaultHeaderAvatarByType(entry.getType());
|
||||||
userAvatar.setImage(img);
|
|
||||||
} else {
|
|
||||||
// ⬇️ فالبک بر اساس نوع
|
|
||||||
setDefaultHeaderAvatarByType(entry.getType());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setDefaultHeaderAvatarByType(entry.getType());
|
setDefaultHeaderAvatarByType(entry.getType());
|
||||||
}
|
}
|
||||||
// userAvatar.setClip(new Circle(20, 20, 20));
|
|
||||||
AvatarFX.circleClip(userAvatar, 36);
|
AvatarFX.circleClip(userAvatar, 36);
|
||||||
|
|
||||||
|
// حالت اولیه (بدون انتظار هدر)
|
||||||
|
if ("channel".equalsIgnoreCase(entry.getType())) {
|
||||||
|
boolean canPostLocal = entry.isOwner() || entry.isAdmin()
|
||||||
|
|| (entry.getPermissions()!=null && entry.getPermissions().optBoolean("can_post", false));
|
||||||
|
applyMode(canPostLocal ? ChatViewMode.NORMAL : ChatViewMode.READ_ONLY);
|
||||||
|
} else {
|
||||||
|
applyMode(ChatViewMode.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fetchAndRenderHeader(entry);
|
|
||||||
|
|
||||||
// پیامها
|
|
||||||
messageContainer.getChildren().clear();
|
messageContainer.getChildren().clear();
|
||||||
loadMessages(entry);
|
loadMessages(entry);
|
||||||
markAsRead(entry);
|
markAsRead(entry);
|
||||||
|
|
||||||
Platform.runLater(() -> messageInput.requestFocus());
|
// حالا هدر بیاد، دوباره نهاییاش میکنیم
|
||||||
|
fetchAndRenderHeader(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void showChat(ChatEntry entry, ChatViewMode mode) {
|
||||||
|
this.currentChat = entry;
|
||||||
|
|
||||||
|
// --- Header ---
|
||||||
|
chatTitle.setText(entry.getName());
|
||||||
|
if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
|
||||||
|
Image img = AvatarLocalResolver.load(entry.getImageUrl());
|
||||||
|
if (img != null) userAvatar.setImage(img); else setDefaultHeaderAvatarByType(entry.getType());
|
||||||
|
} else {
|
||||||
|
setDefaultHeaderAvatarByType(entry.getType());
|
||||||
|
}
|
||||||
|
AvatarFX.circleClip(userAvatar, 36);
|
||||||
|
|
||||||
|
fetchAndRenderHeader(entry);
|
||||||
|
|
||||||
|
// --- Messages ---
|
||||||
|
messageContainer.getChildren().clear();
|
||||||
|
loadMessages(entry);
|
||||||
|
markAsRead(entry);
|
||||||
|
|
||||||
|
// --- حالت UI (Composer / Join / Add Contact) ---
|
||||||
|
applyMode(mode);
|
||||||
|
|
||||||
|
// فوکوس اگر در حالت نرمال هستیم
|
||||||
|
if (mode == ChatViewMode.NORMAL) {
|
||||||
|
Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void loadMessages(ChatEntry entry) {
|
private void loadMessages(ChatEntry entry) {
|
||||||
JSONObject req = new JSONObject();
|
JSONObject req = new JSONObject();
|
||||||
req.put("action", "get_messages_UI");
|
req.put("action", "get_messages_UI");
|
||||||
@@ -967,12 +1074,17 @@ public class ChatPageController {
|
|||||||
req.put("receiver_id", entry.getId().toString());
|
req.put("receiver_id", entry.getId().toString());
|
||||||
req.put("receiver_type", entry.getType()); // باید "private" باشه
|
req.put("receiver_type", entry.getType()); // باید "private" باشه
|
||||||
|
|
||||||
// 👇 اضافه کن: آیدی کاربر فعلی (current user)
|
String viewer = Session.getUserUUID(); // internal_uuid کاربر فعلی
|
||||||
UUID viewerId = UUID.fromString(Session.getUserUUID()); // هر جایی که نگه میداری
|
if (viewer != null && !viewer.isBlank()) {
|
||||||
if ("private".equalsIgnoreCase(entry.getType()) && viewerId != null) {
|
req.put("viewer_id", viewer);
|
||||||
req.put("viewer_id", viewerId.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // 👇 اضافه کن: آیدی کاربر فعلی (current user)
|
||||||
|
// UUID viewerId = UUID.fromString(Session.getUserUUID()); // هر جایی که نگه میداری
|
||||||
|
// if ("private".equalsIgnoreCase(entry.getType()) && viewerId != null) {
|
||||||
|
// req.put("viewer_id", viewerId.toString());
|
||||||
|
// }
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
JSONObject resp;
|
JSONObject resp;
|
||||||
try {
|
try {
|
||||||
@@ -1000,21 +1112,46 @@ public class ChatPageController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private void updatePrivateHeader(ChatEntry entry, JSONObject data) {
|
||||||
|
// String name = nz(data.optString("profile_name", entry.getName()));
|
||||||
|
// chatTitle.setText(name);
|
||||||
|
//
|
||||||
|
// // other_user_id برای ریلتایم status
|
||||||
|
// String other = data.optString("other_user_id", "");
|
||||||
|
// if (!other.isBlank()) {
|
||||||
|
// try { entry.setOtherUserId(java.util.UUID.fromString(other)); } catch (Exception ignore) {}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // تصویر
|
||||||
|
// String img = data.optString("image_url", "");
|
||||||
|
// if (hasVal(img)) {
|
||||||
|
// try {
|
||||||
|
// Image im = AvatarLocalResolver.load(img); // ⬅️
|
||||||
|
// if (im != null) userAvatar.setImage(im);
|
||||||
|
// userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
|
// } catch (Exception ignore) {}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// chatStatus.setText(userStatusText(
|
||||||
|
// data.optBoolean("online", false),
|
||||||
|
// data.optString("last_seen", null)
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
private void updatePrivateHeader(ChatEntry entry, JSONObject data) {
|
private void updatePrivateHeader(ChatEntry entry, JSONObject data) {
|
||||||
String name = nz(data.optString("profile_name", entry.getName()));
|
String name = nz(data.optString("profile_name", entry.getName()));
|
||||||
chatTitle.setText(name);
|
chatTitle.setText(name);
|
||||||
|
|
||||||
// other_user_id برای ریلتایم status
|
|
||||||
String other = data.optString("other_user_id", "");
|
String other = data.optString("other_user_id", "");
|
||||||
if (!other.isBlank()) {
|
if (!other.isBlank()) {
|
||||||
try { entry.setOtherUserId(java.util.UUID.fromString(other)); } catch (Exception ignore) {}
|
try { entry.setOtherUserId(UUID.fromString(other)); } catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// تصویر
|
|
||||||
String img = data.optString("image_url", "");
|
String img = data.optString("image_url", "");
|
||||||
if (hasVal(img)) {
|
if (hasVal(img)) {
|
||||||
try {
|
try {
|
||||||
Image im = AvatarLocalResolver.load(img); // ⬅️
|
Image im = AvatarLocalResolver.load(img);
|
||||||
if (im != null) userAvatar.setImage(im);
|
if (im != null) userAvatar.setImage(im);
|
||||||
userAvatar.setClip(new Circle(20, 20, 20));
|
userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
@@ -1025,7 +1162,19 @@ public class ChatPageController {
|
|||||||
data.optString("last_seen", null)
|
data.optString("last_seen", null)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// ⭐️ بلاک؟
|
||||||
|
boolean blocked = data.optBoolean("blocked", false)
|
||||||
|
|| data.optBoolean("is_blocked", false)
|
||||||
|
|| data.optBoolean("blocked_by_me", false);
|
||||||
|
|
||||||
|
if (blocked) {
|
||||||
|
if (readOnlyLabel != null) readOnlyLabel.setText(""); // فقط UNBLOCK را نشان بده
|
||||||
|
applyMode(ChatViewMode.BLOCKED);
|
||||||
|
} else {
|
||||||
|
applyMode(ChatViewMode.NORMAL);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void updateGroupHeader(ChatEntry entry, JSONObject data) {
|
private void updateGroupHeader(ChatEntry entry, JSONObject data) {
|
||||||
chatTitle.setText(nz(data.optString("group_name", entry.getName())));
|
chatTitle.setText(nz(data.optString("group_name", entry.getName())));
|
||||||
@@ -1046,22 +1195,50 @@ public class ChatPageController {
|
|||||||
: (members + " members"));
|
: (members + " members"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private void updateChannelHeader(ChatEntry entry, JSONObject data) {
|
||||||
|
// chatTitle.setText(nz(data.optString("channel_name", entry.getName())));
|
||||||
|
//
|
||||||
|
// String img = data.optString("image_url", "");
|
||||||
|
// if (hasVal(img)) {
|
||||||
|
// try {
|
||||||
|
// Image im = AvatarLocalResolver.load(img); // ⬅️
|
||||||
|
// if (im != null) userAvatar.setImage(im);
|
||||||
|
// userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
|
// } catch (Exception ignore) {}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// int subs = data.optInt("member_count", 0);
|
||||||
|
// chatStatus.setText(subs + " subscribers");
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
private void updateChannelHeader(ChatEntry entry, JSONObject data) {
|
private void updateChannelHeader(ChatEntry entry, JSONObject data) {
|
||||||
chatTitle.setText(nz(data.optString("channel_name", entry.getName())));
|
chatTitle.setText(nz(data.optString("channel_name", entry.getName())));
|
||||||
|
|
||||||
String img = data.optString("image_url", "");
|
String img = data.optString("image_url", "");
|
||||||
if (hasVal(img)) {
|
if (hasVal(img)) {
|
||||||
try {
|
try {
|
||||||
Image im = AvatarLocalResolver.load(img); // ⬅️
|
Image im = AvatarLocalResolver.load(img);
|
||||||
if (im != null) userAvatar.setImage(im);
|
if (im != null) userAvatar.setImage(im);
|
||||||
userAvatar.setClip(new Circle(20, 20, 20));
|
userAvatar.setClip(new Circle(20, 20, 20));
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int subs = data.optInt("member_count", 0);
|
int subs = data.optInt("member_count", 0);
|
||||||
chatStatus.setText(subs + " subscribers");
|
chatStatus.setText(subs + " subscribers");
|
||||||
|
|
||||||
|
boolean canPost = canPostToChannel(entry, data);
|
||||||
|
if (canPost) {
|
||||||
|
applyMode(ChatViewMode.NORMAL);
|
||||||
|
Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
} else {
|
||||||
|
if (readOnlyLabel != null) readOnlyLabel.setText("YOU CAN’T SEND MESSAGES IN THIS CHANNEL");
|
||||||
|
applyMode(ChatViewMode.READ_ONLY);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void onUserStatusChanged(String userUuid, String status, String lastSeenIso) {
|
public void onUserStatusChanged(String userUuid, String status, String lastSeenIso) {
|
||||||
if (currentChat == null || !"private".equalsIgnoreCase(currentChat.getType())) return;
|
if (currentChat == null || !"private".equalsIgnoreCase(currentChat.getType())) return;
|
||||||
@@ -1112,4 +1289,203 @@ public class ChatPageController {
|
|||||||
java.util.Objects.requireNonNull(getClass().getResourceAsStream(path))
|
java.util.Objects.requireNonNull(getClass().getResourceAsStream(path))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onJoinClicked() {
|
||||||
|
if (currentChat == null) return;
|
||||||
|
|
||||||
|
// 1) internal_uuid کاربر فعلی (UUID)
|
||||||
|
String myInternalUuid = Session.currentUser != null
|
||||||
|
? Session.currentUser.optString("internal_uuid", "")
|
||||||
|
: "";
|
||||||
|
if (myInternalUuid.isBlank()) {
|
||||||
|
addSystemMessage("Join failed: missing current user internal_uuid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) internal_uuid مقصد (گروه/کانال)
|
||||||
|
String targetId = currentChat.getId().toString();
|
||||||
|
|
||||||
|
// 3) نوع و نام اکشن
|
||||||
|
String t = currentChat.getType();
|
||||||
|
String action = "group".equalsIgnoreCase(t) ? "join_group" : "join_channel";
|
||||||
|
|
||||||
|
// 4) درخواست طبق قرارداد سرور (کلیدها: user_id = UUID کاربر، id = UUID مقصد)
|
||||||
|
JSONObject req = new JSONObject()
|
||||||
|
.put("action", action)
|
||||||
|
.put("user_id", myInternalUuid) // ← UUID
|
||||||
|
.put("id", targetId); // ← UUID گروه/کانال
|
||||||
|
|
||||||
|
JSONObject res = ActionHandler.sendWithResponse(req);
|
||||||
|
if (res != null && "success".equalsIgnoreCase(res.optString("status"))) {
|
||||||
|
MainController.getInstance().onJoinedOrAdded(currentChat);
|
||||||
|
applyMode(ChatViewMode.NORMAL);
|
||||||
|
Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
} else {
|
||||||
|
addSystemMessage("Join failed: " + (res != null ? res.optString("message","") : "no response"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onAddContactClicked() {
|
||||||
|
if (currentChat == null) return;
|
||||||
|
|
||||||
|
String myUserId = Session.currentUser != null
|
||||||
|
? Session.currentUser.optString("user_id", "")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
// 2) internal_uuid طرف مقابل
|
||||||
|
UUID other = currentChat.getOtherUserId();
|
||||||
|
if (other == null) {
|
||||||
|
// اگر otherUserId هنوز نگرفتهای، بهتره قبلش از هدر/پروفایل بیاری.
|
||||||
|
addSystemMessage("Cannot add: other user UUID is missing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) درخواست طبق قرارداد سرور
|
||||||
|
JSONObject req = new JSONObject()
|
||||||
|
.put("action", "add_contact")
|
||||||
|
.put("user_id", myUserId) // ← stringِ user_id (غیر UUID)
|
||||||
|
.put("contact_id", other.toString()); // ← UUID طرف مقابل
|
||||||
|
|
||||||
|
// 4) ارسال
|
||||||
|
JSONObject res = ActionHandler.sendWithResponse(req);
|
||||||
|
if (res != null && "success".equalsIgnoreCase(res.optString("status"))) {
|
||||||
|
// به لیست چتها اضافه و سوییچ به حالت نرمال
|
||||||
|
MainController.getInstance().onJoinedOrAdded(currentChat);
|
||||||
|
applyMode(ChatViewMode.NORMAL);
|
||||||
|
Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
} else {
|
||||||
|
addSystemMessage("Add contact failed: " + (res != null ? res.optString("message","") : "no response"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// private void applyMode(ChatViewMode mode) {
|
||||||
|
// currentMode = mode;
|
||||||
|
//
|
||||||
|
// boolean normal = (mode == ChatViewMode.NORMAL);
|
||||||
|
// boolean needsJoin = (mode == ChatViewMode.NEEDS_JOIN);
|
||||||
|
// boolean needsAdd = (mode == ChatViewMode.NEEDS_ADD_CONTACT);
|
||||||
|
//
|
||||||
|
// composerPane.setVisible(normal);
|
||||||
|
// composerPane.setManaged(normal);
|
||||||
|
//
|
||||||
|
// joinPane.setVisible(needsJoin);
|
||||||
|
// joinPane.setManaged(needsJoin);
|
||||||
|
//
|
||||||
|
// addContactPane.setVisible(needsAdd);
|
||||||
|
// addContactPane.setManaged(needsAdd);
|
||||||
|
//
|
||||||
|
// if (needsJoin && joinButton != null && currentChat != null) {
|
||||||
|
// String what = "channel".equalsIgnoreCase(currentChat.getType()) ? "CHANNEL" : "GROUP";
|
||||||
|
// joinButton.setText(("Join " + what).toUpperCase()); // => JOIN CHANNEL / JOIN GROUP
|
||||||
|
// }
|
||||||
|
// if (needsAdd && addContactButton != null) {
|
||||||
|
// addContactButton.setText("ADD CONTACT");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void applyMode(ChatViewMode mode) {
|
||||||
|
currentMode = mode;
|
||||||
|
|
||||||
|
boolean normal = (mode == ChatViewMode.NORMAL);
|
||||||
|
boolean needsJoin = (mode == ChatViewMode.NEEDS_JOIN);
|
||||||
|
boolean needsAdd = (mode == ChatViewMode.NEEDS_ADD_CONTACT);
|
||||||
|
boolean readOnly = (mode == ChatViewMode.READ_ONLY);
|
||||||
|
boolean blocked = (mode == ChatViewMode.BLOCKED);
|
||||||
|
|
||||||
|
// Composer فقط در حالت نرمال
|
||||||
|
composerPane.setVisible(normal);
|
||||||
|
composerPane.setManaged(normal);
|
||||||
|
|
||||||
|
// Join / Add
|
||||||
|
joinPane.setVisible(needsJoin);
|
||||||
|
joinPane.setManaged(needsJoin);
|
||||||
|
addContactPane.setVisible(needsAdd);
|
||||||
|
addContactPane.setManaged(needsAdd);
|
||||||
|
|
||||||
|
// پنل پایین برای READ_ONLY/BLOCKED
|
||||||
|
boolean showRO = readOnly || blocked;
|
||||||
|
if (readOnlyPane != null) {
|
||||||
|
readOnlyPane.setVisible(showRO);
|
||||||
|
readOnlyPane.setManaged(showRO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// متن آبی برای READ_ONLY
|
||||||
|
if (readOnlyLabel != null) {
|
||||||
|
readOnlyLabel.setVisible(readOnly);
|
||||||
|
readOnlyLabel.setManaged(readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
// دکمهٔ قرمز UNBLOCK فقط در BLOCKED
|
||||||
|
if (unblockBtn != null) {
|
||||||
|
unblockBtn.setVisible(blocked);
|
||||||
|
unblockBtn.setManaged(blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
// متن دکمههای Join/Add
|
||||||
|
if (needsJoin && joinButton != null && currentChat != null) {
|
||||||
|
String what = "channel".equalsIgnoreCase(currentChat.getType()) ? "CHANNEL" : "GROUP";
|
||||||
|
joinButton.setText(("Join " + what).toUpperCase());
|
||||||
|
}
|
||||||
|
if (needsAdd && addContactButton != null) {
|
||||||
|
addContactButton.setText("ADD CONTACT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void onUnblockClicked() {
|
||||||
|
if (currentChat == null) return;
|
||||||
|
UUID other = currentChat.getOtherUserId();
|
||||||
|
if (other == null && currentChat.getDisplayId() == null) return;
|
||||||
|
|
||||||
|
// ⚠️ با API خودت هماهنگ کن:
|
||||||
|
// این یک الگوی معمول است: user_id (کاربر فعلی) + contact_id (کسی که بلاک شده)
|
||||||
|
org.json.JSONObject req = new org.json.JSONObject()
|
||||||
|
.put("action", "unblock_user") // یا "unblock_contact" طبق سرور
|
||||||
|
.put("user_id", org.to.telegramfinalproject.Client.Session.currentUser.getString("user_id"))
|
||||||
|
.put("contact_id", (other != null) ? other.toString() : currentChat.getDisplayId());
|
||||||
|
|
||||||
|
org.json.JSONObject res = org.to.telegramfinalproject.Client.ActionHandler.sendWithResponse(req);
|
||||||
|
boolean ok = (res != null) && ("ok".equalsIgnoreCase(res.optString("status"))
|
||||||
|
|| "success".equalsIgnoreCase(res.optString("status")));
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
// برگرد به حالت نرمال: کامپوزر باز شود
|
||||||
|
applyMode(ChatViewMode.NORMAL);
|
||||||
|
Platform.runLater(() -> messageInput.requestFocus());
|
||||||
|
} else {
|
||||||
|
// میتونی یک نوتیف کوچک بزنی
|
||||||
|
addSystemMessage("Unblock failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private boolean canPostToChannel(ChatEntry entry, JSONObject headerData) {
|
||||||
|
// 1) اگر سرور صراحتاً can_post داد، همان را بگیر
|
||||||
|
if (headerData != null && headerData.has("can_post")) {
|
||||||
|
return headerData.optBoolean("can_post", false);
|
||||||
|
}
|
||||||
|
// 2) یا اگر is_owner / is_admin را داد
|
||||||
|
if (headerData != null && (headerData.has("is_owner") || headerData.has("is_admin"))) {
|
||||||
|
return headerData.optBoolean("is_owner", false) || headerData.optBoolean("is_admin", false);
|
||||||
|
}
|
||||||
|
// 3) فالبک به اطلاعات لوکال: ChatEntry + permissions محلی
|
||||||
|
if (entry != null) {
|
||||||
|
if (entry.isOwner() || entry.isAdmin()) return true;
|
||||||
|
if (entry.getPermissions() != null && entry.getPermissions().optBoolean("can_post", false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
|
||||||
|
//For search handling
|
||||||
|
public enum ChatViewMode {
|
||||||
|
NORMAL, // member/contact; can send messages
|
||||||
|
NEEDS_JOIN, // group/channel preview; show Join button
|
||||||
|
NEEDS_ADD_CONTACT, // private preview; show Add Contact button
|
||||||
|
READ_ONLY,
|
||||||
|
BLOCKED
|
||||||
|
}
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ public class MainController {
|
|||||||
GLOBAL,
|
GLOBAL,
|
||||||
CHAT
|
CHAT
|
||||||
}
|
}
|
||||||
|
private ChatViewMode currentMode = ChatViewMode.NORMAL; // حالت فعلی: NORMAL/NEEDS_JOIN/NEEDS_ADD_CONTACT
|
||||||
private SearchMode currentSearchMode = SearchMode.GLOBAL;
|
private SearchMode currentSearchMode = SearchMode.GLOBAL;
|
||||||
private UUID currentChatId; // if in CHAT mode, which chat to search in
|
private UUID currentChatId; // if in CHAT mode, which chat to search in
|
||||||
|
|
||||||
@@ -423,6 +424,9 @@ public class MainController {
|
|||||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/chat_page.fxml"));
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/chat_page.fxml"));
|
||||||
Node chatPage = loader.load();
|
Node chatPage = loader.load();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ChatPageController controller = loader.getController();
|
ChatPageController controller = loader.getController();
|
||||||
controller.showChat(chat);
|
controller.showChat(chat);
|
||||||
|
|
||||||
@@ -439,6 +443,30 @@ public class MainController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void openChatWithMode(ChatEntry chat, ChatViewMode mode) {
|
||||||
|
try {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/chat_page.fxml"));
|
||||||
|
Node chatPage = loader.load();
|
||||||
|
|
||||||
|
ChatPageController controller = loader.getController();
|
||||||
|
controller.showChat(chat, mode); // ⬅️ متد جدید در ChatPageController
|
||||||
|
|
||||||
|
this.chatPageController = controller;
|
||||||
|
Session.currentChatId = chat.getId().toString();
|
||||||
|
|
||||||
|
chatDisplayArea.getChildren().setAll(chatPage);
|
||||||
|
|
||||||
|
chat.setUnreadCount(0);
|
||||||
|
ChatItemController item = itemControllers.get(chat.getId());
|
||||||
|
if (item != null) item.setUnread(0);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void toggleSidebar() {
|
private void toggleSidebar() {
|
||||||
if (isSidebarOpen) {
|
if (isSidebarOpen) {
|
||||||
@@ -832,39 +860,52 @@ public class MainController {
|
|||||||
private void openSearchResult(SearchResult r) {
|
private void openSearchResult(SearchResult r) {
|
||||||
switch (r.type) {
|
switch (r.type) {
|
||||||
case USER: {
|
case USER: {
|
||||||
|
// 1) اگه قبلاً چت پرایوت با این یوزر داری، از همون استفاده کن
|
||||||
|
UUID existingChatId = findExistingPrivateChatId(r.uuid);
|
||||||
|
|
||||||
java.util.UUID chatId = findExistingPrivateChatId(r.uuid);
|
ChatEntry ce = new ChatEntry();
|
||||||
if (chatId == null) {
|
|
||||||
chatId = fetchOrCreatePrivateChat(r.uuid);
|
|
||||||
if (chatId == null) {
|
|
||||||
System.out.println("❌ Failed to create/find private chat.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
org.to.telegramfinalproject.Models.ChatEntry ce = new org.to.telegramfinalproject.Models.ChatEntry();
|
|
||||||
ce.setId(chatId.toString()); // ⬅️ internal chat_id
|
|
||||||
ce.setDisplayId(r.displayId); // username
|
|
||||||
ce.setName(r.title); // profile_name
|
|
||||||
ce.setType("private");
|
ce.setType("private");
|
||||||
|
ce.setName(r.title);
|
||||||
|
ce.setDisplayId(r.displayId);
|
||||||
|
try { ce.setOtherUserId(r.uuid); } catch (Exception ignore) {}
|
||||||
|
|
||||||
openChat(ce);
|
ChatViewMode mode;
|
||||||
|
if (existingChatId != null) {
|
||||||
|
ce.setId(existingChatId.toString());
|
||||||
|
mode = ChatViewMode.NORMAL;
|
||||||
|
} else {
|
||||||
|
// هنوز چتی وجود ندارد → Preview (بدون ساخت چت)
|
||||||
|
// برای Preview از uuid خودِ طرف مقابل بهعنوان id موقت استفاده میکنیم
|
||||||
|
ce.setId(r.uuid.toString());
|
||||||
|
mode = isContact(r.uuid) ? ChatViewMode.NORMAL : ChatViewMode.NEEDS_ADD_CONTACT;
|
||||||
|
}
|
||||||
|
|
||||||
|
openChatWithMode(ce, mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case GROUP:
|
case GROUP:
|
||||||
case CHANNEL: {
|
case CHANNEL: {
|
||||||
org.to.telegramfinalproject.Models.ChatEntry existing =
|
org.to.telegramfinalproject.Models.ChatEntry existing =
|
||||||
findExistingChat(r.uuid, r.receiverType);
|
findExistingChat(r.uuid, r.receiverType);
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
openChat(existing);
|
openChatWithMode(existing, ChatViewMode.NORMAL);
|
||||||
} else {
|
} else {
|
||||||
org.to.telegramfinalproject.Models.ChatEntry ce = new org.to.telegramfinalproject.Models.ChatEntry();
|
org.to.telegramfinalproject.Models.ChatEntry ce = new org.to.telegramfinalproject.Models.ChatEntry();
|
||||||
ce.setId(r.uuid.toString()); // internal_uuid group/channel
|
ce.setId(r.uuid.toString()); // internal_uuid group/channel
|
||||||
ce.setDisplayId(r.displayId); // group_id/channel_id
|
ce.setDisplayId(r.displayId); // group_id/channel_id
|
||||||
ce.setName(r.title);
|
ce.setName(r.title);
|
||||||
ce.setType(r.receiverType);
|
ce.setType(r.receiverType);
|
||||||
openChat(ce);
|
//openChat(ce);
|
||||||
|
ChatViewMode mode;
|
||||||
|
try {
|
||||||
|
mode = isInAnyChatList(r.uuid) ? ChatViewMode.NORMAL : ChatViewMode.NEEDS_JOIN;
|
||||||
|
} catch (Exception e) {
|
||||||
|
mode = ChatViewMode.NEEDS_JOIN;
|
||||||
|
}
|
||||||
|
openChatWithMode(ce, mode);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -968,4 +1009,87 @@ public class MainController {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Search
|
||||||
|
private boolean isInAnyChatList(UUID chatId) {
|
||||||
|
var lists = List.of(
|
||||||
|
Session.chatList != null ? Session.chatList : List.<ChatEntry>of(),
|
||||||
|
Session.activeChats != null ? Session.activeChats : List.<ChatEntry>of(),
|
||||||
|
Session.archivedChats != null ? Session.archivedChats : List.<ChatEntry>of()
|
||||||
|
);
|
||||||
|
for (var lst : lists) {
|
||||||
|
for (var c : lst) {
|
||||||
|
try {
|
||||||
|
if (chatId.equals(UUID.fromString(c.getId().toString()))) return true;
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isContact(UUID userUuid) {
|
||||||
|
if (Session.contactEntries == null) return false;
|
||||||
|
try {
|
||||||
|
for (var c : Session.contactEntries) {
|
||||||
|
UUID id = c.getContactId();
|
||||||
|
if (userUuid.equals(id)) return true;
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ChatViewMode computeMode(ChatEntry ce) {
|
||||||
|
try {
|
||||||
|
UUID id = UUID.fromString(ce.getId().toString());
|
||||||
|
if (isInAnyChatList(id)) return ChatViewMode.NORMAL;
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
|
String t = ce.getType();
|
||||||
|
if ("group".equalsIgnoreCase(t) || "channel".equalsIgnoreCase(t)) {
|
||||||
|
return ChatViewMode.NEEDS_JOIN;
|
||||||
|
}
|
||||||
|
if ("private".equalsIgnoreCase(t)) {
|
||||||
|
UUID other = null;
|
||||||
|
try { other = ce.getOtherUserId(); } catch (Exception ignore) {}
|
||||||
|
return (other != null && isContact(other))
|
||||||
|
? ChatViewMode.NORMAL
|
||||||
|
: ChatViewMode.NEEDS_ADD_CONTACT;
|
||||||
|
}
|
||||||
|
return ChatViewMode.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onJoinedOrAdded(ChatEntry ce) {
|
||||||
|
try {
|
||||||
|
UUID id = UUID.fromString(ce.getId().toString());
|
||||||
|
if (!isInAnyChatList(id)) {
|
||||||
|
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;
|
||||||
|
if (t1 == null) return 1;
|
||||||
|
if (t2 == null) return -1;
|
||||||
|
return t2.compareTo(t1);
|
||||||
|
};
|
||||||
|
if (Session.chatList != null) Session.chatList.sort(byTimeDesc);
|
||||||
|
if (Session.activeChats != null) Session.activeChats.sort(byTimeDesc);
|
||||||
|
|
||||||
|
refreshChatListUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/* دقیقاً فقط روی این دو دکمه اعمال میشود */
|
||||||
|
#joinAction, #addContactAction {
|
||||||
|
-fx-background-color: transparent, transparent, transparent, transparent;
|
||||||
|
-fx-background-insets: 0,0,0,0;
|
||||||
|
-fx-background-radius: 0,0,0,0;
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-text-fill: #1e88e5; /* آبی لینک */
|
||||||
|
-fx-font-weight: 700;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-padding: 12 0 12 0;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
-fx-effect: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#joinAction:hover, #addContactAction:hover {
|
||||||
|
-fx-underline: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#joinAction:focused, #addContactAction:focused {
|
||||||
|
-fx-underline: true;
|
||||||
|
-fx-focus-color: transparent;
|
||||||
|
-fx-faint-focus-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#joinAction:armed, #addContactAction:armed {
|
||||||
|
-fx-opacity: .85;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ظرف بنر پایین */
|
||||||
|
.chat-footer-banner {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* دکمهٔ لینکمانند (مشترک) */
|
||||||
|
.footer-link-btn {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
-fx-padding: 6 0 6 0; /* نازک مثل لینک */
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-font-weight: 700;
|
||||||
|
-fx-text-fill: #1a73e8; /* پیشفرض آبی (برای Join/Add) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* کانتینر بنر پایین چت */
|
||||||
|
.chat-footer-banner {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-alignment: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* متن آبی برای حالت Read-only */
|
||||||
|
.chat-footer-banner .banner-text-blue {
|
||||||
|
-fx-text-fill: #1E88E5; /* آبی */
|
||||||
|
-fx-font-weight: 700;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* لینکاستایل دکمهها در بنر */
|
||||||
|
.chat-footer-banner .footer-link-btn {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: #1E88E5; /* آبی پیشفرض */
|
||||||
|
-fx-font-weight: 700;
|
||||||
|
-fx-padding: 6 12;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
.chat-footer-banner .footer-link-btn:hover {
|
||||||
|
-fx-underline: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* نسخه قرمز برای UNBLOCK */
|
||||||
|
.chat-footer-banner .footer-link-btn.danger {
|
||||||
|
-fx-text-fill: #D32F2F; /* قرمز */
|
||||||
|
}
|
||||||
@@ -6,17 +6,19 @@
|
|||||||
<?import javafx.scene.image.ImageView?>
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import java.net.URL?>
|
||||||
<VBox xmlns="http://javafx.com/javafx/17"
|
<VBox xmlns="http://javafx.com/javafx/17"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:controller="org.to.telegramfinalproject.UI.ChatPageController"
|
fx:controller="org.to.telegramfinalproject.UI.ChatPageController"
|
||||||
spacing="0"
|
spacing="0"
|
||||||
styleClass="chat-root">
|
styleClass="chat-root">
|
||||||
|
<stylesheets>
|
||||||
|
<URL value="@/org/to/telegramfinalproject/CSS/chat.css"/>
|
||||||
|
</stylesheets>
|
||||||
|
|
||||||
<!-- HEADER: avatar + name/status (left) | search + more (right) -->
|
<!-- HEADER: avatar + name/status (left) | search + more (right) -->
|
||||||
<HBox fx:id="chatHeader" spacing="10" styleClass="chat-header">
|
<HBox fx:id="chatHeader" spacing="10" styleClass="chat-header">
|
||||||
<padding>
|
<padding><Insets top="8" right="12" bottom="8" left="12"/></padding>
|
||||||
<Insets top="8" right="12" bottom="8" left="12"/>
|
|
||||||
</padding>
|
|
||||||
|
|
||||||
<!-- Avatar -->
|
<!-- Avatar -->
|
||||||
<ImageView fx:id="userAvatar" fitWidth="36" fitHeight="36" preserveRatio="true"/>
|
<ImageView fx:id="userAvatar" fitWidth="36" fitHeight="36" preserveRatio="true"/>
|
||||||
@@ -33,9 +35,7 @@
|
|||||||
<Button fx:id="searchInChatButton" styleClass="icon-btn" onAction="#openSearchPanel">
|
<Button fx:id="searchInChatButton" styleClass="icon-btn" onAction="#openSearchPanel">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fx:id="searchIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
|
<ImageView fx:id="searchIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
|
||||||
<image>
|
<image><Image url="@/org/to/telegramfinalproject/Icons/search_light.png"/></image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/search_light.png"/>
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -44,9 +44,7 @@
|
|||||||
<Button fx:id="moreButton" styleClass="icon-btn">
|
<Button fx:id="moreButton" styleClass="icon-btn">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fx:id="moreIcon" fitWidth="18" fitHeight="18" preserveRatio="true">
|
<ImageView fx:id="moreIcon" fitWidth="18" fitHeight="18" preserveRatio="true">
|
||||||
<image>
|
<image><Image url="@/org/to/telegramfinalproject/Icons/more_light.png"/></image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/more_light.png"/>
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
<contextMenu>
|
<contextMenu>
|
||||||
@@ -55,18 +53,14 @@
|
|||||||
<MenuItem fx:id="viewProfileItem" text="View profile">
|
<MenuItem fx:id="viewProfileItem" text="View profile">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
<image>
|
<image><Image url="@/org/to/telegramfinalproject/Icons/view_profile_light.png"/></image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/view_profile_light.png"/>
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem fx:id="deleteChatItem" text="Delete chat" style="-fx-text-fill: red;">
|
<MenuItem fx:id="deleteChatItem" text="Delete chat" style="-fx-text-fill: red;">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
<image>
|
<image><Image url="@/org/to/telegramfinalproject/Icons/delete_red.png"/></image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/delete_red.png"/>
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -85,26 +79,23 @@
|
|||||||
styleClass="chat-scroll">
|
styleClass="chat-scroll">
|
||||||
<content>
|
<content>
|
||||||
<VBox fx:id="messageContainer" spacing="10">
|
<VBox fx:id="messageContainer" spacing="10">
|
||||||
<padding>
|
<padding><Insets top="10" right="10" bottom="10" left="10"/></padding>
|
||||||
<Insets top="10" right="10" bottom="10" left="10"/>
|
|
||||||
</padding>
|
|
||||||
</VBox>
|
</VBox>
|
||||||
</content>
|
</content>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
|
|
||||||
<!-- INPUT BAR -->
|
<!-- ===== FOOTER: فقط یکی از این پنلها نمایش داده میشود ===== -->
|
||||||
<HBox fx:id="inputBar" spacing="8" styleClass="chat-input-bar">
|
|
||||||
<padding>
|
<!-- حالت عادی: کامپوزر پیام -->
|
||||||
<Insets top="8" right="12" bottom="8" left="12"/>
|
<VBox fx:id="composerPane" spacing="0" visible="true" managed="true">
|
||||||
</padding>
|
<HBox spacing="8" styleClass="chat-input-bar">
|
||||||
|
<padding><Insets top="8" right="12" bottom="8" left="12"/></padding>
|
||||||
|
|
||||||
<!-- Attachment -->
|
<!-- Attachment -->
|
||||||
<Button fx:id="attachmentButton" styleClass="icon-btn">
|
<Button fx:id="attachmentButton" styleClass="icon-btn">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fx:id="attachmentIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
|
<ImageView fx:id="attachmentIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
|
||||||
<image>
|
<image><Image url="@/org/to/telegramfinalproject/Icons/attachment_light.png"/></image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/attachment_light.png"/>
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -124,11 +115,44 @@
|
|||||||
<Button fx:id="sendButton" styleClass="send-btn">
|
<Button fx:id="sendButton" styleClass="send-btn">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fx:id="sendIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
|
<ImageView fx:id="sendIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
|
||||||
<image>
|
<image><Image url="@/org/to/telegramfinalproject/Icons/send_cyan2.png"/></image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/send_cyan2.png"/>
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|
||||||
|
<VBox fx:id="joinPane" alignment="CENTER" visible="false" managed="false" styleClass="chat-input-bar">
|
||||||
|
<padding><Insets top="12" right="12" bottom="12" left="12"/></padding>
|
||||||
|
<Button fx:id="joinButton" id="joinAction"
|
||||||
|
text="JOIN CHANNEL" onAction="#onJoinClicked"/>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<VBox fx:id="addContactPane" alignment="CENTER" visible="false" managed="false" styleClass="chat-input-bar">
|
||||||
|
<padding><Insets top="12" right="12" bottom="12" left="12"/></padding>
|
||||||
|
<Button fx:id="addContactButton" id="addContactAction"
|
||||||
|
text="ADD CONTACT" onAction="#onAddContactClicked"/>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<VBox fx:id="readOnlyPane" spacing="0" alignment="CENTER"
|
||||||
|
visible="false" managed="false" styleClass="chat-footer-banner" >
|
||||||
|
<padding><Insets top="10" right="12" bottom="10" left="12"/></padding>
|
||||||
|
<Label fx:id="readOnlyLabel"
|
||||||
|
text="You are not allowed to send messages."
|
||||||
|
styleClass="footer-link-btn"/>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<VBox fx:id="blockedPane" spacing="0" alignment="CENTER"
|
||||||
|
visible="false" managed="false" styleClass="chat-footer-banner">
|
||||||
|
<padding><Insets top="12" right="12" bottom="12" left="12"/></padding>
|
||||||
|
|
||||||
|
<Button fx:id="unblockBtn"
|
||||||
|
text="UNBLOCK"
|
||||||
|
onAction="#onUnblockClicked"
|
||||||
|
styleClass="footer-link-btn danger"/>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
|||||||
Reference in New Issue
Block a user