From 4a4e646940162662cfa785dbf3d6d9204617f316 Mon Sep 17 00:00:00 2001 From: Asal Lotfi Date: Mon, 8 Sep 2025 11:55:09 +0330 Subject: [PATCH] Manage Channel UI + backend implemented. --- .../UI/ChannelInfoController.java | 57 ++++- .../UI/ManageChannelAdminsController.java | 228 ++++++++++++++++++ .../UI/ManageChannelController.java | 180 ++++++++++++++ .../UI/ManageSubscribersController.java | 160 ++++++++++++ .../telegramfinalproject/CSS/dark_theme.css | 18 ++ .../telegramfinalproject/CSS/light_theme.css | 16 ++ .../Fxml/manage_channel.fxml | 81 +++++++ .../Fxml/manage_channel_admins.fxml | 43 ++++ .../Fxml/manage_subscribers.fxml | 44 ++++ 9 files changed, 826 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/to/telegramfinalproject/UI/ManageChannelAdminsController.java create mode 100644 src/main/java/org/to/telegramfinalproject/UI/ManageChannelController.java create mode 100644 src/main/java/org/to/telegramfinalproject/UI/ManageSubscribersController.java create mode 100644 src/main/resources/org/to/telegramfinalproject/Fxml/manage_channel.fxml create mode 100644 src/main/resources/org/to/telegramfinalproject/Fxml/manage_channel_admins.fxml create mode 100644 src/main/resources/org/to/telegramfinalproject/Fxml/manage_subscribers.fxml diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java b/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java index 8366f0e..ac5960a 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java @@ -208,7 +208,62 @@ public class ChannelInfoController { } private void openManageChannel() { - // TODO similar to ManageGroupController + new Thread(() -> { + try { + JSONObject req = new JSONObject() + .put("action", "view_channel") + .put("channel_id", channelId.toString()); // ✅ matches server case + + JSONObject resp = ActionHandler.sendWithResponse(req); + + if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) { + Platform.runLater(() -> MainController.getInstance().showAlert( + "Error", + resp != null ? resp.optString("message") : "Server not responding.", + Alert.AlertType.ERROR + )); + return; + } + + JSONObject data = resp.optJSONObject("data"); + if (data == null) { + Platform.runLater(() -> MainController.getInstance().showAlert( + "Error", + "Malformed server response.", + Alert.AlertType.ERROR + )); + return; + } + + Platform.runLater(() -> { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/manage_channel.fxml")); + Node overlay = loader.load(); + + ManageChannelController controller = loader.getController(); + controller.setChannelData(data); // ✅ pass JSON to controller + + MainController.getInstance().showOverlay(overlay); + } catch (IOException e) { + e.printStackTrace(); + MainController.getInstance().showAlert( + "Error", + "Could not load Manage Channel scene.", + Alert.AlertType.ERROR + ); + } + }); + + } catch (Exception e) { + e.printStackTrace(); + Platform.runLater(() -> MainController.getInstance().showAlert( + "Error", + "Error while fetching channel info: " + e.getMessage(), + Alert.AlertType.ERROR + )); + } + }).start(); } private void openAddSubscriberScene() { diff --git a/src/main/java/org/to/telegramfinalproject/UI/ManageChannelAdminsController.java b/src/main/java/org/to/telegramfinalproject/UI/ManageChannelAdminsController.java new file mode 100644 index 0000000..85bec1a --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/ManageChannelAdminsController.java @@ -0,0 +1,228 @@ +package org.to.telegramfinalproject.UI; + +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import org.json.JSONArray; +import org.json.JSONObject; +import org.to.telegramfinalproject.Client.ActionHandler; +import org.to.telegramfinalproject.Client.AvatarLocalResolver; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class ManageChannelAdminsController { + + @FXML private VBox adminsCard; + @FXML private Pane overlayBackground; + @FXML private Button closeButton; + @FXML private Button closeFooterButton; + @FXML private Button addAdminButton; + + @FXML private ScrollPane adminsScroll; + @FXML private VBox adminsList; + + private String channelId; // internal_uuid of channel + + @FXML + private void initialize() { + closeButton.setOnAction(e -> MainController.getInstance().closeOverlay(adminsCard.getParent())); + closeFooterButton.setOnAction(e -> MainController.getInstance().closeOverlay(adminsCard.getParent())); + overlayBackground.setOnMouseClicked(e -> MainController.getInstance().closeOverlay(adminsCard.getParent())); + + //addAdminButton.setOnAction(e -> openAddAdminOverlay()); + + // Smooth scroll feel + adminsScroll.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm()); + adminsScroll.setPannable(true); + adminsScroll.setFitToWidth(true); + adminsScroll.setFitToHeight(false); + adminsScroll.getContent().setOnScroll(event -> { + double deltaY = event.getDeltaY() * 0.003; + adminsScroll.setVvalue(adminsScroll.getVvalue() - deltaY); + }); + } + + public void setChannelData(String channelId, JSONArray admins) { + this.channelId = channelId; + + adminsList.getChildren().clear(); + + // Convert to list for sorting + List adminList = new ArrayList<>(); + for (int i = 0; i < admins.length(); i++) { + adminList.add(admins.getJSONObject(i)); + } + + // Sort: owner first, then admins + adminList.sort((a, b) -> { + String roleA = a.optString("role", "subscriber"); + String roleB = b.optString("role", "subscriber"); + if ("owner".equalsIgnoreCase(roleA) && !"owner".equalsIgnoreCase(roleB)) return -1; + if ("owner".equalsIgnoreCase(roleB) && !"owner".equalsIgnoreCase(roleA)) return 1; + return 0; // keep relative order for admins + }); + + // Add rows + for (JSONObject a : adminList) { + addAdminRow(a); + } + } + + private void addAdminRow(JSONObject a) { + HBox row = new HBox(10); + row.getStyleClass().add("member-row"); + row.setAlignment(Pos.CENTER_LEFT); + + // Avatar + ImageView avatar = new ImageView(); + avatar.setFitWidth(36); + avatar.setFitHeight(36); + avatar.setPreserveRatio(true); + + String imgUrl = a.optString("image_url", ""); + if (!imgUrl.isBlank()) { + Image img = AvatarLocalResolver.load(imgUrl); + if (img != null) avatar.setImage(img); + } else { + avatar.setImage(new Image(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_user_profile.png" + ))); + } + + // Name + Status + VBox details = new VBox(2); + Label name = new Label(a.optString("profile_name", "Unknown")); + name.getStyleClass().add("member-name"); + + Label status = new Label( + a.optBoolean("is_online", false) ? "online" + : "last seen recently" + ); + status.getStyleClass().add("member-status"); + + details.getChildren().addAll(name, status); + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + row.getChildren().addAll(avatar, details, spacer); + + // Remove admin (cannot remove owner) + String role = a.optString("role", "admin"); + if (!"owner".equalsIgnoreCase(role)) { + Button removeBtn = new Button("Remove"); + removeBtn.getStyleClass().add("link-btn"); + removeBtn.setOnAction(e -> removeAdmin(a.optString("user_id"), row)); + row.getChildren().add(removeBtn); + } + + adminsList.getChildren().add(row); + + // User clicks an admin → open set channel admin permissions scene + row.setOnMouseClicked(e -> { + if (!"owner".equalsIgnoreCase(role)) { + try { + // request current permissions from server + JSONObject req = new JSONObject() + .put("action", "get_channel_admin_permissions") + .put("channel_id", channelId) + .put("admin_id", a.optString("user_id")); + + JSONObject resp = ActionHandler.sendWithResponse(req); + JSONObject oldPerms = (resp != null) ? resp.optJSONObject("permissions") : null; + + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/set_channel_admin_permissions.fxml")); + Node overlay = loader.load(); + +// SetChannelAdminPermissionsController controller = loader.getController(); +// controller.setTarget( +// UUID.fromString(channelId), +// UUID.fromString(a.optString("user_id")), +// true, +// oldPerms +// ); + + MainController.getInstance().showOverlay(overlay); + } catch (IOException ex) { + ex.printStackTrace(); + MainController.getInstance().showAlert("Error", "Could not open permissions scene.", Alert.AlertType.ERROR); + } + } + }); + } + + private void removeAdmin(String userId, HBox row) { + JSONObject req = new JSONObject() + .put("action", "remove_admin_from_channel") + .put("channel_id", channelId) + .put("target_user_id", userId); + + System.out.println("Sending remove_admin_from_channel request: " + req.toString(2)); + + row.setDisable(true); + + new Thread(() -> { + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + Platform.runLater(() -> adminsList.getChildren().remove(row)); + } else { + Platform.runLater(() -> { + row.setDisable(false); + Alert a = new Alert(Alert.AlertType.ERROR, + resp != null ? resp.optString("message", "Failed to remove admin.") : "No response from server.", + ButtonType.OK); + a.show(); + }); + } + }).start(); + } + +// private void openAddAdminOverlay() { +// try { +// // === Query the server for full subscribers list === +// JSONObject req = new JSONObject() +// .put("action", "view_channel_subscribers") +// .put("channel_id", channelId); +// +// JSONObject resp = ActionHandler.sendWithResponse(req); +// +// if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) { +// MainController.getInstance().showAlert( +// "Error", +// "Could not fetch subscribers.", +// Alert.AlertType.ERROR +// ); +// return; +// } +// +// JSONObject data = resp.optJSONObject("data"); +// JSONArray subscribers = (data != null) ? data.optJSONArray("subscribers") : new JSONArray(); +// +// // === Load Add Admins FXML (same FXML reused) === +// FXMLLoader loader = new FXMLLoader(getClass().getResource( +// "/org/to/telegramfinalproject/Fxml/add_admins.fxml")); +// Node overlay = loader.load(); +// +// AddChannelAdminsController controller = loader.getController(); +// controller.setChannelData(channelId, subscribers); // send raw subscribers, filtering is done in AddChannelAdminsController +// +// MainController.getInstance().showOverlay(overlay); +// +// } catch (IOException e) { +// e.printStackTrace(); +// MainController.getInstance().showAlert( +// "Error", "Could not load Add Admins scene.", Alert.AlertType.ERROR +// ); +// } +// } +} diff --git a/src/main/java/org/to/telegramfinalproject/UI/ManageChannelController.java b/src/main/java/org/to/telegramfinalproject/UI/ManageChannelController.java new file mode 100644 index 0000000..18f731a --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/ManageChannelController.java @@ -0,0 +1,180 @@ +package org.to.telegramfinalproject.UI; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import javafx.stage.FileChooser; +import org.json.JSONArray; +import org.json.JSONObject; +import org.to.telegramfinalproject.Client.ActionHandler; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; +import java.util.UUID; + +public class ManageChannelController { + + @FXML private VBox manageChannelCard; + @FXML private Pane overlayBackground; + @FXML private Button closeButton, changePicButton, cancelButton, saveButton; + @FXML private ImageView channelImage; + @FXML private TextField channelNameField; + @FXML private TextField channelIdField; + @FXML private TextArea channelDescriptionField; + @FXML private Label adminCount, subscriberCount; + @FXML private Button manageAdminsButton; + @FXML private Button manageSubscribersButton; + + private String channelId; // Internal UUID of the channel + private String originalName; + private String originalDisplayId; + private String originalImageUrl; + private String originalDescription; + private File selectedImageFile; + + private JSONObject data; + + @FXML + public void initialize() { + closeButton.setOnAction(e -> MainController.getInstance().closeOverlay(manageChannelCard.getParent())); + cancelButton.setOnAction(e -> MainController.getInstance().closeOverlay(manageChannelCard.getParent())); + overlayBackground.setOnMouseClicked(e -> MainController.getInstance().closeOverlay(manageChannelCard.getParent())); + + changePicButton.setOnAction(e -> choosePicture()); + saveButton.setOnAction(e -> saveChanges()); + + manageAdminsButton.setOnAction(e -> openManageAdminsScene(data)); + manageSubscribersButton.setOnAction(e -> openManageSubscribersScene(data)); + } + + public void setChannelData(JSONObject data) { + this.data = data; + this.channelId = data.optString("internal_uuid"); + + originalName = data.optString("channel_name", ""); + originalDisplayId = data.optString("channel_id", ""); + originalImageUrl = data.optString("image_url", ""); + originalDescription = data.optString("description", ""); + + channelNameField.setText(originalName); + channelIdField.setText(originalDisplayId); + channelDescriptionField.setText(originalDescription); + + if (!originalImageUrl.isBlank()) { + channelImage.setImage(new Image(originalImageUrl, true)); + } + adminCount.setText(String.valueOf(countRole(data, "admin") + 1)); // owner + admins + subscriberCount.setText(String.valueOf(countRole(data, "subscriber") + Integer.parseInt(adminCount.getText()))); + } + + private int countRole(JSONObject channelData, String role) { + var arr = channelData.optJSONArray("subscribers"); + if (arr == null) return 0; + int count = 0; + for (int i = 0; i < arr.length(); i++) { + if (role.equalsIgnoreCase(arr.getJSONObject(i).optString("role"))) count++; + } + return count; + } + + private void choosePicture() { + FileChooser fc = new FileChooser(); + fc.setTitle("Select channel picture"); + selectedImageFile = fc.showOpenDialog(manageChannelCard.getScene().getWindow()); + if (selectedImageFile != null) { + channelImage.setImage(new Image(selectedImageFile.toURI().toString())); + } + } + + private void saveChanges() { + String newName = channelNameField.getText().trim(); + String newChannelId = channelIdField.getText().trim(); + String newDescription = channelDescriptionField.getText().trim(); + String newImageUrl = (selectedImageFile != null) + ? selectedImageFile.toURI().toString() + : originalImageUrl; + + boolean changed = + !Objects.equals(originalName, newName) || + !Objects.equals(originalDisplayId, newChannelId) || + !Objects.equals(originalDescription, newDescription) || + !Objects.equals(originalImageUrl, newImageUrl); + + if (!changed) { + MainController.getInstance().closeOverlay(manageChannelCard.getParent()); + return; + } + + JSONObject req = new JSONObject() + .put("action", "edit_channel_info") + .put("channel_id", channelId) // internal UUID + .put("new_channel_id", newChannelId) + .put("name", newName) + .put("description", newDescription); + + if (newImageUrl != null && !newImageUrl.isBlank()) { + req.put("image_url", newImageUrl); + } else { + req.put("image_url", JSONObject.NULL); + } + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + MainController.getInstance().closeOverlay(manageChannelCard.getParent()); + } else { + String msg = (resp != null) + ? resp.optString("message", "Failed to update channel") + : "No response from server"; + Alert a = new Alert(Alert.AlertType.ERROR, msg, ButtonType.OK); + a.show(); + } + } + + private void openManageAdminsScene(JSONObject data) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/manage_channel_admins.fxml")); + Node overlay = loader.load(); + + ManageChannelAdminsController controller = loader.getController(); + // Pass channelId and subscribers list from JSON + controller.setChannelData( + data.optString("internal_uuid"), + data.optJSONArray("subscribers") + ); + + MainController.getInstance().showOverlay(overlay); + } catch (IOException e) { + e.printStackTrace(); + MainController.getInstance().showAlert( + "Error", "Could not load Manage Channel Admins scene.", Alert.AlertType.ERROR + ); + } + } + + private void openManageSubscribersScene(JSONObject data) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/manage_subscribers.fxml")); + Node overlay = loader.load(); + + ManageSubscribersController controller = loader.getController(); + // Pass channelId and subscribers list from the JSON data + controller.setChannelData( + data.optString("internal_uuid"), + data.optJSONArray("subscribers") + ); + + MainController.getInstance().showOverlay(overlay); + } catch (IOException e) { + e.printStackTrace(); + MainController.getInstance().showAlert( + "Error", "Could not load Manage Subscribers scene.", Alert.AlertType.ERROR); + } + } +} diff --git a/src/main/java/org/to/telegramfinalproject/UI/ManageSubscribersController.java b/src/main/java/org/to/telegramfinalproject/UI/ManageSubscribersController.java new file mode 100644 index 0000000..e0b6661 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/ManageSubscribersController.java @@ -0,0 +1,160 @@ +package org.to.telegramfinalproject.UI; + +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import org.json.JSONArray; +import org.json.JSONObject; +import org.to.telegramfinalproject.Client.ActionHandler; +import org.to.telegramfinalproject.Client.AvatarLocalResolver; + +import java.io.IOException; +import java.util.UUID; + +public class ManageSubscribersController { + + @FXML private VBox subscribersCard; + @FXML private Pane overlayBackground; + @FXML private Button closeButton; + @FXML private Button closeFooterButton; + @FXML private Button addSubscribersButton; + @FXML private ScrollPane subscribersScroll; + @FXML private VBox subscribersList; + + private String channelId; // internal UUID + + @FXML + private void initialize() { + closeButton.setOnAction(e -> MainController.getInstance().closeOverlay(subscribersCard.getParent())); + closeFooterButton.setOnAction(e -> MainController.getInstance().closeOverlay(subscribersCard.getParent())); + overlayBackground.setOnMouseClicked(e -> MainController.getInstance().closeOverlay(subscribersCard.getParent())); + + addSubscribersButton.setOnAction(e -> openAddSubscribersOverlay(channelId)); + + // Smooth scroll + subscribersScroll.getStylesheets().add( + getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm() + ); + subscribersScroll.setPannable(true); + subscribersScroll.setFitToWidth(true); + subscribersScroll.setFitToHeight(false); + subscribersScroll.getContent().setOnScroll(event -> { + double deltaY = event.getDeltaY() * 0.003; + subscribersScroll.setVvalue(subscribersScroll.getVvalue() - deltaY); + }); + } + + public void setChannelData(String channelId, JSONArray subscribers) { + this.channelId = channelId; + subscribersList.getChildren().clear(); + + for (int i = 0; i < subscribers.length(); i++) { + Object raw = subscribers.get(i); + JSONObject sub = (raw instanceof JSONObject) + ? (JSONObject) raw + : new JSONObject((java.util.Map) raw); + + addSubscriberRow(sub); + } + } + + private void addSubscriberRow(JSONObject sub) { + HBox row = new HBox(10); + row.getStyleClass().add("member-row"); + row.setAlignment(Pos.CENTER_LEFT); + + // Avatar + ImageView avatar = new ImageView(); + avatar.setFitWidth(36); + avatar.setFitHeight(36); + avatar.setPreserveRatio(true); + + String imgUrl = sub.optString("image_url", ""); + if (!imgUrl.isBlank()) { + Image img = AvatarLocalResolver.load(imgUrl); + if (img != null) avatar.setImage(img); + } else { + avatar.setImage(new Image(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_user_profile.png" + ))); + } + + // Name + Status + VBox details = new VBox(2); + Label name = new Label(sub.optString("profile_name", "Unknown")); + name.getStyleClass().add("member-name"); + + Label status = new Label( + sub.optBoolean("is_online", false) ? "online" : "last seen recently" + ); + status.getStyleClass().add("member-status"); + + details.getChildren().addAll(name, status); + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + row.getChildren().addAll(avatar, details, spacer); + + // Only non-owners can be removed + String role = sub.optString("role", "subscriber"); + if (!"owner".equalsIgnoreCase(role)) { + Button removeBtn = new Button("Remove"); + removeBtn.getStyleClass().add("link-btn"); + removeBtn.setOnAction(e -> removeSubscriber(sub.optString("user_id"), row)); + row.getChildren().add(removeBtn); + } + + subscribersList.getChildren().add(row); + } + + private void removeSubscriber(String userId, HBox row) { + JSONObject req = new JSONObject() + .put("action", "remove_subscriber_from_channel") + .put("channel_id", channelId) + .put("user_id", userId); + + row.setDisable(true); + + new Thread(() -> { + JSONObject resp = ActionHandler.sendWithResponse(req); + + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + Platform.runLater(() -> subscribersList.getChildren().remove(row)); + } else { + Platform.runLater(() -> { + row.setDisable(false); + Alert a = new Alert(Alert.AlertType.ERROR, + resp != null ? resp.optString("message", "Failed to remove subscriber.") + : "No response from server.", + ButtonType.OK); + a.show(); + }); + } + }).start(); + } + + private void openAddSubscribersOverlay(String channelId) { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/add_subscriber.fxml")); + Node overlay = loader.load(); + + AddSubscriberController controller = loader.getController(); + controller.setChannelInfo(UUID.fromString(channelId), "", "", null, ""); + + MainController.getInstance().showOverlay(overlay); + } catch (IOException e) { + e.printStackTrace(); + MainController.getInstance().showAlert( + "Error", "Could not load Add Subscribers scene.", Alert.AlertType.ERROR + ); + } + } +} diff --git a/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css b/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css index d68630e..a94b76b 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css @@ -1441,3 +1441,21 @@ -fx-font-size: 12px; -fx-text-fill: #777; } + +/* Dark theme: Channel Manage styles */ +.description-input { + -fx-background-color: #1f2a36; /* darker to match card */ + -fx-control-inner-background: #1f2a36; + -fx-border-color: #444444; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-padding: 6; + -fx-font-size: 13px; + -fx-text-fill: #dddddd; /* normal text */ + -fx-prompt-text-fill: #888888; /* dimmed placeholder text */ +} + +.description-input:focused { + -fx-border-color: #4a90e2; /* Telegram blue */ + -fx-effect: dropshadow(gaussian, rgba(74,144,226,0.5), 6, 0.4, 0, 0); +} diff --git a/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css b/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css index f1601a3..5a6ba18 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css @@ -1392,3 +1392,19 @@ -fx-font-size: 12px; -fx-text-fill: #777; } + +/* Light theme: Channel Manage styles */ +.description-input { + -fx-background-color: #ffffff; + -fx-border-color: #d0d0d0; + -fx-border-radius: 6; + -fx-background-radius: 6; + -fx-padding: 6; + -fx-font-size: 13px; + -fx-text-fill: #333333; +} + +.description-input:focused { + -fx-border-color: #4a90e2; /* Telegram blue highlight */ + -fx-effect: dropshadow(gaussian, rgba(74,144,226,0.4), 4, 0.3, 0, 0); +} diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/manage_channel.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/manage_channel.fxml new file mode 100644 index 0000000..bcfd565 --- /dev/null +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/manage_channel.fxml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + +