Channel Info UI + backend implemented.
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@ tasks.withType(JavaCompile) {
|
|||||||
|
|
||||||
application {
|
application {
|
||||||
mainModule = 'org.to.telegramfinalproject'
|
mainModule = 'org.to.telegramfinalproject'
|
||||||
mainClass = 'org.to.telegramfinalproject.HelloApplication'
|
mainClass = 'org.to.telegramfinalproject.Server.MainServer'
|
||||||
}
|
}
|
||||||
|
|
||||||
javafx {
|
javafx {
|
||||||
|
|||||||
@@ -691,4 +691,83 @@ public class ChannelDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JSONObject getChannelInfo(UUID channelId, UUID viewerUuid) throws SQLException {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect()) {
|
||||||
|
// === Channel header (name, subscriber count, etc.)
|
||||||
|
String channelQuery = """
|
||||||
|
SELECT c.internal_uuid, c.channel_id, c.channel_name, c.image_url, c.description,
|
||||||
|
COUNT(s.user_id) as subscriber_count
|
||||||
|
FROM channels c
|
||||||
|
LEFT JOIN channel_subscribers s ON c.internal_uuid = s.channel_id
|
||||||
|
WHERE c.internal_uuid = ?
|
||||||
|
GROUP BY c.internal_uuid, c.channel_id, c.channel_name, c.image_url, c.description
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = conn.prepareStatement(channelQuery)) {
|
||||||
|
ps.setObject(1, channelId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
result.put("internal_uuid", rs.getString("internal_uuid"));
|
||||||
|
result.put("channel_id", rs.getString("channel_id")); // varchar if needed
|
||||||
|
result.put("channel_name", rs.getString("channel_name"));
|
||||||
|
result.put("subscriber_count", rs.getInt("subscriber_count"));
|
||||||
|
result.put("image_url", rs.getString("image_url"));
|
||||||
|
result.put("description", rs.getString("description")); // ✅ here
|
||||||
|
} else {
|
||||||
|
return null; // no such channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Subscribers (id, name, role, status, image)
|
||||||
|
JSONArray subscribersArr = new JSONArray();
|
||||||
|
|
||||||
|
String subscribersQuery = """
|
||||||
|
SELECT u.internal_uuid, u.profile_name, u.user_id, u.image_url,
|
||||||
|
cs.role, u.status, u.last_seen
|
||||||
|
FROM channel_subscribers cs
|
||||||
|
JOIN users u ON cs.user_id = u.internal_uuid
|
||||||
|
WHERE cs.channel_id = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = conn.prepareStatement(subscribersQuery)) {
|
||||||
|
ps.setObject(1, channelId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
JSONObject sub = new JSONObject();
|
||||||
|
sub.put("user_id", rs.getString("internal_uuid"));
|
||||||
|
sub.put("profile_name", rs.getString("profile_name"));
|
||||||
|
sub.put("username", rs.getString("user_id"));
|
||||||
|
sub.put("image_url", rs.getString("image_url"));
|
||||||
|
sub.put("role", rs.getString("role"));
|
||||||
|
sub.put("status", rs.getString("status"));
|
||||||
|
sub.put("last_seen", rs.getString("last_seen"));
|
||||||
|
subscribersArr.put(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.put("subscribers", subscribersArr);
|
||||||
|
|
||||||
|
// === Viewer role (so UI knows if viewer can manage/delete)
|
||||||
|
String roleQuery = """
|
||||||
|
SELECT role
|
||||||
|
FROM channel_subscribers
|
||||||
|
WHERE channel_id = ? AND user_id = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = conn.prepareStatement(roleQuery)) {
|
||||||
|
ps.setObject(1, channelId);
|
||||||
|
ps.setObject(2, viewerUuid);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
result.put("my_role", rs.getString("role"));
|
||||||
|
} else {
|
||||||
|
result.put("my_role", ""); // not a subscriber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.to.telegramfinalproject.Server;
|
|||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Client.Session;
|
||||||
import org.to.telegramfinalproject.Database.*;
|
import org.to.telegramfinalproject.Database.*;
|
||||||
import org.to.telegramfinalproject.Models.*;
|
import org.to.telegramfinalproject.Models.*;
|
||||||
import org.to.telegramfinalproject.Security.PasswordHashing;
|
import org.to.telegramfinalproject.Security.PasswordHashing;
|
||||||
@@ -3061,6 +3062,30 @@ public class ClientHandler implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "view_channel": {
|
||||||
|
if (currentUser == null) {
|
||||||
|
response = new ResponseModel("error", "Unauthorized. Please login first.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
UUID channelId = UUID.fromString(requestJson.getString("channel_id"));
|
||||||
|
UUID viewerId = currentUser.getInternal_uuid(); // logged-in user
|
||||||
|
|
||||||
|
// Query channel details
|
||||||
|
JSONObject channelData = ChannelDatabase.getChannelInfo(channelId, viewerId);
|
||||||
|
|
||||||
|
if (channelData == null) {
|
||||||
|
response = new ResponseModel("error", "Channel not found.");
|
||||||
|
} else {
|
||||||
|
response = new ResponseModel("success", "Channel info fetched.", channelData);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
response = new ResponseModel("error", "Error processing channel info: " + e.getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
response = new ResponseModel("error", "Unknown action: " + action);
|
response = new ResponseModel("error", "Unknown action: " + action);
|
||||||
|
|||||||
@@ -1,11 +1,267 @@
|
|||||||
package org.to.telegramfinalproject.UI;
|
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.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||||
|
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||||
|
import org.to.telegramfinalproject.Client.Session;
|
||||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ChannelInfoController {
|
public class ChannelInfoController {
|
||||||
|
|
||||||
|
@FXML private VBox channelCard;
|
||||||
|
@FXML private Pane overlayBackground;
|
||||||
|
@FXML private Button closeButton;
|
||||||
|
|
||||||
|
@FXML private ImageView channelAvatar;
|
||||||
|
@FXML private Label channelName;
|
||||||
|
@FXML private Label subscriberCount;
|
||||||
|
@FXML private Label channelDescription;
|
||||||
|
|
||||||
|
@FXML private Button infoMoreButton;
|
||||||
|
@FXML private ContextMenu infoMoreMenu;
|
||||||
|
@FXML private MenuItem addMembersBtn;
|
||||||
|
@FXML private MenuItem manageChannelBtn;
|
||||||
|
@FXML private MenuItem deleteChannelBtn;
|
||||||
|
@FXML private ImageView moreIcon;
|
||||||
|
@FXML private ImageView manageChannelIcon;
|
||||||
|
@FXML private ImageView addMemberIcon;
|
||||||
|
|
||||||
|
@FXML private VBox subscribersList;
|
||||||
|
@FXML private Label subscribersHeader;
|
||||||
|
@FXML private Button addSubscriberButton;
|
||||||
|
@FXML private ImageView subscribersIcon;
|
||||||
|
@FXML private ScrollPane subscribersScroll;
|
||||||
|
|
||||||
|
private UUID channelId;
|
||||||
|
private String myRole;
|
||||||
|
|
||||||
|
private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/";
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
closeButton.setOnAction(e ->
|
||||||
|
MainController.getInstance().closeOverlay(channelCard.getParent()));
|
||||||
|
overlayBackground.setOnMouseClicked(e ->
|
||||||
|
MainController.getInstance().closeOverlay(channelCard.getParent()));
|
||||||
|
|
||||||
|
infoMoreButton.setOnAction(e -> {
|
||||||
|
if (infoMoreMenu != null) infoMoreMenu.show(infoMoreButton, javafx.geometry.Side.BOTTOM, 0, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (manageChannelBtn != null) {
|
||||||
|
manageChannelBtn.setOnAction(e -> openManageChannel());
|
||||||
|
}
|
||||||
|
deleteChannelBtn.setOnAction(e -> handleDeleteChannel());
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (channelCard.getScene() != null) {
|
||||||
|
ThemeManager.getInstance().registerScene(channelCard.getScene());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ThemeManager.getInstance().darkModeProperty().addListener((obs, oldVal, newVal) -> {
|
||||||
|
updateIcons(newVal);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateIcons(ThemeManager.getInstance().isDarkMode());
|
||||||
|
|
||||||
|
// Smooth scroll feel
|
||||||
|
subscribersScroll.getStylesheets().add(
|
||||||
|
getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm()
|
||||||
|
);
|
||||||
|
subscribersScroll.skinProperty().addListener((obs, oldSkin, newSkin) -> {
|
||||||
|
if (newSkin != null) {
|
||||||
|
ScrollBar vBar = (ScrollBar) subscribersScroll.lookup(".scroll-bar:vertical");
|
||||||
|
if (vBar != null) {
|
||||||
|
subscribersScroll.setOnScroll(event -> {
|
||||||
|
double deltaY = event.getDeltaY() * 0.003;
|
||||||
|
double newValue = vBar.getValue() - deltaY;
|
||||||
|
vBar.setValue(Math.max(0, Math.min(newValue, 1)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (addSubscriberButton != null) {
|
||||||
|
addSubscriberButton.setOnAction(e -> openAddSubscriberScene());
|
||||||
|
}
|
||||||
|
if (addMembersBtn != null) {
|
||||||
|
addMembersBtn.setOnAction(e -> openAddSubscriberScene());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setChannelDataFromJson(ChatEntry entry, JSONObject data) {
|
public void setChannelDataFromJson(ChatEntry entry, JSONObject data) {
|
||||||
// read name, status, bio, image url from JSON
|
this.channelId = entry.getId();
|
||||||
// update labels/images accordingly
|
|
||||||
|
channelName.setText(data.optString("channel_name", entry.getName()));
|
||||||
|
subscriberCount.setText(data.optInt("subscriber_count", 0) + " subscribers");
|
||||||
|
channelDescription.setText(data.optString("description", ""));
|
||||||
|
|
||||||
|
// --- Role-based UI ---
|
||||||
|
myRole = data.optString("my_role", "subscriber").toLowerCase();
|
||||||
|
|
||||||
|
deleteChannelBtn.setVisible("owner".equals(myRole));
|
||||||
|
|
||||||
|
boolean canAdd = "owner".equals(myRole) || "admin".equals(myRole);
|
||||||
|
addSubscriberButton.setVisible(canAdd);
|
||||||
|
addSubscriberButton.setManaged(canAdd);
|
||||||
|
if (addMembersBtn != null) {
|
||||||
|
addMembersBtn.setVisible(canAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean showMore = "owner".equals(myRole) || "admin".equals(myRole);
|
||||||
|
infoMoreButton.setVisible(showMore);
|
||||||
|
infoMoreButton.setManaged(showMore);
|
||||||
|
|
||||||
|
// --- Avatar ---
|
||||||
|
String imgUrl = data.optString("image_url", "");
|
||||||
|
if (!imgUrl.isBlank()) {
|
||||||
|
try {
|
||||||
|
Image img = AvatarLocalResolver.load(imgUrl);
|
||||||
|
if (img != null) channelAvatar.setImage(img);
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
} else {
|
||||||
|
channelAvatar.setImage(new Image(
|
||||||
|
getClass().getResourceAsStream("/org/to/telegramfinalproject/Avatars/default_channel_profile.png")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Subscribers list ---
|
||||||
|
subscribersList.getChildren().clear();
|
||||||
|
var arr = data.optJSONArray("subscribers");
|
||||||
|
if (arr != null) {
|
||||||
|
subscribersHeader.setText(arr.length() + " SUBSCRIBERS");
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
JSONObject s = arr.getJSONObject(i);
|
||||||
|
addSubscriberRow(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSubscriberRow(JSONObject s) {
|
||||||
|
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);
|
||||||
|
AvatarFX.circleClip(avatar, 36);
|
||||||
|
|
||||||
|
String imgUrl = s.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 nameBox = new VBox(2);
|
||||||
|
Label name = new Label(s.optString("profile_name", "Unknown"));
|
||||||
|
name.getStyleClass().add("member-name");
|
||||||
|
|
||||||
|
String status;
|
||||||
|
if (s.optBoolean("is_online", false)) {
|
||||||
|
status = "online";
|
||||||
|
} else {
|
||||||
|
status = ChatPageController.getInstance().userStatusText(
|
||||||
|
false,
|
||||||
|
s.optString("last_seen", null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Label statusLbl = new Label(status);
|
||||||
|
statusLbl.getStyleClass().add("member-status");
|
||||||
|
|
||||||
|
nameBox.getChildren().addAll(name, statusLbl);
|
||||||
|
|
||||||
|
// Role
|
||||||
|
Label roleLbl = new Label();
|
||||||
|
String roleStr = s.optString("role", "");
|
||||||
|
if (!roleStr.isBlank()) {
|
||||||
|
roleLbl.setText(roleStr.toLowerCase());
|
||||||
|
roleLbl.getStyleClass().add("member-role");
|
||||||
|
}
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
|
row.getChildren().addAll(avatar, nameBox, spacer, roleLbl);
|
||||||
|
subscribersList.getChildren().add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openManageChannel() {
|
||||||
|
// TODO similar to ManageGroupController
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openAddSubscriberScene() {
|
||||||
|
try {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||||
|
"/org/to/telegramfinalproject/Fxml/add_subscriber.fxml")); // point to new fxml
|
||||||
|
Node overlay = loader.load();
|
||||||
|
|
||||||
|
AddSubscriberController controller = loader.getController();
|
||||||
|
controller.setChannelInfo(channelId, channelName.getText(), "", null, "");
|
||||||
|
// passing UUID + name, other fields optional
|
||||||
|
|
||||||
|
MainController.getInstance().showOverlay(overlay);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
MainController.getInstance().showAlert(
|
||||||
|
"Error",
|
||||||
|
"Could not load Add Subscribers scene.",
|
||||||
|
Alert.AlertType.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDeleteChannel() {
|
||||||
|
System.out.println("Deleting channel...");
|
||||||
|
// TODO: implement backend call
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIcons(boolean dark) {
|
||||||
|
String suffix = dark ? "_light.png" : "_dark.png";
|
||||||
|
|
||||||
|
moreIcon.setImage(loadImage(ICON_PATH + "more" + suffix));
|
||||||
|
subscribersIcon.setImage(loadImage(ICON_PATH + "channel_subscriber" + suffix));
|
||||||
|
addSubscriberButton.setGraphic(makeIcon(ICON_PATH + "add_member" + suffix));
|
||||||
|
manageChannelIcon.setImage(loadImage(ICON_PATH + "manage" + suffix));
|
||||||
|
addMemberIcon.setImage(loadImage(ICON_PATH + "add_member" + suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView makeIcon(String path) {
|
||||||
|
ImageView iv = new ImageView();
|
||||||
|
Image img = loadImage(path);
|
||||||
|
if (img != null) {
|
||||||
|
iv.setImage(img);
|
||||||
|
iv.setFitWidth(22);
|
||||||
|
iv.setFitHeight(22);
|
||||||
|
iv.setPreserveRatio(true);
|
||||||
|
}
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image loadImage(String path) {
|
||||||
|
URL res = getClass().getResource(path);
|
||||||
|
if (res == null) return null;
|
||||||
|
return new Image(res.toExternalForm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class GroupInfoController {
|
|||||||
@FXML private MenuItem manageGroupItem;
|
@FXML private MenuItem manageGroupItem;
|
||||||
@FXML private MenuItem deleteGroupItem;
|
@FXML private MenuItem deleteGroupItem;
|
||||||
@FXML private ImageView moreIcon;
|
@FXML private ImageView moreIcon;
|
||||||
|
@FXML private ImageView manageChannelIcon;
|
||||||
|
@FXML private ImageView addMemberIcon;
|
||||||
|
|
||||||
@FXML private VBox membersList;
|
@FXML private VBox membersList;
|
||||||
@FXML private Label membersHeader;
|
@FXML private Label membersHeader;
|
||||||
@@ -300,7 +302,8 @@ public class GroupInfoController {
|
|||||||
membersIcon.setImage(loadImage(ICON_PATH + "group" + suffix));
|
membersIcon.setImage(loadImage(ICON_PATH + "group" + suffix));
|
||||||
addMemberButton.setGraphic(makeIcon(ICON_PATH + "add_member" + suffix));
|
addMemberButton.setGraphic(makeIcon(ICON_PATH + "add_member" + suffix));
|
||||||
membersIcon.setImage(loadImage(ICON_PATH + "group_member" + suffix));
|
membersIcon.setImage(loadImage(ICON_PATH + "group_member" + suffix));
|
||||||
|
manageChannelIcon.setImage(loadImage(ICON_PATH + "manage" + suffix));
|
||||||
|
addMemberIcon.setImage(loadImage(ICON_PATH + "add_member" + suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- helpers -------------------------------------------------------------
|
// --- helpers -------------------------------------------------------------
|
||||||
|
|||||||
@@ -1410,3 +1410,34 @@
|
|||||||
-fx-border-color: #555;
|
-fx-border-color: #555;
|
||||||
-fx-text-fill: #aaa;
|
-fx-text-fill: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel-info-card {
|
||||||
|
-fx-background-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-title {
|
||||||
|
-fx-text-fill: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-subcount {
|
||||||
|
-fx-text-fill: #aaaaaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-buttons .button {
|
||||||
|
-fx-background-color: #3c3c3c;
|
||||||
|
-fx-text-fill: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-button {
|
||||||
|
-fx-text-fill: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriber-item,
|
||||||
|
.admin-item {
|
||||||
|
-fx-text-fill: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-description {
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-text-fill: #777;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1387,3 +1387,8 @@
|
|||||||
-fx-border-color: #ccc;
|
-fx-border-color: #ccc;
|
||||||
-fx-text-fill: #222;
|
-fx-text-fill: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-description {
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-text-fill: #777;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,122 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import java.util.*?>
|
|
||||||
<?import javafx.scene.*?>
|
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx"
|
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
fx:controller="org.to.telegramfinalproject.UI.ChannelInfoController"
|
||||||
fx:controller="org.to.telegramfinalproject.UI.ChannelInfoController"
|
styleClass="overlay-root">
|
||||||
prefHeight="400.0" prefWidth="600.0">
|
|
||||||
|
|
||||||
</AnchorPane>
|
<!-- Background (click to close) -->
|
||||||
|
<Pane fx:id="overlayBackground" styleClass="overlay-background"/>
|
||||||
|
|
||||||
|
<!-- Channel card -->
|
||||||
|
<VBox fx:id="channelCard" styleClass="profile-card" spacing="16"
|
||||||
|
prefWidth="360" maxWidth="360"
|
||||||
|
prefHeight="500" maxHeight="500">
|
||||||
|
|
||||||
|
<!-- ===== Header: Title + More + Close ===== -->
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="10">
|
||||||
|
<Label text="Channel Info" styleClass="profile-title" HBox.hgrow="ALWAYS"/>
|
||||||
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<!-- More -->
|
||||||
|
<Button fx:id="infoMoreButton" styleClass="icon-button">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fx:id="moreIcon" fitWidth="18" fitHeight="18" preserveRatio="true"/>
|
||||||
|
</graphic>
|
||||||
|
<contextMenu>
|
||||||
|
<ContextMenu fx:id="infoMoreMenu">
|
||||||
|
<items>
|
||||||
|
<!-- Add Members -->
|
||||||
|
<MenuItem fx:id="addMembersBtn" text="Add members">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fx:id="addMemberIcon" fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/org/to/telegramfinalproject/Icons/add_member_dark.png"/>
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<!-- Manage Channel -->
|
||||||
|
<MenuItem fx:id="manageChannelBtn" text="Manage channel">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fx:id="manageChannelIcon" fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/org/to/telegramfinalproject/Icons/manage_dark.png"/>
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<!-- Delete Channel (owner only) -->
|
||||||
|
<MenuItem fx:id="deleteChannelBtn" text="Delete channel" style="-fx-text-fill: red;" visible="false">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/org/to/telegramfinalproject/Icons/delete_red.png"/>
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</MenuItem>
|
||||||
|
</items>
|
||||||
|
</ContextMenu>
|
||||||
|
</contextMenu>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- Close -->
|
||||||
|
<Button fx:id="closeButton" styleClass="icon-button" text="✕"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<!-- ===== Channel picture + name + subscriber count ===== -->
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="12">
|
||||||
|
<!-- Channel picture -->
|
||||||
|
<ImageView fx:id="channelAvatar" fitWidth="60" fitHeight="60" preserveRatio="true"
|
||||||
|
styleClass="profile-picture"/>
|
||||||
|
|
||||||
|
<!-- Name + subs stacked -->
|
||||||
|
<VBox alignment="CENTER_LEFT" spacing="4">
|
||||||
|
<Label fx:id="channelName" text="Channel name" styleClass="info-title"/>
|
||||||
|
<Label fx:id="subscriberCount" text="0 subscribers" styleClass="info-subtitle"/>
|
||||||
|
<!-- New description label -->
|
||||||
|
<Label fx:id="channelDescription" text="Channel description..." wrapText="true"
|
||||||
|
styleClass="info-description"/>
|
||||||
|
</VBox>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<Separator styleClass="section-separator"/>
|
||||||
|
|
||||||
|
<!-- ===== Subscribers list ===== -->
|
||||||
|
<VBox spacing="8" styleClass="info-blocks" VBox.vgrow="ALWAYS">
|
||||||
|
<HBox spacing="8" alignment="CENTER_LEFT">
|
||||||
|
<ImageView fx:id="subscribersIcon" fitWidth="26" fitHeight="26" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/org/to/telegramfinalproject/Icons/group_member_dark.png"/>
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label fx:id="subscribersHeader" text="0 SUBSCRIBERS" styleClass="profile-info-sub"/>
|
||||||
|
<Pane HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button fx:id="addSubscriberButton" styleClass="icon-button">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/org/to/telegramfinalproject/Icons/add_member_dark.png"/>
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<ScrollPane fx:id="subscribersScroll" fitToWidth="true" styleClass="member-scroll" VBox.vgrow="ALWAYS">
|
||||||
|
<content>
|
||||||
|
<VBox fx:id="subscribersList" spacing="10"/>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<!-- Add Member -->
|
<!-- Add Member -->
|
||||||
<MenuItem fx:id="addMemberItem" text="Add member">
|
<MenuItem fx:id="addMemberItem" text="Add member">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
<ImageView fx:id="addMemberIcon" fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/add_member_dark.png"/>
|
<Image url="@/org/to/telegramfinalproject/Icons/add_member_dark.png"/>
|
||||||
</image>
|
</image>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<!-- Manage Group -->
|
<!-- Manage Group -->
|
||||||
<MenuItem fx:id="manageGroupItem" text="Manage group">
|
<MenuItem fx:id="manageGroupItem" text="Manage group">
|
||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
|
<ImageView fx:id="manageChannelIcon" fitWidth="16" fitHeight="16" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@/org/to/telegramfinalproject/Icons/manage_dark.png"/>
|
<Image url="@/org/to/telegramfinalproject/Icons/manage_dark.png"/>
|
||||||
</image>
|
</image>
|
||||||
|
|||||||
Reference in New Issue
Block a user