Merge remote-tracking branch 'origin/Main-UI' into Main-UI
This commit is contained in:
@@ -109,8 +109,10 @@ public class AddMembersController {
|
|||||||
String name = c.optString("profile_name", "");
|
String name = c.optString("profile_name", "");
|
||||||
String id = c.optString("contact_id", ""); // ← internal_uuid مخاطب
|
String id = c.optString("contact_id", ""); // ← internal_uuid مخاطب
|
||||||
if (id.isBlank()) continue;
|
if (id.isBlank()) continue;
|
||||||
String imageUrl = c.optString("image_url",
|
String imageUrl = c.optString("image_url", "").trim();
|
||||||
"/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
if (imageUrl.isEmpty() || imageUrl.equals("null")) {
|
||||||
|
imageUrl = "/org/to/telegramfinalproject/Avatars/default_user_profile.png";
|
||||||
|
}
|
||||||
String status = Optional.ofNullable(c.optString("last_seen", ""))
|
String status = Optional.ofNullable(c.optString("last_seen", ""))
|
||||||
.filter(s -> !s.isBlank()).map(s -> "last seen " + s).orElse("");
|
.filter(s -> !s.isBlank()).map(s -> "last seen " + s).orElse("");
|
||||||
allContacts.add(new Contact(id, name, status, imageUrl));
|
allContacts.add(new Contact(id, name, status, imageUrl));
|
||||||
@@ -146,12 +148,13 @@ public class AddMembersController {
|
|||||||
HBox item = new HBox(10);
|
HBox item = new HBox(10);
|
||||||
item.getStyleClass().add("contact-item");
|
item.getStyleClass().add("contact-item");
|
||||||
|
|
||||||
// آواتار
|
// Avatar
|
||||||
ImageView avatar = new ImageView(loadAvatar(c.getImageUrl()));
|
ImageView avatar = new ImageView(loadAvatar(c.getImageUrl()));
|
||||||
avatar.setFitWidth(48);
|
avatar.setFitWidth(48);
|
||||||
avatar.setFitHeight(48);
|
avatar.setFitHeight(48);
|
||||||
avatar.setPreserveRatio(true);
|
avatar.setPreserveRatio(true);
|
||||||
|
|
||||||
|
// Details (name + status)
|
||||||
VBox details = new VBox(2);
|
VBox details = new VBox(2);
|
||||||
Label nameLabel = new Label(c.getName());
|
Label nameLabel = new Label(c.getName());
|
||||||
nameLabel.getStyleClass().add("contact-name");
|
nameLabel.getStyleClass().add("contact-name");
|
||||||
@@ -162,28 +165,27 @@ public class AddMembersController {
|
|||||||
Region spacer = new Region();
|
Region spacer = new Region();
|
||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
CheckBox cb = new CheckBox();
|
// Add elements (no checkbox here)
|
||||||
cb.setSelected(selectedContacts.contains(c));
|
item.getChildren().addAll(avatar, details, spacer);
|
||||||
cb.selectedProperty().addListener((obs, ov, nv) -> {
|
|
||||||
if (nv) selectedContacts.add(c); else selectedContacts.remove(c);
|
// Highlight if already selected
|
||||||
|
if (selectedContacts.contains(c)) {
|
||||||
|
item.getStyleClass().add("contact-selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle selection by clicking the whole row
|
||||||
|
item.setOnMouseClicked(e -> {
|
||||||
|
if (selectedContacts.contains(c)) {
|
||||||
|
selectedContacts.remove(c);
|
||||||
|
item.getStyleClass().remove("contact-selected");
|
||||||
|
} else {
|
||||||
|
selectedContacts.add(c);
|
||||||
|
item.getStyleClass().add("contact-selected");
|
||||||
|
}
|
||||||
updateMemberCount();
|
updateMemberCount();
|
||||||
updateSelectedMembersPane();
|
updateSelectedMembersPane();
|
||||||
// برای هایلایت
|
|
||||||
if (nv) item.getStyleClass().add("contact-selected");
|
|
||||||
else item.getStyleClass().remove("contact-selected");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
item.getChildren().addAll(avatar, details, spacer, cb);
|
|
||||||
|
|
||||||
// کلیک روی ردیف = toggle
|
|
||||||
item.setOnMouseClicked(e -> {
|
|
||||||
boolean newVal = !cb.isSelected();
|
|
||||||
cb.setSelected(newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
// هایلایت انتخابشده
|
|
||||||
if (selectedContacts.contains(c)) item.getStyleClass().add("contact-selected");
|
|
||||||
|
|
||||||
contactsList.getChildren().add(item);
|
contactsList.getChildren().add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,8 +106,10 @@ public class AddSubscriberController {
|
|||||||
String name = c.optString("profile_name", "");
|
String name = c.optString("profile_name", "");
|
||||||
String id = c.optString("contact_id", ""); // internal_uuid
|
String id = c.optString("contact_id", ""); // internal_uuid
|
||||||
if (id.isBlank()) continue;
|
if (id.isBlank()) continue;
|
||||||
String imageUrl = c.optString("image_url",
|
String imageUrl = c.optString("image_url", "").trim();
|
||||||
"/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
if (imageUrl.isEmpty() || imageUrl.equals("null")) {
|
||||||
|
imageUrl = "/org/to/telegramfinalproject/Avatars/default_user_profile.png";
|
||||||
|
}
|
||||||
String status = Optional.ofNullable(c.optString("last_seen", ""))
|
String status = Optional.ofNullable(c.optString("last_seen", ""))
|
||||||
.filter(s -> !s.isBlank()).map(s -> "last seen " + s).orElse("");
|
.filter(s -> !s.isBlank()).map(s -> "last seen " + s).orElse("");
|
||||||
allContacts.add(new Contact(id, name, status, imageUrl));
|
allContacts.add(new Contact(id, name, status, imageUrl));
|
||||||
@@ -142,11 +144,13 @@ public class AddSubscriberController {
|
|||||||
HBox item = new HBox(10);
|
HBox item = new HBox(10);
|
||||||
item.getStyleClass().add("contact-item");
|
item.getStyleClass().add("contact-item");
|
||||||
|
|
||||||
|
// Avatar
|
||||||
ImageView avatar = new ImageView(loadAvatar(c.getImageUrl()));
|
ImageView avatar = new ImageView(loadAvatar(c.getImageUrl()));
|
||||||
avatar.setFitWidth(48);
|
avatar.setFitWidth(48);
|
||||||
avatar.setFitHeight(48);
|
avatar.setFitHeight(48);
|
||||||
avatar.setPreserveRatio(true);
|
avatar.setPreserveRatio(true);
|
||||||
|
|
||||||
|
// Details
|
||||||
VBox details = new VBox(2);
|
VBox details = new VBox(2);
|
||||||
Label nameLabel = new Label(c.getName());
|
Label nameLabel = new Label(c.getName());
|
||||||
nameLabel.getStyleClass().add("contact-name");
|
nameLabel.getStyleClass().add("contact-name");
|
||||||
@@ -157,22 +161,27 @@ public class AddSubscriberController {
|
|||||||
Region spacer = new Region();
|
Region spacer = new Region();
|
||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
CheckBox cb = new CheckBox();
|
// Add elements (no checkbox)
|
||||||
cb.setSelected(selected.contains(c));
|
item.getChildren().addAll(avatar, details, spacer);
|
||||||
cb.selectedProperty().addListener((obs, ov, nv) -> {
|
|
||||||
if (nv) selected.add(c); else selected.remove(c);
|
// Highlight if already selected
|
||||||
|
if (selected.contains(c)) {
|
||||||
|
item.getStyleClass().add("contact-selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle selection by clicking the row
|
||||||
|
item.setOnMouseClicked(e -> {
|
||||||
|
if (selected.contains(c)) {
|
||||||
|
selected.remove(c);
|
||||||
|
item.getStyleClass().remove("contact-selected");
|
||||||
|
} else {
|
||||||
|
selected.add(c);
|
||||||
|
item.getStyleClass().add("contact-selected");
|
||||||
|
}
|
||||||
updateCount();
|
updateCount();
|
||||||
updateSelectedPane();
|
updateSelectedPane();
|
||||||
if (nv) item.getStyleClass().add("contact-selected");
|
|
||||||
else item.getStyleClass().remove("contact-selected");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
item.getChildren().addAll(avatar, details, spacer, cb);
|
|
||||||
|
|
||||||
item.setOnMouseClicked(e -> cb.setSelected(!cb.isSelected()));
|
|
||||||
|
|
||||||
if (selected.contains(c)) item.getStyleClass().add("contact-selected");
|
|
||||||
|
|
||||||
contactsList.getChildren().add(item);
|
contactsList.getChildren().add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
|
||||||
|
public class BlockedUsersController {
|
||||||
|
|
||||||
|
@FXML private Pane overlayBackground;
|
||||||
|
@FXML private Button backButton;
|
||||||
|
@FXML private Button closeButton;
|
||||||
|
@FXML private Label countLabel;
|
||||||
|
@FXML private ListView<HBox> blockedList;
|
||||||
|
@FXML private VBox blockedCard;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
// Back button icon
|
||||||
|
backButton.setGraphic(makeIcon("/org/to/telegramfinalproject/Icons/back_button_dark.png"));
|
||||||
|
|
||||||
|
// Example blocked users (replace with server data)
|
||||||
|
addBlockedUser("Asal", "@Whales_suicide", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Replies", "@replies", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Asal", "@Whales_suicide", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Replies", "@replies", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Asal", "@Whales_suicide", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Replies", "@replies", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Asal", "@Whales_suicide", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Replies", "@replies", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Asal", "@Whales_suicide", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
addBlockedUser("Replies", "@replies", "/org/to/telegramfinalproject/Avatars/default_user_profile.png");
|
||||||
|
|
||||||
|
updateCount();
|
||||||
|
|
||||||
|
closeButton.setOnAction(e -> MainController.getInstance().closeOverlay(overlayBackground.getParent()));
|
||||||
|
backButton.setOnAction(e -> MainController.getInstance().goBack((StackPane) overlayBackground.getParent()));
|
||||||
|
|
||||||
|
overlayBackground.setOnMouseClicked(e -> MainController.getInstance().closeOverlay(blockedCard.getParent()));
|
||||||
|
|
||||||
|
// Register scene for ThemeManager → stylesheet swap will handle colors/icons
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (blockedCard.getScene() != null) {
|
||||||
|
ThemeManager.getInstance().registerScene(blockedCard.getScene());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listener for theme change
|
||||||
|
ThemeManager.getInstance().darkModeProperty().addListener((obs, oldVal, newVal) -> {
|
||||||
|
updateBackIcon(newVal);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set initial state
|
||||||
|
updateBackIcon(ThemeManager.getInstance().isDarkMode());
|
||||||
|
|
||||||
|
// Add CSS
|
||||||
|
blockedList.getStylesheets().add(
|
||||||
|
getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait until skin is ready
|
||||||
|
blockedList.skinProperty().addListener((obs, oldSkin, newSkin) -> {
|
||||||
|
if (newSkin != null) {
|
||||||
|
// Look up vertical scrollbar inside the ListView
|
||||||
|
ScrollBar vBar = (ScrollBar) blockedList.lookup(".scroll-bar:vertical");
|
||||||
|
if (vBar != null) {
|
||||||
|
blockedList.setOnScroll(event -> {
|
||||||
|
double deltaY = event.getDeltaY() * 0.003; // smaller = smoother
|
||||||
|
double newValue = vBar.getValue() - deltaY;
|
||||||
|
vBar.setValue(Math.max(0, Math.min(newValue, 1))); // clamp between 0–1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBlockedUser(String name, String id, String avatarPath) {
|
||||||
|
// Avatar
|
||||||
|
ImageView avatar = new ImageView(new Image(getClass().getResourceAsStream(avatarPath)));
|
||||||
|
avatar.setFitWidth(60);
|
||||||
|
avatar.setFitHeight(60);
|
||||||
|
avatar.setPreserveRatio(true);
|
||||||
|
avatar.setSmooth(true);
|
||||||
|
avatar.setCache(true);
|
||||||
|
|
||||||
|
// round mask
|
||||||
|
Circle clip = new Circle(20, 20, 40); // x, y, radius (same as half of fit size)
|
||||||
|
avatar.setClip(clip);
|
||||||
|
|
||||||
|
// Name + ID
|
||||||
|
VBox info = new VBox(2);
|
||||||
|
Label nameLabel = new Label(name);
|
||||||
|
nameLabel.getStyleClass().add("blocked-name");
|
||||||
|
Label idLabel = new Label(id);
|
||||||
|
idLabel.getStyleClass().add("blocked-id");
|
||||||
|
info.getChildren().addAll(nameLabel, idLabel);
|
||||||
|
|
||||||
|
// Unblock button
|
||||||
|
Button unblockBtn = new Button("Unblock");
|
||||||
|
unblockBtn.getStyleClass().add("unblock-button");
|
||||||
|
unblockBtn.setOnAction(e -> {
|
||||||
|
blockedList.getItems().removeIf(h -> h.getChildren().contains(avatar));
|
||||||
|
updateCount();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Row
|
||||||
|
HBox row = new HBox(10, avatar, info, unblockBtn);
|
||||||
|
row.setSpacing(12);
|
||||||
|
row.getStyleClass().add("blocked-row");
|
||||||
|
HBox.setHgrow(info, javafx.scene.layout.Priority.ALWAYS);
|
||||||
|
|
||||||
|
blockedList.getItems().add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCount() {
|
||||||
|
countLabel.setText(blockedList.getItems().size() + " blocked users");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBackIcon(boolean darkMode) {
|
||||||
|
String iconPath = darkMode
|
||||||
|
? "/org/to/telegramfinalproject/Icons/back_button_light.png"
|
||||||
|
: "/org/to/telegramfinalproject/Icons/back_button_dark.png";
|
||||||
|
|
||||||
|
ImageView icon = new ImageView(new Image(getClass().getResourceAsStream(iconPath)));
|
||||||
|
icon.setFitWidth(16);
|
||||||
|
icon.setFitHeight(16);
|
||||||
|
backButton.setGraphic(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView makeIcon(String path) {
|
||||||
|
ImageView iv = new ImageView(new Image(getClass().getResourceAsStream(path)));
|
||||||
|
iv.setFitWidth(20);
|
||||||
|
iv.setFitHeight(20);
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
public class ChangeCredentialsController {
|
||||||
|
|
||||||
|
@FXML private VBox credentialsCard;
|
||||||
|
@FXML private Pane overlayBackground;
|
||||||
|
@FXML private TextField usernameField;
|
||||||
|
@FXML private Label usernameLabel;
|
||||||
|
|
||||||
|
@FXML private Label passwordLabel;
|
||||||
|
@FXML private PasswordField passwordField;
|
||||||
|
@FXML private TextField visiblePasswordField;
|
||||||
|
@FXML private Button togglePasswordBtn;
|
||||||
|
|
||||||
|
@FXML private Label confirmPasswordLabel;
|
||||||
|
@FXML private PasswordField confirmPasswordField;
|
||||||
|
@FXML private TextField visibleConfirmPasswordField;
|
||||||
|
@FXML private Button toggleConfirmBtn;
|
||||||
|
|
||||||
|
@FXML private Button cancelButton;
|
||||||
|
@FXML private Button saveButton;
|
||||||
|
@FXML private Button closeButton;
|
||||||
|
|
||||||
|
private boolean passwordVisible = false;
|
||||||
|
private boolean confirmVisible = false;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
// Toggle password visibility
|
||||||
|
togglePasswordBtn.setOnAction(e -> togglePasswordVisibility());
|
||||||
|
toggleConfirmBtn.setOnAction(e -> toggleConfirmVisibility());
|
||||||
|
|
||||||
|
saveButton.setOnAction(e -> validateAndSave());
|
||||||
|
|
||||||
|
// Close the overlay
|
||||||
|
closeButton.setOnAction(e ->
|
||||||
|
MainController.getInstance().closeOverlay(credentialsCard.getParent()));
|
||||||
|
cancelButton.setOnAction(e ->
|
||||||
|
MainController.getInstance().closeOverlay(credentialsCard.getParent()));
|
||||||
|
overlayBackground.setOnMouseClicked(e ->
|
||||||
|
MainController.getInstance().closeOverlay(credentialsCard.getParent())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset error when typing
|
||||||
|
usernameField.textProperty().addListener((obs, o, n) -> removeError(usernameField, usernameLabel));
|
||||||
|
|
||||||
|
passwordField.textProperty().addListener((obs, o, n) -> {
|
||||||
|
visiblePasswordField.setText(n); // keep synced
|
||||||
|
removeError(passwordField, passwordLabel);
|
||||||
|
removeError(visiblePasswordField, passwordLabel);
|
||||||
|
});
|
||||||
|
visiblePasswordField.textProperty().addListener((obs, o, n) -> {
|
||||||
|
passwordField.setText(n);
|
||||||
|
removeError(passwordField, passwordLabel);
|
||||||
|
removeError(visiblePasswordField, passwordLabel);
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmPasswordField.textProperty().addListener((obs, o, n) -> {
|
||||||
|
visibleConfirmPasswordField.setText(n);
|
||||||
|
removeError(confirmPasswordField, confirmPasswordLabel);
|
||||||
|
removeError(visibleConfirmPasswordField, confirmPasswordLabel);
|
||||||
|
});
|
||||||
|
visibleConfirmPasswordField.textProperty().addListener((obs, o, n) -> {
|
||||||
|
confirmPasswordField.setText(n);
|
||||||
|
removeError(confirmPasswordField, confirmPasswordLabel);
|
||||||
|
removeError(visibleConfirmPasswordField, confirmPasswordLabel);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto_focus password field when overlay opens
|
||||||
|
Platform.runLater(() -> usernameField.requestFocus());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void togglePasswordVisibility() {
|
||||||
|
passwordVisible = !passwordVisible;
|
||||||
|
visiblePasswordField.setText(passwordField.getText());
|
||||||
|
visiblePasswordField.setVisible(passwordVisible);
|
||||||
|
visiblePasswordField.setManaged(passwordVisible);
|
||||||
|
|
||||||
|
passwordField.setVisible(!passwordVisible);
|
||||||
|
passwordField.setManaged(!passwordVisible);
|
||||||
|
|
||||||
|
togglePasswordBtn.setText(passwordVisible ? "👁" : "👁");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleConfirmVisibility() {
|
||||||
|
confirmVisible = !confirmVisible;
|
||||||
|
visibleConfirmPasswordField.setText(confirmPasswordField.getText());
|
||||||
|
visibleConfirmPasswordField.setVisible(confirmVisible);
|
||||||
|
visibleConfirmPasswordField.setManaged(confirmVisible);
|
||||||
|
|
||||||
|
confirmPasswordField.setVisible(!confirmVisible);
|
||||||
|
confirmPasswordField.setManaged(!confirmVisible);
|
||||||
|
|
||||||
|
toggleConfirmBtn.setText(confirmVisible ? "👁" : "👁");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateAndSave() {
|
||||||
|
boolean hasError = false;
|
||||||
|
|
||||||
|
// --- Username check ---
|
||||||
|
if (usernameField.getText().trim().isEmpty()) {
|
||||||
|
addError(usernameField, usernameLabel);
|
||||||
|
hasError = true;
|
||||||
|
} else {
|
||||||
|
removeError(usernameField, usernameLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Password check ---
|
||||||
|
String pwd = passwordVisible ? visiblePasswordField.getText().trim() : passwordField.getText().trim();
|
||||||
|
if (pwd.isEmpty()) {
|
||||||
|
addError(passwordField, passwordLabel);
|
||||||
|
addError(visiblePasswordField, passwordLabel);
|
||||||
|
hasError = true;
|
||||||
|
} else {
|
||||||
|
removeError(passwordField, passwordLabel);
|
||||||
|
removeError(visiblePasswordField, passwordLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Confirm password check ---
|
||||||
|
String confirm = confirmVisible ? visibleConfirmPasswordField.getText().trim() : confirmPasswordField.getText().trim();
|
||||||
|
if (confirm.isEmpty()) {
|
||||||
|
addError(confirmPasswordField, confirmPasswordLabel);
|
||||||
|
addError(visibleConfirmPasswordField, confirmPasswordLabel);
|
||||||
|
hasError = true;
|
||||||
|
} else if (!confirm.equals(pwd)) {
|
||||||
|
addError(confirmPasswordField, confirmPasswordLabel);
|
||||||
|
addError(visibleConfirmPasswordField, confirmPasswordLabel);
|
||||||
|
hasError = true;
|
||||||
|
} else {
|
||||||
|
removeError(confirmPasswordField, confirmPasswordLabel);
|
||||||
|
removeError(visibleConfirmPasswordField, confirmPasswordLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError) return;
|
||||||
|
|
||||||
|
// TODO: send to server
|
||||||
|
System.out.println("Saving new credentials...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addError(TextField field, Label label) {
|
||||||
|
if (!field.getStyleClass().contains("error-input")) {
|
||||||
|
field.getStyleClass().add("error-input");
|
||||||
|
}
|
||||||
|
if (!label.getStyleClass().contains("error-label")) {
|
||||||
|
label.getStyleClass().add("error-label");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeError(TextField field, Label label) {
|
||||||
|
field.getStyleClass().remove("error-input");
|
||||||
|
label.getStyleClass().remove("error-label");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,16 @@ package org.to.telegramfinalproject.UI;
|
|||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class CheckPasswordController {
|
public class CheckPasswordController {
|
||||||
|
|
||||||
@FXML private VBox checkPasswordCard;
|
@FXML private VBox checkPasswordCard;
|
||||||
@@ -103,9 +107,8 @@ public class CheckPasswordController {
|
|||||||
visiblePasswordField.getStyleClass().remove("error");
|
visiblePasswordField.getStyleClass().remove("error");
|
||||||
passwordLabel.getStyleClass().remove("error");
|
passwordLabel.getStyleClass().remove("error");
|
||||||
|
|
||||||
// TODO: go to change credentials scene
|
// Go to the next scene
|
||||||
System.out.println("Password OK → go to next scene");
|
openChangeCredentials();
|
||||||
// MainController.getInstance().showOverlay("/org/to/telegramfinalproject/Fxml/change_credentials.fxml");
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Error → add error styles
|
// Error → add error styles
|
||||||
@@ -142,4 +145,18 @@ public class CheckPasswordController {
|
|||||||
icon.setFitHeight(16);
|
icon.setFitHeight(16);
|
||||||
backButton.setGraphic(icon);
|
backButton.setGraphic(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openChangeCredentials() {
|
||||||
|
try {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||||
|
"/org/to/telegramfinalproject/Fxml/change_credentials.fxml"));
|
||||||
|
Node overlay = loader.load();
|
||||||
|
|
||||||
|
// Show new overlay
|
||||||
|
MainController.getInstance().showOverlay(overlay);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.to.telegramfinalproject.UI;
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
@@ -13,6 +15,10 @@ import javafx.scene.layout.Pane;
|
|||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||||
|
import org.to.telegramfinalproject.Client.Session;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -43,11 +49,18 @@ public class EditProfileController {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// close on background click
|
// close on background click
|
||||||
overlayBackground.setOnMouseClicked(e -> closeEdit());
|
overlayBackground.setOnMouseClicked(e -> {
|
||||||
|
saveChangesAndClose();
|
||||||
|
closeEdit();
|
||||||
|
});
|
||||||
|
|
||||||
closeButton.setOnAction(e -> closeEdit());
|
closeButton.setOnAction(e -> {
|
||||||
|
saveChangesAndClose();
|
||||||
|
closeEdit();
|
||||||
|
});
|
||||||
|
|
||||||
backButton.setOnAction(e -> {
|
backButton.setOnAction(e -> {
|
||||||
|
saveChangesAndClose();
|
||||||
MainController.getInstance().goBack(overlayBackground);
|
MainController.getInstance().goBack(overlayBackground);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,4 +125,78 @@ public class EditProfileController {
|
|||||||
usernameField.setText(userId);
|
usernameField.setText(userId);
|
||||||
profileImageView.setImage(profileImage);
|
profileImageView.setImage(profileImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveChangesAndClose() {
|
||||||
|
JSONObject currentUser = Session.currentUser;
|
||||||
|
|
||||||
|
String newBio = bioField.getText().trim();
|
||||||
|
String newName = nameField.getText().trim();
|
||||||
|
String newUserId = usernameField.getText().trim();
|
||||||
|
|
||||||
|
boolean anyError = false;
|
||||||
|
|
||||||
|
// --- Update bio ---
|
||||||
|
String oldBio = currentUser.optString("bio", "");
|
||||||
|
if (!newBio.equals(oldBio)) {
|
||||||
|
JSONObject req = new JSONObject().put("action", "edit_bio").put("new_bio", newBio);
|
||||||
|
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||||
|
if (resp == null || !"success".equals(resp.optString("status"))) {
|
||||||
|
anyError = true;
|
||||||
|
showAlert("Error", resp != null ? resp.optString("message", "Unknown error") : "Unknown error", Alert.AlertType.ERROR);
|
||||||
|
} else {
|
||||||
|
currentUser.put("bio", newBio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Update profile name ---
|
||||||
|
String oldName = currentUser.optString("profile_name", "");
|
||||||
|
if (!newName.equals(oldName)) {
|
||||||
|
JSONObject req = new JSONObject().put("action", "edit_profile_name").put("new_profile_name", newName);
|
||||||
|
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||||
|
if (resp == null || !"success".equals(resp.optString("status"))) {
|
||||||
|
anyError = true;
|
||||||
|
showAlert("Error", resp != null ? resp.optString("message", "Unknown error") : "Unknown error", Alert.AlertType.ERROR);
|
||||||
|
} else {
|
||||||
|
currentUser.put("profile_name", newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Update user ID (username) ---
|
||||||
|
String oldUserId = currentUser.optString("user_id", "");
|
||||||
|
if (!newUserId.equals(oldUserId)) {
|
||||||
|
JSONObject req = new JSONObject().put("action", "edit_user_id").put("new_user_id", newUserId);
|
||||||
|
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||||
|
if (resp == null || !"success".equals(resp.optString("status"))) {
|
||||||
|
anyError = true;
|
||||||
|
showAlert("Error", resp != null ? resp.optString("message", "Unknown error") : "Unknown error", Alert.AlertType.ERROR);
|
||||||
|
} else {
|
||||||
|
currentUser.put("user_id", newUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyError) {
|
||||||
|
MyProfileController.getInstance().setProfileData(
|
||||||
|
currentUser.optString("profile_name"),
|
||||||
|
"online",
|
||||||
|
currentUser.optString("bio"),
|
||||||
|
currentUser.optString("user_id"),
|
||||||
|
currentUser.optString("image_url", "/org/to/telegramfinalproject/Avatars/default_user_profile.png")
|
||||||
|
);
|
||||||
|
MainController.getInstance().closeOverlay(bioField.getParent().getParent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(String title, String message, Alert.AlertType type) {
|
||||||
|
Alert alert = new Alert(type);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(null); // no big header, just the message
|
||||||
|
alert.setContentText(message);
|
||||||
|
|
||||||
|
// optional: style it to fit your dark/light theme
|
||||||
|
if (alert.getDialogPane().getScene() != null) {
|
||||||
|
ThemeManager.getInstance().registerScene(alert.getDialogPane().getScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.to.telegramfinalproject.UI;
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.animation.TranslateTransition;
|
import javafx.animation.TranslateTransition;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
@@ -605,6 +606,9 @@ public class MainController {
|
|||||||
overlayLayer.getChildren().remove(current);
|
overlayLayer.getChildren().remove(current);
|
||||||
}
|
}
|
||||||
overlayLayer.getChildren().add(overlayNode);
|
overlayLayer.getChildren().add(overlayNode);
|
||||||
|
|
||||||
|
// Automatically close sidebar
|
||||||
|
closeSidebar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeOverlay(Node overlayNode) {
|
public void closeOverlay(Node overlayNode) {
|
||||||
@@ -640,6 +644,7 @@ public class MainController {
|
|||||||
|
|
||||||
private enum SRType { USER, GROUP, CHANNEL, MESSAGE }
|
private enum SRType { USER, GROUP, CHANNEL, MESSAGE }
|
||||||
|
|
||||||
|
// Inner class
|
||||||
private static class SearchResult {
|
private static class SearchResult {
|
||||||
final SRType type;
|
final SRType type;
|
||||||
final String title; // name / sender_name / ...
|
final String title; // name / sender_name / ...
|
||||||
@@ -1133,8 +1138,4 @@ public class MainController {
|
|||||||
getChatPageController().showChat(entry);
|
getChatPageController().showChat(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,14 @@ public class MyProfileController {
|
|||||||
@FXML private VBox profileCard;
|
@FXML private VBox profileCard;
|
||||||
@FXML private VBox bioBlock;
|
@FXML private VBox bioBlock;
|
||||||
|
|
||||||
private final ThemeManager themeManager = ThemeManager.getInstance();
|
private String userID;
|
||||||
|
private static MyProfileController instance;
|
||||||
|
|
||||||
|
public MyProfileController() {
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MyProfileController getInstance() { return instance;}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
@@ -77,11 +84,12 @@ public class MyProfileController {
|
|||||||
bioBlock.setManaged(false);
|
bioBlock.setManaged(false);
|
||||||
} else {
|
} else {
|
||||||
userBio.setText(bio);
|
userBio.setText(bio);
|
||||||
userBio.setVisible(true);
|
bioBlock.setVisible(true);
|
||||||
userBio.setManaged(true);
|
bioBlock.setManaged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userId.setText(userId);
|
this.userId.setText("@" + userId);
|
||||||
|
userID = userId;
|
||||||
|
|
||||||
if (imageUrl != null && !imageUrl.isEmpty()) {
|
if (imageUrl != null && !imageUrl.isEmpty()) {
|
||||||
profileImage.setImage(new Image(imageUrl));
|
profileImage.setImage(new Image(imageUrl));
|
||||||
@@ -111,8 +119,8 @@ public class MyProfileController {
|
|||||||
controller.setProfileData(
|
controller.setProfileData(
|
||||||
profileName.getText(),
|
profileName.getText(),
|
||||||
profileStatus.getText(),
|
profileStatus.getText(),
|
||||||
userBio.isVisible() ? userBio.getText() : null,
|
bioBlock.isVisible() ? userBio.getText() : null,
|
||||||
userId.getText(),
|
userID,
|
||||||
profileImage.getImage()
|
profileImage.getImage()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,17 @@ public class PrivacySecurityController {
|
|||||||
|
|
||||||
// Blocked users management
|
// Blocked users management
|
||||||
blockedUsersButton.setOnAction(e -> {
|
blockedUsersButton.setOnAction(e -> {
|
||||||
// TODO: open Blocked Users scene
|
try {
|
||||||
System.out.println("Open blocked users management");
|
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||||
|
"/org/to/telegramfinalproject/Fxml/blocked_users.fxml"));
|
||||||
|
Node overlay = loader.load();
|
||||||
|
|
||||||
|
// Show new overlay
|
||||||
|
MainController.getInstance().showOverlay(overlay);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close
|
// Close
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package org.to.telegramfinalproject.UI;
|
package org.to.telegramfinalproject.UI;
|
||||||
|
|
||||||
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.animation.TranslateTransition;
|
import javafx.animation.TranslateTransition;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
@@ -15,6 +17,7 @@ import javafx.scene.layout.Region;
|
|||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||||
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||||
|
|
||||||
|
|
||||||
@@ -182,17 +185,42 @@ public class SidebarMenuController {
|
|||||||
// Sidebar actions
|
// Sidebar actions
|
||||||
private void openMyProfile() {
|
private void openMyProfile() {
|
||||||
try {
|
try {
|
||||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/my_profile.fxml"));
|
// 1. Ask server for profile info
|
||||||
|
JSONObject request = new JSONObject().put("action", "get_user_profile");
|
||||||
|
JSONObject response = ActionHandler.sendWithResponse(request);
|
||||||
|
|
||||||
|
if (response == null || !"success".equals(response.optString("status"))) {
|
||||||
|
showAlert("Error", "Failed to load profile", Alert.AlertType.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject profile = response.optJSONObject("data");
|
||||||
|
if (profile == null) {
|
||||||
|
showAlert("Error", "Malformed profile data", Alert.AlertType.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract fields
|
||||||
|
String profileName = profile.optString("profile_name", "Unknown");
|
||||||
|
String bio = profile.optString("bio", "");
|
||||||
|
String userId = profile.optString("user_id", "");
|
||||||
|
String imageUrl = profile.optString("profile_picture_url", null);
|
||||||
|
|
||||||
|
// 2. Load overlay FXML
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||||
|
"/org/to/telegramfinalproject/Fxml/my_profile.fxml"));
|
||||||
Node profileOverlay = loader.load();
|
Node profileOverlay = loader.load();
|
||||||
|
|
||||||
|
// 3. Pass real data to controller
|
||||||
MyProfileController controller = loader.getController();
|
MyProfileController controller = loader.getController();
|
||||||
controller.setProfileData("Asal", "online", "Pass me that lovely little gun♡.", "@Whales_suicide", null);
|
controller.setProfileData(profileName, "online", bio, userId, imageUrl);
|
||||||
|
|
||||||
// Show the overlay on top of mainRoot
|
// 4. Show overlay
|
||||||
MainController.getInstance().showOverlay(profileOverlay);
|
MainController.getInstance().showOverlay(profileOverlay);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
showAlert("Error", "Error opening profile", Alert.AlertType.ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,5 +337,17 @@ public class SidebarMenuController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showAlert(String title, String message, Alert.AlertType type) {
|
||||||
|
Alert alert = new Alert(type);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(null); // no big header, just the message
|
||||||
|
alert.setContentText(message);
|
||||||
|
|
||||||
|
// optional: style it to fit your dark/light theme
|
||||||
|
if (alert.getDialogPane().getScene() != null) {
|
||||||
|
ThemeManager.getInstance().registerScene(alert.getDialogPane().getScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* دقیقاً فقط روی این دو دکمه اعمال میشود */
|
|
||||||
#joinAction, #addContactAction {
|
#joinAction, #addContactAction {
|
||||||
-fx-background-color: transparent, transparent, transparent, transparent;
|
-fx-background-color: transparent, transparent, transparent, transparent;
|
||||||
-fx-background-insets: 0,0,0,0;
|
-fx-background-insets: 0,0,0,0;
|
||||||
|
|||||||
@@ -989,3 +989,93 @@
|
|||||||
-fx-padding: 0 6 0 6;
|
-fx-padding: 0 6 0 6;
|
||||||
-fx-font-size: 14px;
|
-fx-font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blocked-card {
|
||||||
|
-fx-background-color: #1f2a36;
|
||||||
|
-fx-background-radius: 10;
|
||||||
|
-fx-padding: 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-header {
|
||||||
|
-fx-padding: 8;
|
||||||
|
}
|
||||||
|
.blocked-title {
|
||||||
|
-fx-font-size: 16px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-count {
|
||||||
|
-fx-font-size: 13px;
|
||||||
|
-fx-text-fill: #999;
|
||||||
|
-fx-padding: 4 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-list {
|
||||||
|
-fx-background-color: #253544;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-row {
|
||||||
|
-fx-padding: 8;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
.blocked-row:hover {
|
||||||
|
-fx-background-color: #253544;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-name {
|
||||||
|
-fx-font-size: 13px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
.blocked-id {
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-text-fill: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unblock-button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: #3a86ff;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unblock-button:hover {
|
||||||
|
-fx-text-fill: #66aaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-info-label {
|
||||||
|
-fx-background-color: #2c3e50; /* darker banner background */
|
||||||
|
-fx-text-fill: #bbb; /* lighter muted text */
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-wrap-text: true;
|
||||||
|
-fx-padding: 5;
|
||||||
|
-fx-background-radius: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-list .thumb {
|
||||||
|
-fx-background-color: rgba(200, 200, 200, 0.5); /* lighter for dark bg */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each list cell */
|
||||||
|
.blocked-list .list-cell {
|
||||||
|
-fx-background-color: #1f2a36;
|
||||||
|
-fx-text-fill: #eeeeee;
|
||||||
|
-fx-padding: 8 12;
|
||||||
|
-fx-border-color: #2f2f2f;
|
||||||
|
-fx-border-width: 0 0 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-list .list-cell:hover {
|
||||||
|
-fx-background-color: #2a3a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error label */
|
||||||
|
.label.error-label {
|
||||||
|
-fx-text-fill: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error input */
|
||||||
|
.error-input {
|
||||||
|
-fx-border-color: red;
|
||||||
|
-fx-border-radius: 6;
|
||||||
|
}
|
||||||
|
|||||||
@@ -989,3 +989,84 @@
|
|||||||
-fx-padding: 0 6 0 6;
|
-fx-padding: 0 6 0 6;
|
||||||
-fx-font-size: 14px;
|
-fx-font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Blocked Users card */
|
||||||
|
.blocked-card {
|
||||||
|
-fx-background-color: white;
|
||||||
|
-fx-background-radius: 10;
|
||||||
|
-fx-padding: 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.blocked-header {
|
||||||
|
-fx-padding: 8;
|
||||||
|
}
|
||||||
|
.blocked-title {
|
||||||
|
-fx-font-size: 16px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-text-fill: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Count */
|
||||||
|
.blocked-count {
|
||||||
|
-fx-font-size: 13px;
|
||||||
|
-fx-text-fill: #888;
|
||||||
|
-fx-padding: 4 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List */
|
||||||
|
.blocked-list {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Row */
|
||||||
|
.blocked-row {
|
||||||
|
-fx-padding: 8;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
.blocked-row:hover {
|
||||||
|
-fx-background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels */
|
||||||
|
.blocked-name {
|
||||||
|
-fx-font-size: 13px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-text-fill: black;
|
||||||
|
}
|
||||||
|
.blocked-id {
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-text-fill: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button */
|
||||||
|
.unblock-button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: #2196f3;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unblock-button:hover {
|
||||||
|
-fx-text-fill: #66aaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-info-label {
|
||||||
|
-fx-background-color: #f5f5f5; /* light gray background */
|
||||||
|
-fx-text-fill: #555; /* muted text */
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-wrap-text: true;
|
||||||
|
-fx-padding: 5; /* space inside like a banner */
|
||||||
|
-fx-background-radius: 4; /* soft rounded edges */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error label */
|
||||||
|
.label.error-label {
|
||||||
|
-fx-text-fill: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error input */
|
||||||
|
.error-input {
|
||||||
|
-fx-border-color: red;
|
||||||
|
-fx-border-radius: 6;
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,3 +20,20 @@
|
|||||||
-fx-background-color: rgba(100, 100, 100, 0.5); /* Semi-transparent thumb */
|
-fx-background-color: rgba(100, 100, 100, 0.5); /* Semi-transparent thumb */
|
||||||
-fx-background-radius: 3px;
|
-fx-background-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.blocked-list .scroll-bar {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-opacity: 0;
|
||||||
|
-fx-pref-width: 6px;
|
||||||
|
-fx-background-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-list:hover .scroll-bar {
|
||||||
|
-fx-opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocked-list .thumb {
|
||||||
|
-fx-background-color: rgba(100, 100, 100, 0.5);
|
||||||
|
-fx-background-radius: 3px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* رنگها */
|
/* Colors */
|
||||||
:root {
|
:root {
|
||||||
-tg-bg: #ffffff;
|
-tg-bg: #ffffff;
|
||||||
-tg-sheet: #ffffff;
|
-tg-sheet: #ffffff;
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
|
||||||
|
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.to.telegramfinalproject.UI.BlockedUsersController"
|
||||||
|
styleClass="overlay-root">
|
||||||
|
|
||||||
|
<!-- Dimmed background -->
|
||||||
|
<Pane fx:id="overlayBackground" styleClass="overlay-background"/>
|
||||||
|
|
||||||
|
<!-- Card -->
|
||||||
|
<VBox fx:id="blockedCard" styleClass="blocked-card" spacing="8"
|
||||||
|
prefWidth="380" maxWidth="380"
|
||||||
|
prefHeight="500" maxHeight="500">
|
||||||
|
|
||||||
|
<!-- ===== Header ===== -->
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="10" styleClass="blocked-header">
|
||||||
|
<Button fx:id="backButton" styleClass="icon-button"/>
|
||||||
|
<Label text="Blocked users" styleClass="blocked-title"/>
|
||||||
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button fx:id="closeButton" styleClass="icon-button" text="✕"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<Separator/>
|
||||||
|
|
||||||
|
<Label text="Blocked users can't send you messages or add you to groups.
|
||||||
|
They will not see your profile photos, online and last seen status."
|
||||||
|
wrapText="true"
|
||||||
|
prefHeight="160"
|
||||||
|
maxHeight="160"
|
||||||
|
styleClass="blocked-info-label"/>
|
||||||
|
|
||||||
|
<!-- ===== List of blocked users ===== -->
|
||||||
|
<Label fx:id="countLabel" text="0 blocked users" styleClass="blocked-count"/>
|
||||||
|
|
||||||
|
<ListView fx:id="blockedList" styleClass="blocked-list" VBox.vgrow="ALWAYS"/>
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
|
||||||
|
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.to.telegramfinalproject.UI.ChangeCredentialsController"
|
||||||
|
styleClass="overlay-root">
|
||||||
|
|
||||||
|
<!-- Dimmed background -->
|
||||||
|
<Pane fx:id="overlayBackground" styleClass="overlay-background"/>
|
||||||
|
|
||||||
|
<!-- Main card -->
|
||||||
|
<VBox fx:id="credentialsCard"
|
||||||
|
styleClass="privacy-card"
|
||||||
|
spacing="16"
|
||||||
|
alignment="TOP_CENTER"
|
||||||
|
prefWidth="380" maxWidth="380"
|
||||||
|
prefHeight="280" maxHeight="280">
|
||||||
|
|
||||||
|
<!-- ===== Header ===== -->
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="10" styleClass="privacy-header">
|
||||||
|
<Label text="Change Credentials" styleClass="privacy-title"/>
|
||||||
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button fx:id="closeButton" styleClass="icon-button" text="✕"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<Separator/>
|
||||||
|
|
||||||
|
<!-- ===== Form ===== -->
|
||||||
|
<VBox spacing="10" alignment="CENTER_LEFT" styleClass="privacy-form">
|
||||||
|
|
||||||
|
<!-- New Username -->
|
||||||
|
<Label fx:id="usernameLabel" text="New Username" styleClass="password-label"/>
|
||||||
|
<TextField fx:id="usernameField"
|
||||||
|
promptText="Enter new username"
|
||||||
|
styleClass="privacy-input"/>
|
||||||
|
|
||||||
|
<!-- New Password -->
|
||||||
|
<Label fx:id="passwordLabel" text="New Password" styleClass="password-label"/>
|
||||||
|
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="4" styleClass="privacy-item">
|
||||||
|
<PasswordField fx:id="passwordField"
|
||||||
|
promptText="Enter new password"
|
||||||
|
styleClass="privacy-input"
|
||||||
|
HBox.hgrow="ALWAYS"/>
|
||||||
|
<TextField fx:id="visiblePasswordField"
|
||||||
|
promptText="Enter new password"
|
||||||
|
styleClass="privacy-input"
|
||||||
|
visible="false" managed="false"
|
||||||
|
HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button fx:id="togglePasswordBtn"
|
||||||
|
text="👁"
|
||||||
|
styleClass="toggle-visibility-btn"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<!-- Confirm Password -->
|
||||||
|
<Label fx:id="confirmPasswordLabel" text="Confirm Password" styleClass="password-label"/>
|
||||||
|
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="4" styleClass="privacy-item">
|
||||||
|
<PasswordField fx:id="confirmPasswordField"
|
||||||
|
promptText="Re-enter new password"
|
||||||
|
styleClass="privacy-input"
|
||||||
|
HBox.hgrow="ALWAYS"/>
|
||||||
|
<TextField fx:id="visibleConfirmPasswordField"
|
||||||
|
promptText="Re-enter new password"
|
||||||
|
styleClass="privacy-input"
|
||||||
|
visible="false" managed="false"
|
||||||
|
HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button fx:id="toggleConfirmBtn"
|
||||||
|
text="👁"
|
||||||
|
styleClass="toggle-visibility-btn"/>
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<!-- ===== Footer ===== -->
|
||||||
|
<HBox alignment="CENTER_RIGHT" spacing="12">
|
||||||
|
<Button fx:id="cancelButton" text="Cancel" styleClass="footer-button"/>
|
||||||
|
<Button fx:id="saveButton" text="Save" styleClass="action-button"/>
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
Reference in New Issue
Block a user