From fe70aa6bd7bdabf409de4e2a5cd3d2bf190184f1 Mon Sep 17 00:00:00 2001 From: Asal Lotfi Date: Sat, 30 Aug 2025 17:01:58 +0330 Subject: [PATCH] Global search UI implemented. --- .../UI/ChatItemController.java | 14 +- .../UI/ChatPageController.java | 11 +- .../UI/MainController.java | 244 +++++++++++++++--- .../UI/SidebarMenuController.java | 5 +- .../Avatars/default_channel_profile.png | Bin 0 -> 1837 bytes .../Avatars/default_group_profile.png | Bin 0 -> 1478 bytes ...t_profile.png => default_user_profile.png} | Bin .../telegramfinalproject/CSS/dark_theme.css | 87 ++++++- .../telegramfinalproject/CSS/light_theme.css | 108 +++++++- .../to/telegramfinalproject/Fxml/main.fxml | 42 +-- .../Icons/no_result_dark.png | Bin 0 -> 812 bytes .../Icons/no_result_light.png | Bin 0 -> 827 bytes 12 files changed, 446 insertions(+), 65 deletions(-) create mode 100644 src/main/resources/org/to/telegramfinalproject/Avatars/default_channel_profile.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Avatars/default_group_profile.png rename src/main/resources/org/to/telegramfinalproject/Avatars/{default_profile.png => default_user_profile.png} (100%) create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/no_result_dark.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/no_result_light.png diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java b/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java index 270cb94..e4e5d1e 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ChatItemController.java @@ -34,7 +34,7 @@ public class ChatItemController { * @param unread Unread message count * @param imageUrl Path/URL of profile image (can be null/empty) */ - public void setChatData(String name, String lastMsg, String time, int unread, String imageUrl) { + public void setChatData(String name, String lastMsg, String time, int unread, String imageUrl, String chatType) { chatName.setText(name); lastMessage.setText(lastMsg); chatTime.setText(time); @@ -77,9 +77,17 @@ public class ChatItemController { profileImageUser.setManaged(true); } else { + String path; + if ("group".equalsIgnoreCase(chatType)) { + path = "/org/to/telegramfinalproject/Avatars/default_group_profile.png"; + } else if ("channel".equalsIgnoreCase(chatType)) { + path = "/org/to/telegramfinalproject/Avatars/default_channel_profile.png"; + } else { + path = "/org/to/telegramfinalproject/Avatars/default_user_profile.png"; + } + profileImageUser.setImage(new Image( - Objects.requireNonNull(getClass().getResourceAsStream( - "/org/to/telegramfinalproject/Icons/default_profile.png")) + Objects.requireNonNull(getClass().getResourceAsStream(path)) )); profileImageUser.setVisible(true); profileImageUser.setManaged(true); diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java b/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java index aee726f..58d12fc 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java @@ -588,12 +588,10 @@ public class ChatPageController { return (id==null||id.isEmpty()) ? "Unknown" : id.substring(0, Math.min(8,id.length())); } - - private void markAsRead(ChatEntry entry) { JSONObject readReq = new JSONObject(); readReq.put("action", "mark_as_read"); - readReq.put("receiver_id", entry.getId().toString()); // ⛳️ internal_id + readReq.put("receiver_id", entry.getId().toString()); // internal_id readReq.put("receiver_type", entry.getType()); ActionHandler.sendWithResponse(readReq); } @@ -632,10 +630,6 @@ public class ChatPageController { // messageContainer.getChildren().add(row); // } - - - - private void addBubble( boolean outgoing, String displayName, @@ -801,7 +795,4 @@ public class ChatPageController { private static boolean notBlank(String s) { return s != null && !s.isBlank(); } private static String ellipsize(String s, int max) { return s.length() > max ? s.substring(0, max) + "…" : s; } - - - } diff --git a/src/main/java/org/to/telegramfinalproject/UI/MainController.java b/src/main/java/org/to/telegramfinalproject/UI/MainController.java index 132b5bc..2ecd909 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/MainController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/MainController.java @@ -9,6 +9,7 @@ import javafx.scene.Node; 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; @@ -16,25 +17,30 @@ import javafx.util.Duration; import org.to.telegramfinalproject.Models.ChatEntry; import org.to.telegramfinalproject.Client.ActionHandler; import java.io.IOException; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class MainController { // === LEFT PANE === @FXML private VBox chatListContainer; // inside the scrollPane @FXML private ScrollPane scrollPane; // chat list scroll + + // === Global Search === + @FXML private TextField searchBar; + @FXML private VBox globalSearchPane; + @FXML private ListView globalSearchResults; + @FXML private VBox noResultsBox; + @FXML private ImageView noResultIcon; + private enum SearchMode { + GLOBAL, + CHAT + } + private SearchMode currentSearchMode = SearchMode.GLOBAL; + private UUID currentChatId; // if in CHAT mode, which chat to search in + + // === Search In Chat === @FXML private VBox chatSearchPane; // search results panel @FXML private ListView chatSearchResults; - @FXML private MenuButton scopeDropdown; - - // === TOP BAR === - @FXML private TextField searchBar; // global search bar - @FXML private Button menuButton; // === RIGHT PANE === @FXML private VBox leftPane; @@ -137,15 +143,104 @@ public class MainController { // } // }); - // وقتی کاربر Enter زد روی سرچ‌بار، درخواست سرچ بفرست - searchBar.setOnAction(e -> performGlobalSearch(searchBar.getText().trim())); + searchBar.setOnAction(e -> { + String keyword = searchBar.getText().trim(); + if (keyword.isEmpty()) return; - // باز/بسته کردن پنل نتایج با تایپ (دلخواه) - searchBar.textProperty().addListener((obs,o,n)->{ - if (n!=null && !n.isBlank()) showSearchPanel(); + if (currentSearchMode == SearchMode.GLOBAL) { + performGlobalSearch(keyword); + } else if (currentSearchMode == SearchMode.CHAT && currentChatId != null) { + performChatSearch(keyword, currentChatId); + } }); - // کلیک روی نتیجه + searchBar.setOnAction(e -> performGlobalSearch(searchBar.getText().trim())); + + searchBar.textProperty().addListener((obs,o,n)->{ + if (n!=null && !n.isBlank()) showGlobalSearchPanel(); + }); + + globalSearchResults.setOnMouseClicked(e -> { + int idx = globalSearchResults.getSelectionModel().getSelectedIndex(); + if (idx >= 0 && idx < searchBacking.size()) { + openSearchResult(searchBacking.get(idx)); + } + }); + + globalSearchResults.setCellFactory(list -> new ListCell<>() { + private final HBox container = new HBox(10); + private final ImageView avatar = new ImageView(); + private final VBox texts = new VBox(2); + private final Label title = new Label(); + private final Label subtitle = new Label(); + + { + avatar.setFitWidth(36); + avatar.setFitHeight(36); + avatar.getStyleClass().add("global-search-avatar"); + title.getStyleClass().add("global-search-title"); + subtitle.getStyleClass().add("global-search-subtitle"); + texts.getChildren().addAll(title, subtitle); + container.getChildren().addAll(avatar, texts); + } + + @Override + protected void updateItem(SearchResult r, boolean empty) { + super.updateItem(r, empty); + if (empty || r == null) { + setGraphic(null); + } else { + title.setText(r.title); + if (r.type == SRType.MESSAGE) { + subtitle.setText(r.subtitle != null ? r.subtitle : ""); + } else { + subtitle.setText("Press to see messages"); + } + + // Default profile images depending on type + switch (r.type) { + case USER: + avatar.setImage(new Image( + Objects.requireNonNull(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_user_profile.png" + )) + )); + break; + case GROUP: + avatar.setImage(new Image( + Objects.requireNonNull(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_group_profile.png" + )) + )); + break; + case CHANNEL: + avatar.setImage(new Image( + Objects.requireNonNull(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_channel_profile.png" + )) + )); + break; + case MESSAGE: + // For messages, use sender default (user style) + avatar.setImage(new Image( + Objects.requireNonNull(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_user_profile.png" + )) + )); + break; + } + + avatar.setFitWidth(50); + avatar.setFitHeight(50); + avatar.setPreserveRatio(true); + avatar.setSmooth(true); + + setGraphic(container); + } + } + }); + + // CLick on the search result chatSearchResults.setOnMouseClicked(e -> { int idx = chatSearchResults.getSelectionModel().getSelectedIndex(); if (idx >= 0 && idx < searchBacking.size()) { @@ -166,11 +261,25 @@ public class MainController { searchBar.requestFocus(); } + public void showGlobalSearchPanel() { + scrollPane.setVisible(false); + scrollPane.setManaged(false); + + chatSearchPane.setVisible(false); + chatSearchPane.setManaged(false); + + globalSearchPane.setVisible(true); + globalSearchPane.setManaged(true); + } + @FXML public void closeSearchPanel() { chatSearchPane.setVisible(false); chatSearchPane.setManaged(false); + currentSearchMode = SearchMode.GLOBAL; + currentChatId = null; + scrollPane.setVisible(true); scrollPane.setManaged(true); } @@ -282,7 +391,7 @@ public class MainController { Node chatItem = loader.load(); ChatItemController controller = loader.getController(); - cc.setChatData(chat.getName(), preview, timeText, chat.getUnreadCount(), "/org/to/telegramfinalproject/Avatars/default_profile.png"); + cc.setChatData(chat.getName(), preview, timeText, chat.getUnreadCount(), "/org/to/telegramfinalproject/Avatars/default_user_profile.png", chat.getType()); item.setOnMouseClicked(e -> openChat(chat)); chatListContainer.getChildren().add(item); @@ -325,6 +434,9 @@ public class MainController { ChatPageController controller = loader.getController(); controller.showChat(chat); + currentSearchMode = SearchMode.CHAT; + currentChatId = UUID.fromString(chat.getId().toString()); + chatDisplayArea.getChildren().setAll(chatPage); ChatItemController item = itemControllers.get(chat.getId()); @@ -441,11 +553,13 @@ public class MainController { private void updateIconsForDarkMode() { // Example: switch images to white versions menuIcon.setImage(new Image(getClass().getResourceAsStream("/org/to/telegramfinalproject/Icons/menu_light.png"))); + noResultIcon.setImage(new Image(getClass().getResourceAsStream("/org/to/telegramfinalproject/Icons/no_result_light.png"))); } private void updateIconsForLightMode() { // Example: switch images to black versions menuIcon.setImage(new Image(getClass().getResourceAsStream("/org/to/telegramfinalproject/Icons/menu_dark.png"))); + noResultIcon.setImage(new Image(getClass().getResourceAsStream("/org/to/telegramfinalproject/Icons/no_result_dark.png"))); } private void updateLabelsForDarkMode() { @@ -497,6 +611,49 @@ public class MainController { } } + private void performChatSearch(String keyword, UUID chatId) { + if (keyword == null || keyword.isBlank()) { + chatSearchResults.getItems().clear(); + return; + } + + showSearchPanel(); + + org.json.JSONObject req = new org.json.JSONObject(); + req.put("action", "search_in_chat"); + req.put("keyword", keyword); + req.put("chat_id", chatId.toString()); + req.put("user_id", org.to.telegramfinalproject.Client.Session.currentUser.getString("user_id")); + + new Thread(() -> { + org.json.JSONObject resp; + try { + resp = ActionHandler.sendWithResponse(req); + } catch (Exception ex) { + ex.printStackTrace(); + return; + } + + if (resp == null || !"success".equals(resp.optString("status"))) return; + org.json.JSONArray arr = resp.optJSONObject("data").optJSONArray("results"); + if (arr == null) arr = new org.json.JSONArray(); + + java.util.List tmp = new java.util.ArrayList<>(); + for (int i = 0; i < arr.length(); i++) { + org.json.JSONObject it = arr.getJSONObject(i); + String time = it.optString("time", ""); + String content = it.optString("content", "[No content]"); + tmp.add("🗨 " + content + (time.isEmpty() ? "" : " • " + time)); + } + + Platform.runLater(() -> { + chatSearchResults.getItems().setAll(tmp); + chatSearchResults.setVisible(true); + chatSearchResults.setManaged(true); + }); + }).start(); + } + public void performGlobalSearch(String keyword) { if (keyword == null || keyword.isBlank()) { @@ -510,7 +667,7 @@ public class MainController { return; } - showSearchPanel(); + showGlobalSearchPanel(); // درخواست به سرور (مثل کنسول) org.json.JSONObject req = new org.json.JSONObject(); @@ -597,17 +754,46 @@ public class MainController { private String subtitleSep(String s){ return s==null || s.isBlank()? "" : " — "; } - private void renderSearchResults(java.util.List results) { - searchBacking.clear(); - searchBacking.addAll(results); +// private void renderSearchResults(java.util.List results) { +// searchBacking.clear(); +// searchBacking.addAll(results); +// +// javafx.collections.ObservableList view = +// javafx.collections.FXCollections.observableArrayList(results); +// globalSearchResults.setItems(view); +// globalSearchResults.setVisible(true); +// globalSearchResults.setManaged(true); +// } - javafx.collections.ObservableList view = javafx.collections.FXCollections.observableArrayList(); - for (SearchResult r : results) view.add(r.toDisplay()); - chatSearchResults.setItems(view); - chatSearchResults.setVisible(true); - chatSearchResults.setManaged(true); + private void renderSearchResults(List results) { + globalSearchResults.getItems().clear(); + + if (results.isEmpty()) { + noResultsBox.setVisible(true); + noResultsBox.setManaged(true); + globalSearchResults.setVisible(false); + globalSearchResults.setManaged(false); + return; + } + + noResultsBox.setVisible(false); + noResultsBox.setManaged(false); + globalSearchResults.setVisible(true); + globalSearchResults.setManaged(true); + + globalSearchResults.getItems().addAll(results); } + @FXML + public void closeGlobalSearch() { + globalSearchPane.setVisible(false); + globalSearchPane.setManaged(false); + + scrollPane.setVisible(true); + scrollPane.setManaged(true); + + searchBar.clear(); + } private void openSearchResult(SearchResult r) { switch (r.type) { @@ -754,8 +940,4 @@ public class MainController { return false; } - - - - } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/UI/SidebarMenuController.java b/src/main/java/org/to/telegramfinalproject/UI/SidebarMenuController.java index 6a5eff8..d1810ee 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/SidebarMenuController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/SidebarMenuController.java @@ -3,7 +3,6 @@ package org.to.telegramfinalproject.UI; import javafx.animation.TranslateTransition; import javafx.application.Platform; import javafx.fxml.FXML; -import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.image.Image; @@ -48,7 +47,7 @@ public class SidebarMenuController { @FXML public void initialize() { // Load default profile image - Image profile = loadImage("/org/to/telegramfinalproject/Avatars/default_profile.png"); + Image profile = loadImage("/org/to/telegramfinalproject/Avatars/default_user_profile.png"); if (profile != null) profileImage.setImage(profile); setupButtonActions(); @@ -197,7 +196,7 @@ public class SidebarMenuController { String img = user.optString("image_url", ""); Image pic = tryLoadImage(img); if (pic == null) { - pic = loadImage("/org/to/telegramfinalproject/Avatars/default_profile.png"); + pic = loadImage("/org/to/telegramfinalproject/Avatars/default_user_profile.png"); } if (pic != null) profileImage.setImage(pic); } diff --git a/src/main/resources/org/to/telegramfinalproject/Avatars/default_channel_profile.png b/src/main/resources/org/to/telegramfinalproject/Avatars/default_channel_profile.png new file mode 100644 index 0000000000000000000000000000000000000000..f9343f6beb60f6bcc99f4de717f073641d9bdb9f GIT binary patch literal 1837 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/org/to/telegramfinalproject/Avatars/default_profile.png b/src/main/resources/org/to/telegramfinalproject/Avatars/default_user_profile.png similarity index 100% rename from src/main/resources/org/to/telegramfinalproject/Avatars/default_profile.png rename to src/main/resources/org/to/telegramfinalproject/Avatars/default_user_profile.png 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 8360317..5e00d84 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css @@ -1,3 +1,9 @@ +/* ===== Dark Theme Root ===== */ +.root { + -fx-background-color: #0f1c26; /* overall dark base */ + -fx-text-fill: #e8f1f8; +} + /* Sidebar buttons */ .sidebar-btn { -fx-background-color: transparent; @@ -170,7 +176,7 @@ .search-placeholder { -fx-text-fill: #8ea1b2; -fx-font-size: 13px; } /* ===== Global search bar ===== */ -.global-search { +.global-search-pane { -fx-background-color: #1b2b39; -fx-background-radius: 15; -fx-text-fill: #e8f1f8; @@ -294,3 +300,82 @@ -fx-background-radius: 10; -fx-font-weight: bold; } + +/* === Global Search List (Dark) === */ +.global-search-list { + -fx-background-color: #1b2735; /* uniform dark background */ + -fx-control-inner-background: #1b2735; + -fx-control-inner-background-alt: #1b2735; /* disable stripes */ +} + +.global-search-list .virtual-flow, +.global-search-list .clipped-container, +.global-search-list .sheet { + -fx-background-color: #1b2735; /* internal containers same */ +} + +.global-search-list .list-cell { + -fx-padding: 8 10; + -fx-background-color: #1b2735; /* solid dark rows */ + -fx-border-color: #2e3948; /* subtle divider */ + -fx-border-width: 0 0 0.5 0; + -fx-alignment: CENTER_LEFT; + -fx-text-fill: #e8f1f8; /* light text */ +} + +.global-search-list .list-cell:hover { + -fx-background-color: rgba(255, 255, 255, 0.08); /* lighter on hover */ +} + +.global-search-list .list-cell:selected { + -fx-background-color: #2a3a4a; /* darker blue-gray on select */ +} + +/* Titles & subtitles */ +.global-search-title { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #e8f1f8; +} +.global-search-subtitle { + -fx-font-size: 12px; + -fx-text-fill: #8ea1b2; /* muted gray-blue */ +} + +/* Search header */ +.search-header { + -fx-padding: 8 12; + -fx-background-color: #1b2735; + -fx-border-color: #2e3948; + -fx-border-width: 0 0 1 0; +} +.search-header-label { + -fx-font-size: 13px; + -fx-font-weight: bold; + -fx-text-fill: #e8f1f8; +} + +/* No results */ +.no-results-label { + -fx-font-size: 12px; + -fx-text-fill: #8ea1b2; +} + +/* Avatar placeholders */ +.global-search-avatar { + -fx-background-radius: 50%; + -fx-background-color: #444; + -fx-min-width: 36; + -fx-min-height: 36; +} + +/* Close button */ +.search-close-btn { + -fx-background-color: transparent; + -fx-text-fill: #e8f1f8; + -fx-font-size: 14px; + -fx-cursor: hand; +} +.search-close-btn:hover { + -fx-text-fill: #2ca4ff; +} 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 f2b261d..5bf2e2e 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css @@ -1,3 +1,8 @@ +.root { + -fx-base: #ffffff; + -fx-text-base-color: #000000; +} + /* Sidebar buttons */ .sidebar-btn { -fx-background-color: transparent; @@ -195,7 +200,7 @@ -fx-cursor: hand; } -.global-search { +.global-search-pane { -fx-background-color: #f5f5f5; -fx-background-radius: 15; -fx-text-fill: #0f141a; @@ -293,3 +298,104 @@ -fx-background-radius: 10; -fx-font-weight: bold; } + +/* === Global Search List === */ +.global-search-list .list-cell { + -fx-padding: 8 10; + -fx-background-color: transparent; + -fx-border-color: #ddd; /* light divider */ + -fx-border-width: 0 0 0.5 0; + -fx-alignment: CENTER_LEFT; +} + +.global-search-list .list-cell:hover { + -fx-background-color: rgba(0, 0, 0, 0.05); /* light hover */ +} + +.global-search-title { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: black; +} + +.global-search-subtitle { + -fx-font-size: 12px; + -fx-text-fill: #666; /* gray for light mode */ +} + +/* Avatar placeholder (if no image) */ +.global-search-avatar { + -fx-background-radius: 50%; + -fx-background-color: #bbb; + -fx-min-width: 36; + -fx-min-height: 36; +} + +/* Global search panel */ +.global-search-pane { + -fx-background-color: -fx-base; +} + +.search-header { + -fx-padding: 8 12; + -fx-background-color: transparent; + -fx-border-color: #ddd; + -fx-border-width: 0 0 1 0; +} + +.search-header-label { + -fx-font-size: 13px; + -fx-font-weight: bold; + -fx-text-fill: -fx-text-base-color; +} + +.no-results-label { + -fx-font-size: 12px; + -fx-text-fill: gray; +} + +.close-btn { + -fx-background-color: transparent; + -fx-cursor: hand; + -fx-font-size: 14px; +} + +/* ===== Global Search Result Panel ===== */ +.search-results-container { + -fx-background-color: -fx-base; + -fx-padding: 8; + -fx-spacing: 6; + -fx-border-color: transparent transparent #ddd transparent; + -fx-border-width: 0 0 1 0; +} + +.search-results-title { + -fx-font-weight: bold; + -fx-font-size: 13px; + -fx-text-fill: -fx-text-base-color; +} + +.search-result-item { + -fx-background-color: transparent; + -fx-padding: 6 10; + -fx-spacing: 8; +} + +.search-result-item:hover { + -fx-background-color: rgba(0,0,0,0.05); +} + +/* Avatar always square, not squeezed */ +.search-result-avatar { + -fx-fit-width: 40; + -fx-fit-height: 40; + -fx-preserve-ratio: true; +} + +/* Close button */ +.search-close-btn { + -fx-background-color: transparent; + -fx-text-fill: -fx-text-base-color; + -fx-font-size: 14px; + -fx-cursor: hand; +} diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/main.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/main.fxml index d6db77f..62f4177 100644 --- a/src/main/resources/org/to/telegramfinalproject/Fxml/main.fxml +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/main.fxml @@ -69,44 +69,54 @@ - + -