diff --git a/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java b/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java index b58f4c0..8d72664 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java +++ b/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java @@ -7,7 +7,7 @@ import java.sql.SQLException; public class ConnectionDb { private static final String JDBC_URL = "jdbc:postgresql://localhost:5432/Telegram"; private static final String USERNAME = "postgres"; - private static final String PASSWORD = "Partow@1384"; + private static final String PASSWORD = "124postpass"; public ConnectionDb() { } diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java b/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java new file mode 100644 index 0000000..1f48397 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/ChannelInfoController.java @@ -0,0 +1,11 @@ +package org.to.telegramfinalproject.UI; + +import org.json.JSONObject; +import org.to.telegramfinalproject.Models.ChatEntry; + +public class ChannelInfoController { + public void setChannelDataFromJson(ChatEntry entry, JSONObject data) { + // read name, status, bio, image url from JSON + // update labels/images accordingly + } +} diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java b/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java index b4eabd5..469b70d 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java @@ -2,6 +2,7 @@ package org.to.telegramfinalproject.UI; import javafx.application.Platform; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.Side; @@ -23,6 +24,7 @@ import org.to.telegramfinalproject.Client.Session; import org.to.telegramfinalproject.Models.ChatEntry; import java.io.File; +import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; @@ -58,6 +60,8 @@ public class ChatPageController { private Label chatTitle; // contact/group title @FXML private Label chatStatus; // last seen / online + @FXML + private HBox chatHeader; // Header @FXML private Button searchInChatButton; // magnifier button @@ -84,11 +88,11 @@ public class ChatPageController { @FXML private VBox joinPane; @FXML private VBox addContactPane; - //For search handeling + // For search handling @FXML private Button joinButton; @FXML private Button addContactButton; - //Handle View chat + // Handle View chat @FXML private Button unblockBtn; @FXML private VBox readOnlyPane; @FXML private Label readOnlyLabel; @@ -124,6 +128,7 @@ public class ChatPageController { // Where your icons live private static final String ICON_BASE = "/org/to/telegramfinalproject/Icons/"; + private static ChatPageController instance; private ChatEntry currentChat; private UUID me; @@ -148,6 +153,14 @@ public class ChatPageController { } } + public ChatPageController() { + instance = this; + } + + public static ChatPageController getInstance() { + return instance; + } + private static String str(org.json.JSONObject j, String k) { try { return (j.has(k) && !j.isNull(k)) ? j.getString(k) : ""; } catch (Exception e) { return ""; } } @@ -813,7 +826,6 @@ public void showChat(ChatEntry entry) { } AvatarFX.circleClip(userAvatar, 36); - // حالت اولیه (بدون انتظار هدر) if ("channel".equalsIgnoreCase(entry.getType())) { boolean canPostLocal = entry.isOwner() || entry.isAdmin() @@ -830,6 +842,8 @@ public void showChat(ChatEntry entry) { // حالا هدر بیاد، دوباره نهایی‌اش می‌کنیم fetchAndRenderHeader(entry); + // === (3-dot menu + header click) === + configureHeaderActions(entry); } @@ -883,16 +897,18 @@ public void showChat(ChatEntry entry) { public void showChat(ChatEntry entry, ChatViewMode mode) { this.currentChat = entry; - // --- Header --- + // --- Header (fallback until server responds) --- chatTitle.setText(entry.getName()); if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) { Image img = AvatarLocalResolver.load(entry.getImageUrl()); - if (img != null) userAvatar.setImage(img); else setDefaultHeaderAvatarByType(entry.getType()); + if (img != null) userAvatar.setImage(img); + else setDefaultHeaderAvatarByType(entry.getType()); } else { setDefaultHeaderAvatarByType(entry.getType()); } AvatarFX.circleClip(userAvatar, 36); + // Fetch live header info from server fetchAndRenderHeader(entry); // --- Messages --- @@ -900,13 +916,222 @@ public void showChat(ChatEntry entry) { loadMessages(entry); markAsRead(entry); - // --- حالت UI (Composer / Join / Add Contact) --- + // --- UI mode (Composer / Join / Add Contact) --- applyMode(mode); - // فوکوس اگر در حالت نرمال هستیم + // Focus if normal mode if (mode == ChatViewMode.NORMAL) { Platform.runLater(() -> messageInput.requestFocus()); } + + // Wire up menu + header click + configureHeaderActions(entry); + } + + private void configureHeaderActions(ChatEntry entry) { + moreMenu.getItems().clear(); + + switch (entry.getType().toLowerCase(Locale.ROOT)) { + case "private" -> { + MenuItem viewProfile = new MenuItem("View profile"); + viewProfile.setOnAction(e -> openInfoScene(entry)); + + MenuItem deleteChat = new MenuItem("Delete chat"); + deleteChat.setStyle("-fx-text-fill: red;"); + deleteChat.setOnAction(e -> deleteChatButton(entry)); + + moreMenu.getItems().addAll(viewProfile, deleteChat); + } + case "group" -> { + MenuItem viewGroup = new MenuItem("View group info"); + viewGroup.setOnAction(e -> openInfoScene(entry)); + + MenuItem leaveGroup = new MenuItem("Leave group"); + leaveGroup.setOnAction(e -> leaveGroupButton(entry)); + + moreMenu.getItems().addAll(viewGroup, leaveGroup); + } + case "channel" -> { + MenuItem viewChannel = new MenuItem("View channel info"); + viewChannel.setOnAction(e -> openInfoScene(entry)); + + MenuItem leaveChannel = new MenuItem("Leave channel"); + leaveChannel.setOnAction(e -> leaveChannelButton(entry)); + + moreMenu.getItems().addAll(viewChannel, leaveChannel); + } + } + + // Clicking header (but not the 3-dot) → open info scene + chatHeader.setOnMouseClicked(e -> { + if (!moreButton.equals(e.getTarget())) { + openInfoScene(entry); + } + }); + } + + private void openInfoScene(ChatEntry entry) { + JSONObject req = new JSONObject(); + + switch (entry.getType().toLowerCase()) { + case "private" -> { + // First ask server for the target_id of this private chat + JSONObject targetReq = new JSONObject() + .put("action", "get_private_chat_target") + .put("chat_id", entry.getId().toString()); + + JSONObject targetResp = ActionHandler.sendWithResponse(targetReq); + if (targetResp == null || !"success".equalsIgnoreCase(targetResp.optString("status"))) { + Platform.runLater(() -> + MainController.getInstance().showAlert( + "Error", + targetResp != null ? targetResp.optString("message") : "Failed to fetch chat target.", + Alert.AlertType.ERROR + ) + ); + return; + } + + String targetId = targetResp.optJSONObject("data").optString("target_id", null); + if (targetId == null || targetId.isBlank()) { + Platform.runLater(() -> + MainController.getInstance().showAlert( + "Error", + "No target user found for this chat.", + Alert.AlertType.ERROR + ) + ); + return; + } + + req.put("action", "view_profile") + .put("target_id", targetId); + } + case "group" -> { + req.put("action", "view_group") + .put("group_id", entry.getId().toString()); + } + case "channel" -> { + req.put("action", "view_channel") + .put("channel_id", entry.getId().toString()); + } + default -> { + Platform.runLater(() -> + MainController.getInstance().showAlert( + "Error", + "Unsupported chat type: " + entry.getType(), + Alert.AlertType.ERROR + ) + ); + return; + } + } + + new Thread(() -> { + 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; + Node overlay = null; + + switch (entry.getType().toLowerCase()) { + case "private" -> { + loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/user_info.fxml")); + overlay = loader.load(); + UserInfoController c = loader.getController(); + c.setProfileDataFromJson(entry, data); + } + case "group" -> { + loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/group_info.fxml")); + overlay = loader.load(); + GroupInfoController c = loader.getController(); + c.setGroupDataFromJson(entry, data); + } + case "channel" -> { + loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/channel_info.fxml")); + overlay = loader.load(); + ChannelInfoController c = loader.getController(); + c.setChannelDataFromJson(entry, data); + } + } + + if (overlay != null) { + MainController.getInstance().showOverlay(overlay); + } + + } catch (IOException e) { + e.printStackTrace(); + MainController.getInstance().showAlert( + "Error", + "Could not load info scene.", + Alert.AlertType.ERROR + ); + } + }); + }).start(); + } + + private void deleteChatButton(ChatEntry entry) { + JSONObject req = new JSONObject() + .put("action", "delete_chat") + .put("receiver_id", entry.getId().toString()) + .put("viewer_id", Session.getUserUUID()); + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + // TODO: close chat + refresh sidebar + MainController.getInstance().closeCurrentChat(); + } + } + + private void leaveGroupButton(ChatEntry entry) { + JSONObject req = new JSONObject() + .put("action", "leave_group") + .put("group_id", entry.getId().toString()) + .put("viewer_id", Session.getUserUUID()); + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + MainController.getInstance().closeCurrentChat(); + } + } + + private void leaveChannelButton(ChatEntry entry) { + JSONObject req = new JSONObject() + .put("action", "leave_channel") + .put("channel_id", entry.getId().toString()) + .put("viewer_id", Session.getUserUUID()); + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + MainController.getInstance().closeCurrentChat(); + } } private void loadMessages(ChatEntry entry) { @@ -1845,7 +2070,7 @@ private void addBubble( - private String userStatusText(boolean online, String lastSeenIso) { + public String userStatusText(boolean online, String lastSeenIso) { if (online) return "online"; LocalDateTime ts = parseWhen(lastSeenIso); diff --git a/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java b/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java new file mode 100644 index 0000000..e0d4a76 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java @@ -0,0 +1,11 @@ +package org.to.telegramfinalproject.UI; + +import org.json.JSONObject; +import org.to.telegramfinalproject.Models.ChatEntry; + +public class GroupInfoController { + public void setGroupDataFromJson(ChatEntry entry, JSONObject data) { + // read name, status, bio, image url from JSON + // update labels/images accordingly + } +} diff --git a/src/main/java/org/to/telegramfinalproject/UI/MainController.java b/src/main/java/org/to/telegramfinalproject/UI/MainController.java index d6dab47..723cbef 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/MainController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/MainController.java @@ -41,9 +41,6 @@ public class MainController { private boolean blockedByMeFlag = false; private boolean blockedMeFlag = false; - - - private enum SearchMode { GLOBAL, CHAT @@ -635,6 +632,37 @@ public class MainController { } } + public void closeCurrentChat() { + // 1. Clear the chat area + chatDisplayArea.getChildren().clear(); + + // 2. Re-add the placeholder label + placeholderLabel.setVisible(true); + placeholderLabel.setManaged(true); + placeholderLabel.setText("Select a chat to start messaging"); + + chatDisplayArea.getChildren().add(placeholderLabel); + + // 3. Ask the chat list to refresh + MainController.getInstance().refreshChatListUI(); + } + + public void showAlert(String title, String message, Alert.AlertType type) { + Platform.runLater(() -> { + Alert alert = new Alert(type); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + + // Tie it to the main window so it centers nicely + if (mainRoot.getScene() != null && mainRoot.getScene().getWindow() != null) { + alert.initOwner(mainRoot.getScene().getWindow()); + } + + alert.showAndWait(); + }); + } + // ===== Search UI state ===== private final java.util.List searchBacking = new java.util.ArrayList<>(); diff --git a/src/main/java/org/to/telegramfinalproject/UI/UserInfoController.java b/src/main/java/org/to/telegramfinalproject/UI/UserInfoController.java new file mode 100644 index 0000000..dc4ec69 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/UserInfoController.java @@ -0,0 +1,171 @@ +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.Pane; +import javafx.scene.layout.VBox; +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; + +public class UserInfoController { + + @FXML private VBox profileCard; + @FXML private Pane overlayBackground; + @FXML private Button closeButton; + + @FXML private ImageView profileImage; + @FXML private Label profileName; + @FXML private Label profileStatus; + @FXML private VBox bioBlock; + @FXML private Label userBio; + @FXML private VBox usernameBlock; + @FXML private Label userId; + + @FXML private Button moreButton; + @FXML private ContextMenu moreMenu; + @FXML private MenuItem deleteChatItem; + @FXML private MenuItem blockItem; + @FXML private MenuItem unblockItem; + + private String otherUserId; // internal_uuid of the other user + + @FXML + private void initialize() { + closeButton.setOnAction(e -> + MainController.getInstance().closeOverlay(profileCard.getParent())); + overlayBackground.setOnMouseClicked(e -> + MainController.getInstance().closeOverlay(profileCard.getParent())); + + // Show menu manually + moreButton.setOnAction(e -> { + if (moreMenu != null) { + moreMenu.show(moreButton, javafx.geometry.Side.BOTTOM, 0, 0); + } + }); + + // Hide menu when clicking outside + Platform.runLater(() -> { + if (moreButton.getScene() != null) { + moreButton.getScene().addEventFilter( + javafx.scene.input.MouseEvent.MOUSE_PRESSED, ev -> { + if (moreMenu.isShowing() + && !moreButton.localToScreen(moreButton.getBoundsInLocal()) + .contains(ev.getScreenX(), ev.getScreenY())) { + moreMenu.hide(); + } + } + ); + } + }); + + deleteChatItem.setOnAction(e -> handleDeleteChat()); + blockItem.setOnAction(e -> handleBlock()); + unblockItem.setOnAction(e -> handleUnblock()); + } + + /** Backend JSON → UI */ + public void setProfileDataFromJson(ChatEntry entry, JSONObject data) { + // --- Profile name --- + String name = data.optString("profile_name", entry.getName()); + profileName.setText(name); + + // --- Status --- + String status = ChatPageController.getInstance().userStatusText( + data.optBoolean("is_online", false), + data.optString("last_seen", null) + ); + profileStatus.setText(status); + + // --- Bio --- + String bio = data.optString("bio", ""); + if (bio != null && !bio.isBlank()) { + userBio.setText(bio); + bioBlock.setVisible(true); + bioBlock.setManaged(true); + } else { + bioBlock.setVisible(false); + bioBlock.setManaged(false); + } + + // --- Username --- + String username = data.optString("user_id", ""); + if (username != null && !username.isBlank()) { + userId.setText("@" + username); + usernameBlock.setVisible(true); + usernameBlock.setManaged(true); + } else { + usernameBlock.setVisible(false); + usernameBlock.setManaged(false); + } + + // --- Profile picture --- + String imgUrl = data.optString("image_url", ""); + if (imgUrl != null && !imgUrl.isBlank()) { + try { + Image img = AvatarLocalResolver.load(imgUrl); + if (img != null) profileImage.setImage(img); + } catch (Exception ignore) {} + } else { + profileImage.setImage( + new Image(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_user_profile.png")) + ); + } + + // --- Other user ID (needed for block/delete) --- + this.otherUserId = data.optString("other_user_id", entry.getId().toString()); + + // --- Block/Unblock --- + boolean blocked = data.optBoolean("blocked", false) + || data.optBoolean("is_blocked", false) + || data.optBoolean("blocked_by_me", false); + updateBlockMenu(blocked); + } + + private void handleDeleteChat() { + JSONObject req = new JSONObject() + .put("action", "delete_chat") + .put("receiver_id", otherUserId) + .put("viewer_id", Session.getUserUUID()); + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + MainController.getInstance().closeOverlay(profileCard.getParent()); + } + } + + private void handleBlock() { + JSONObject req = new JSONObject() + .put("action", "block_user") + .put("target_id", otherUserId) + .put("viewer_id", Session.getUserUUID()); + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + updateBlockMenu(true); + } + } + + private void handleUnblock() { + JSONObject req = new JSONObject() + .put("action", "unblock_user") + .put("target_id", otherUserId) + .put("viewer_id", Session.getUserUUID()); + + JSONObject resp = ActionHandler.sendWithResponse(req); + if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) { + updateBlockMenu(false); + } + } + + private void updateBlockMenu(boolean isBlocked) { + blockItem.setVisible(!isBlocked); + unblockItem.setVisible(isBlocked); + } +} diff --git a/src/main/resources/org/to/telegramfinalproject/Avatars/user1.png b/src/main/resources/org/to/telegramfinalproject/Avatars/user1.png new file mode 100644 index 0000000..575ea2a Binary files /dev/null and b/src/main/resources/org/to/telegramfinalproject/Avatars/user1.png differ diff --git a/src/main/resources/org/to/telegramfinalproject/Avatars/user2.png b/src/main/resources/org/to/telegramfinalproject/Avatars/user2.png new file mode 100644 index 0000000..89fa6b1 Binary files /dev/null and b/src/main/resources/org/to/telegramfinalproject/Avatars/user2.png differ diff --git a/src/main/resources/org/to/telegramfinalproject/Avatars/user3.png b/src/main/resources/org/to/telegramfinalproject/Avatars/user3.png new file mode 100644 index 0000000..ceb5329 Binary files /dev/null and b/src/main/resources/org/to/telegramfinalproject/Avatars/user3.png differ diff --git a/src/main/resources/org/to/telegramfinalproject/Avatars/user4.png b/src/main/resources/org/to/telegramfinalproject/Avatars/user4.png new file mode 100644 index 0000000..0a281db Binary files /dev/null and b/src/main/resources/org/to/telegramfinalproject/Avatars/user4.png differ 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 967258c..5a5e578 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css @@ -1079,3 +1079,56 @@ -fx-border-color: red; -fx-border-radius: 6; } + +/* === User Info (Dark) === */ +.user-info-card { + -fx-background-color: #1f1f1f; + -fx-background-radius: 8; + -fx-padding: 16; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.5), 12, 0, 0, 3); +} +.user-info-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #f2f2f2; +} +.user-info-name { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #fff; +} +.user-info-status { + -fx-font-size: 12px; + -fx-text-fill: #aaa; +} +.user-info-main { + -fx-font-size: 13px; + -fx-text-fill: #f0f0f0; +} +.user-info-sub { + -fx-font-size: 11px; + -fx-text-fill: #999; +} +.user-info-separator { + -fx-background-color: #333; +} + +/* === Shared buttons === */ +.danger-button { + -fx-background-color: #e74c3c; + -fx-text-fill: white; + -fx-background-radius: 6; + -fx-padding: 4 12; +} +.danger-button:hover { + -fx-background-color: #c0392b; +} + +/* === Profile image === */ +.profile-picture { + -fx-background-color: #2c2c2c; + -fx-background-radius: 50%; + -fx-border-color: #444; + -fx-border-radius: 50%; + -fx-border-width: 1; +} 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 9f00b66..b701157 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css @@ -1070,3 +1070,56 @@ -fx-border-color: red; -fx-border-radius: 6; } + +/* === User Info (Light) === */ +.user-info-card { + -fx-background-color: #ffffff; + -fx-background-radius: 8; + -fx-padding: 16; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 8, 0, 0, 2); +} +.user-info-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #111; +} +.user-info-name { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #111; +} +.user-info-status { + -fx-font-size: 12px; + -fx-text-fill: #555; +} +.user-info-main { + -fx-font-size: 13px; + -fx-text-fill: #222; +} +.user-info-sub { + -fx-font-size: 11px; + -fx-text-fill: #888; +} +.user-info-separator { + -fx-background-color: #e6e6e6; +} + +/* === Shared buttons === */ +.danger-button { + -fx-background-color: #e74c3c; + -fx-text-fill: white; + -fx-background-radius: 6; + -fx-padding: 4 12; +} +.danger-button:hover { + -fx-background-color: #c0392b; +} + +/* === Profile image === */ +.profile-picture { + -fx-background-color: #f0f0f0; + -fx-background-radius: 50%; + -fx-border-color: #ddd; + -fx-border-radius: 50%; + -fx-border-width: 1; +} diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/channel_info.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/channel_info.fxml new file mode 100644 index 0000000..f3b9457 --- /dev/null +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/channel_info.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml new file mode 100644 index 0000000..5b28061 --- /dev/null +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/user_info.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/user_info.fxml new file mode 100644 index 0000000..5f5cd36 --- /dev/null +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/user_info.fxml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + +