From 5f84e6af55f4e29d7cd7bd14e3b1fde85d6ba0f5 Mon Sep 17 00:00:00 2001 From: Asal Lotfi Date: Sun, 7 Sep 2025 10:35:56 +0330 Subject: [PATCH] Group info UI + backend implemented. --- .../Database/GroupDatabase.java | 79 ++++++ .../Server/ClientHandler.java | 22 ++ .../UI/BlockedUsersController.java | 2 +- .../UI/ChatPageController.java | 45 +++- .../UI/GroupInfoController.java | 242 +++++++++++++++++- .../UI/NewChannelController.java | 4 +- .../telegramfinalproject/CSS/dark_theme.css | 56 ++++ .../telegramfinalproject/CSS/light_theme.css | 55 ++++ .../telegramfinalproject/Fxml/chat_page.fxml | 42 ++- .../telegramfinalproject/Fxml/group_info.fxml | 123 ++++++++- .../Icons/add_member_dark.png | Bin 0 -> 1305 bytes .../Icons/add_member_light.png | Bin 0 -> 1134 bytes .../telegramfinalproject/Icons/group_dark.png | Bin 0 -> 899 bytes .../Icons/group_light.png | Bin 0 -> 920 bytes .../Icons/group_member_dark.png | Bin 0 -> 915 bytes .../Icons/group_member_light.png | Bin 0 -> 339 bytes .../Icons/manage_dark.png | Bin 0 -> 801 bytes .../Icons/manage_light.png | Bin 0 -> 684 bytes 18 files changed, 638 insertions(+), 32 deletions(-) create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/add_member_dark.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/add_member_light.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/group_dark.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/group_light.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/group_member_dark.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/group_member_light.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/manage_dark.png create mode 100644 src/main/resources/org/to/telegramfinalproject/Icons/manage_light.png diff --git a/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java index e64917d..259fe86 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java @@ -679,4 +679,83 @@ public class GroupDatabase { // return result; // } + public static JSONObject getGroupInfo(UUID groupId, UUID viewerUuid) throws SQLException { + JSONObject result = new JSONObject(); + + try (Connection conn = ConnectionDb.connect()) { + // === Group header (name, member count, etc.) + String groupQuery = """ + SELECT g.internal_uuid, g.group_id, g.group_name, g.image_url, + COUNT(m.user_id) as member_count + FROM groups g + LEFT JOIN group_members m ON g.internal_uuid = m.group_id + WHERE g.internal_uuid = ? + GROUP BY g.internal_uuid, g.group_id, g.group_name, g.image_url + """; + + try (PreparedStatement ps = conn.prepareStatement(groupQuery)) { + ps.setObject(1, groupId); // ✅ compare UUID with UUID + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + result.put("internal_uuid", rs.getString("internal_uuid")); + result.put("group_id", rs.getString("group_id")); // varchar handle + result.put("group_name", rs.getString("group_name")); + result.put("member_count", rs.getInt("member_count")); + result.put("image_url", rs.getString("image_url")); + } else { + return null; // no such group + } + } + + // === Members (id, name, role, status, image) + JSONArray membersArr = new JSONArray(); + + String membersQuery = """ + SELECT u.internal_uuid, u.profile_name, u.user_id, u.image_url, + gm.role, u.status, u.last_seen + FROM group_members gm + JOIN users u ON gm.user_id = u.internal_uuid + WHERE gm.group_id = ? + """; + + try (PreparedStatement ps = conn.prepareStatement(membersQuery)) { + ps.setObject(1, groupId); // ✅ gm.group_id is UUID + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + JSONObject member = new JSONObject(); + member.put("user_id", rs.getString("internal_uuid")); + member.put("profile_name", rs.getString("profile_name")); + member.put("username", rs.getString("user_id")); + member.put("image_url", rs.getString("image_url")); + member.put("role", rs.getString("role")); + member.put("status", rs.getString("status")); + member.put("last_seen", rs.getString("last_seen")); + membersArr.put(member); + } + } + + result.put("members", membersArr); + + // === Viewer role (to decide if they can delete group, etc.) + String roleQuery = """ + SELECT role + FROM group_members + WHERE group_id = ? AND user_id = ? + """; + + try (PreparedStatement ps = conn.prepareStatement(roleQuery)) { + ps.setObject(1, groupId); + 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 member + } + } + } + + return result; + } + } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index f72258a..a85e9c6 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -2997,7 +2997,29 @@ public class ClientHandler implements Runnable { break; } + case "view_group": { + if (currentUser == null) { + response = new ResponseModel("error", "Unauthorized. Please login first."); + break; + } + try { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + UUID viewerId = UUID.fromString(requestJson.getString("viewer_id")); + + // Query group details + JSONObject groupData = GroupDatabase.getGroupInfo(groupId, viewerId); + + if (groupData == null) { + response = new ResponseModel("error", "Group not found."); + } else { + response = new ResponseModel("success", "Group info fetched.", groupData); + } + } catch (Exception e) { + response = new ResponseModel("error", "Error processing group info: " + e.getMessage()); + } + break; + } default: diff --git a/src/main/java/org/to/telegramfinalproject/UI/BlockedUsersController.java b/src/main/java/org/to/telegramfinalproject/UI/BlockedUsersController.java index e92249a..1539b5c 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/BlockedUsersController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/BlockedUsersController.java @@ -51,7 +51,7 @@ public class BlockedUsersController { ThemeManager.getInstance().darkModeProperty().addListener((obs, oldVal, newVal) -> updateBackIcon(newVal)); updateBackIcon(ThemeManager.getInstance().isDarkMode()); - // اسکرول نرم + // Smooth scroll feel blockedList.getStylesheets().add( getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm() ); diff --git a/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java b/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java index 801a559..872d47e 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/ChatPageController.java @@ -954,28 +954,38 @@ public class ChatPageController { } private void configureHeaderActions(ChatEntry entry) { - chatMoreMenu.getItems().clear(); + // Hide everything by default + archiveItem.setVisible(false); + viewProfileItem.setVisible(false); + deleteChatItem.setVisible(false); + viewGroupItem.setVisible(false); + leaveGroupItem.setVisible(false); + viewChannelItem.setVisible(false); + leaveChannelItem.setVisible(false); switch (entry.getType().toLowerCase(Locale.ROOT)) { case "private" -> { + archiveItem.setVisible(true); + viewProfileItem.setVisible(true); + deleteChatItem.setVisible(true); + archiveItem.setOnAction(e -> toggleArchive(entry)); viewProfileItem.setOnAction(e -> openInfoScene(entry)); deleteChatItem.setOnAction(e -> deleteChatButton(entry)); - chatMoreMenu.getItems().addAll(archiveItem, viewProfileItem, deleteChatItem); } 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)); - chatMoreMenu.getItems().addAll(viewGroup, leaveGroup); + viewGroupItem.setVisible(true); + leaveGroupItem.setVisible(true); + + viewGroupItem.setOnAction(e -> openInfoScene(entry)); + leaveGroupItem.setOnAction(e -> leaveGroupButton(entry)); } 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)); - chatMoreMenu.getItems().addAll(viewChannel, leaveChannel); + viewChannelItem.setVisible(true); + leaveChannelItem.setVisible(true); + + viewChannelItem.setOnAction(e -> openInfoScene(entry)); + leaveChannelItem.setOnAction(e -> leaveChannelButton(entry)); } } @@ -1027,7 +1037,8 @@ public class ChatPageController { } case "group" -> { req.put("action", "view_group") - .put("group_id", entry.getId().toString()); + .put("group_id", entry.getId().toString()) + .put("viewer_id", Session.getUserUUID()); } case "channel" -> { req.put("action", "view_channel") @@ -3036,6 +3047,14 @@ public class ChatPageController { if (deleteChatItem != null && deleteChatItem.getGraphic() instanceof ImageView iv) { iv.setImage(loadIcon("delete_red.png")); // stays red in both themes } + + if (viewGroupItem != null && viewGroupItem.getGraphic() instanceof ImageView iv) { + iv.setImage(loadIcon("group" + suffix)); + } + + if (viewChannelItem != null && viewChannelItem.getGraphic() instanceof ImageView iv) { + iv.setImage(loadIcon("group" + suffix)); + } } // ChatPageController diff --git a/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java b/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java index e0d4a76..b71f1ce 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/GroupInfoController.java @@ -1,11 +1,249 @@ 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.to.telegramfinalproject.Client.ActionHandler; +import org.to.telegramfinalproject.Client.AvatarLocalResolver; import org.to.telegramfinalproject.Models.ChatEntry; +import java.io.IOException; +import java.net.URL; + public class GroupInfoController { + + @FXML private VBox groupCard; + @FXML private Pane overlayBackground; + @FXML private Button closeButton; + + @FXML private ImageView groupImage; + @FXML private Label groupName; + @FXML private Label memberCount; + + @FXML private Button infoMoreButton; + @FXML private ContextMenu infoMoreMenu; + @FXML private MenuItem addMemberItem; + @FXML private MenuItem manageGroupItem; + @FXML private MenuItem deleteGroupItem; + @FXML private ImageView moreIcon; + + @FXML private VBox membersList; + @FXML private Label membersHeader; + @FXML private Button addMemberButton; + @FXML private ImageView membersIcon; + @FXML private ScrollPane membersScroll; + + private String groupId; // the UUID of this group + + private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/"; + + @FXML + private void initialize() { + closeButton.setOnAction(e -> + MainController.getInstance().closeOverlay(groupCard.getParent())); + overlayBackground.setOnMouseClicked(e -> + MainController.getInstance().closeOverlay(groupCard.getParent())); + + infoMoreButton.setOnAction(e -> { + if (infoMoreMenu != null) infoMoreMenu.show(infoMoreButton, javafx.geometry.Side.BOTTOM, 0, 0); + }); + + deleteGroupItem.setOnAction(e -> handleDeleteGroup()); + + Platform.runLater(() -> { + if (groupCard.getScene() != null) { + ThemeManager.getInstance().registerScene(groupCard.getScene()); + } + }); + + ThemeManager.getInstance().darkModeProperty().addListener((obs, oldVal, newVal) -> { + updateIcons(newVal); + }); + + updateIcons(ThemeManager.getInstance().isDarkMode()); + + // Smooth scroll feel + membersScroll.getStylesheets().add( + getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm() + ); + membersScroll.skinProperty().addListener((obs, oldSkin, newSkin) -> { + if (newSkin != null) { + ScrollBar vBar = (ScrollBar) membersScroll.lookup(".scroll-bar:vertical"); + if (vBar != null) { + membersScroll.setOnScroll(event -> { + double deltaY = event.getDeltaY() * 0.003; + double newValue = vBar.getValue() - deltaY; + vBar.setValue(Math.max(0, Math.min(newValue, 1))); + }); + } + } + }); + + if (addMemberButton != null) { + addMemberButton.setOnAction(e -> openAddMemberScene()); + } + if (addMemberItem != null) { + addMemberItem.setOnAction(e -> openAddMemberScene()); + } + } + public void setGroupDataFromJson(ChatEntry entry, JSONObject data) { - // read name, status, bio, image url from JSON - // update labels/images accordingly + this.groupId = data.optString("internal_uuid", entry.getId().toString()); + + groupName.setText(data.optString("group_name", entry.getName())); + memberCount.setText(data.optInt("member_count", 0) + " members"); + + // show/hide Delete button if user is owner + String myRole = data.optString("my_role", "member"); + if ("owner".equalsIgnoreCase(myRole)) { + deleteGroupItem.setVisible(true); + } else { + deleteGroupItem.setVisible(false); + } + + // Group picture + String imgUrl = data.optString("image_url", ""); + if (!imgUrl.isBlank()) { + try { + Image img = AvatarLocalResolver.load(imgUrl); + if (img != null) groupImage.setImage(img); + } catch (Exception ignore) {} + } else { + groupImage.setImage( + new Image(getClass().getResourceAsStream( + "/org/to/telegramfinalproject/Avatars/default_group_profile.png")) + ); + } + + // Members + membersList.getChildren().clear(); + var arr = data.optJSONArray("members"); + if (arr != null) { + membersHeader.setText(arr.length() + " MEMBERS"); + for (int i = 0; i < arr.length(); i++) { + JSONObject m = arr.getJSONObject(i); + addMemberRow(m); + } + } + } + + private void addMemberRow(JSONObject m) { + 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 = m.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(m.optString("profile_name", "Unknown")); + name.getStyleClass().add("member-name"); + + // Status = online / last seen recently + String status; + if (m.optBoolean("is_online", false)) { + status = "online"; + } else { + status = ChatPageController.getInstance().userStatusText( + false, + m.optString("last_seen", null) + ); + } + Label statusLbl = new Label(status); + statusLbl.getStyleClass().add("member-status"); + + nameBox.getChildren().addAll(name, statusLbl); + + // === Role (owner/admin/member) === + Label role = new Label(); + String roleStr = m.optString("role", ""); + if (!roleStr.isBlank()) { + role.setText(roleStr.toLowerCase()); + role.getStyleClass().add("member-role"); + } + + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + + row.getChildren().addAll(avatar, nameBox, spacer, role); + membersList.getChildren().add(row); + } + + private void openAddMemberScene() { + try { + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/org/to/telegramfinalproject/Fxml/add_member.fxml")); + Node overlay = loader.load(); + + AddMembersController controller = loader.getController(); + // 👇 Pass group id so AddMember scene knows which group to add to + controller.setGroupForAdd(groupId, groupName); + + MainController.getInstance().showOverlay(overlay); + } catch (IOException e) { + e.printStackTrace(); + MainController.getInstance().showAlert( + "Error", + "Could not load Add Member scene.", + Alert.AlertType.ERROR + ); + } + } + + private void handleDeleteGroup() { + System.out.println("Deleting group..."); + // TODO: implement backend call + } + + private void updateIcons(boolean dark) { + String suffix = dark ? "_light.png" : "_dark.png"; + + moreIcon.setImage(loadImage(ICON_PATH + "more" + suffix)); + membersIcon.setImage(loadImage(ICON_PATH + "group" + suffix)); + addMemberButton.setGraphic(makeIcon(ICON_PATH + "add_member" + suffix)); + membersIcon.setImage(loadImage(ICON_PATH + "group_member" + suffix)); + + } + + // --- helpers ------------------------------------------------------------- + + 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()); } } diff --git a/src/main/java/org/to/telegramfinalproject/UI/NewChannelController.java b/src/main/java/org/to/telegramfinalproject/UI/NewChannelController.java index 620b4ab..5988989 100644 --- a/src/main/java/org/to/telegramfinalproject/UI/NewChannelController.java +++ b/src/main/java/org/to/telegramfinalproject/UI/NewChannelController.java @@ -63,7 +63,7 @@ public class NewChannelController { createButton.setOnAction(e -> onCreateChannel()); - // محدودیت توضیح + // Limit description final int MAX_LENGTH = 255; channelDescField.addEventFilter(javafx.scene.input.KeyEvent.KEY_TYPED, e -> { if (channelDescField.getText().length() >= MAX_LENGTH) e.consume(); @@ -79,7 +79,7 @@ public class NewChannelController { }); descCounter.setText("0 / 255"); - // پاک کردن استایل خطا هنگام تایپ + // Reset error when user types again channelNameField.textProperty().addListener((obs, ov, nv) -> { if (!nv.trim().isEmpty()) { channelNameField.getStyleClass().remove("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 5a5e578..f85b051 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/dark_theme.css @@ -1132,3 +1132,59 @@ -fx-border-radius: 50%; -fx-border-width: 1; } + +.member-row { + -fx-alignment: CENTER_LEFT; +} + +.member-scroll { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.profile-card { + -fx-background-radius: 12; + -fx-background-color: #1f2a36; /* dark gray card */ + -fx-padding: 16; +} + +.profile-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #e8f1f8; /* light text */ +} + + +.member-row { + -fx-padding: 6 0; +} + +.member-name { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #e8f1f8; /* Telegram light text */ +} + +.member-status { + -fx-font-size: 12px; + -fx-text-fill: #8ea1b2; /* grayish */ +} + +.member-role { + -fx-font-size: 12px; + -fx-text-fill: #4fa8f0; /* blue */ + -fx-font-weight: bold; +} + +/* Group name (bold, light text) */ +.info-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #e8f1f8; /* light gray/white */ +} + +/* Members count (blue link style like Telegram dark mode) */ +.info-subtitle { + -fx-font-size: 13px; + -fx-text-fill: #4fa8f0; /* same Telegram blue */ +} 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 b701157..fd946ba 100644 --- a/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css +++ b/src/main/resources/org/to/telegramfinalproject/CSS/light_theme.css @@ -1123,3 +1123,58 @@ -fx-border-radius: 50%; -fx-border-width: 1; } + +.member-row { + -fx-alignment: CENTER_LEFT; +} + +.member-scroll { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.profile-card { + -fx-background-radius: 12; + -fx-background-color: #ffffff; /* pure white card */ + -fx-padding: 16; +} + +.profile-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #0f141a; /* dark text */ +} + +.member-row { + -fx-padding: 6 0; +} + +.member-name { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: #0f141a; /* Telegram dark gray for names */ +} + +.member-status { + -fx-font-size: 12px; + -fx-text-fill: #8ea1b2; /* grayish */ +} + +.member-role { + -fx-font-size: 12px; + -fx-text-fill: #4fa8f0; /* blue */ + -fx-font-weight: bold; +} + +/* Group name (bold, dark text) */ +.info-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #0f141a; /* deep gray */ +} + +/* Members count (blue link style like Telegram) */ +.info-subtitle { + -fx-font-size: 13px; + -fx-text-fill: #4fa8f0; /* Telegram blue */ +} diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/chat_page.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/chat_page.fxml index f208933..308fc29 100644 --- a/src/main/resources/org/to/telegramfinalproject/Fxml/chat_page.fxml +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/chat_page.fxml @@ -50,28 +50,60 @@ - + + - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml b/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml index 5b28061..a962a03 100644 --- a/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml +++ b/src/main/resources/org/to/telegramfinalproject/Fxml/group_info.fxml @@ -1,14 +1,119 @@ - - - - + + + - + + - + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/to/telegramfinalproject/Icons/add_member_dark.png b/src/main/resources/org/to/telegramfinalproject/Icons/add_member_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..2aab4c1c971492e4e33b861ee5c77332f92b6f2d GIT binary patch literal 1305 zcmV+!1?KvRP)kam95e-Bst*>u%GIpkqX1i%7%`SNsuC z69*AL^uv#UU~mEB@?nDDMiiss5=DmS&-fuas33_81_`J^F^VA|5fY;_Dr(edB3EC> zYx%n8z1Kbc`eP2%Pfyi-_tahL-aE$3VGjL;QmJ&9_x?%%&yZYC@-vbf0lY%;4r}cK z)6W2KD1h4lOq1N(ZUJ}?z?r7sP^naoCHcur1cJ2(z&*y80n-n#)-C|>ZJ^{%l24Jm z&N+7pfXe~gPV(IZ>pb2M?lh0&7Nyvq0PdAIX>;Z*lAi`-ZtAV(0pfseD)T#l%S;Lk z4GkRu;4O9bw{p2$H$}i&dm_m{lrl@RoUc}^M+ep%H$?!z{ffj##+d!G%5ctIp)Fdg z)s8Z~27oV=5;x=&GXP+lvdYz_mx!Y15FM=E`zWt00M9B}D@`v^E|=@-66!3oRZLV^;Y&($kgp&1WcQs7`0AH({yru&=Uv?pB z8>Fq3;evCnCaxnLqxALO`>UiG(+MJBNqjAL z&YdO|gTzPlkITuMvnrvj-T~m$yd*9K@Jk?Y(tCe$n)5;|NWXHs-vhY5UB(kgd;oYL zj+^G$ZIw!eCJUk{Ivv2TN{OFIF3#bSJC%&B&biU7O&R{15Z*~$yh^bM;Hyr}3gCHd zi6jzng)Ob{7D*&6$Xu(fDz2NkzKX4~C@Tr6r}SGAyHnD>Q`Da6HuA>RzRfdQh2%mB zW_1Jdf_Kz^NMN^$iL^wlwey3ty)cVAY?>)3+Yni&KDxZkzEng6!DvaQm&uUnhR#xH z=T}LHlf&O~B*iv=kD}t;BO86egL>t?VU(Fwwcab`^aD}NV)SA!L1n| z!!_#cV)grmmI?rPup5K4YPAZjjIOgfAp+pyAmEch@^vNT=DbA2k~(4NF_H}VN~`WxEuB9S|8L`GMN^=Im literal 0 HcmV?d00001 diff --git a/src/main/resources/org/to/telegramfinalproject/Icons/add_member_light.png b/src/main/resources/org/to/telegramfinalproject/Icons/add_member_light.png new file mode 100644 index 0000000000000000000000000000000000000000..a5fdc035f63b333d27e705488c5d1ca443120105 GIT binary patch literal 1134 zcmV-!1d;oRP)p%t#PoL@ivA~UQlO5!XZ<-D85MO zXhRG=*n_Jpikkld(!5S1VBta->7so`iGUpcy){)ij zJ7><>=es@356;VX_WplseS7b<*IHi?^wLXt0Sp8d0^5P(zy;t8upd|jObp5yU;ywo z&_&OD12hNa2Jj?sj&yVbYk-QN8~`Q)zdA<#0&E3d1LguR11o{Ab2zlQ9Gs{Ht{9^p z0Po2;iJ9{(aNcp|jpADFpa(kSZ(x3q0AMh1%*_1@7#oxVU;^-uF=koH`HwoQxulWwhQgTA%sqcmX`^L>%u+P1BZ;&wX{W+#i<5GH7ivIv)qc2DoMncr}C04_Sjo zCV*|mfa9JTFF3i^$ZS@50L*Z<*J6*z*J>LY)U62b6yM70KV)k78;-Cu6_1>iQNf#%zEf$N6dprb))25w2cW zePfQYT5M?abz&ayha)JVcyhw|1D)_I+ya)wMLa>y2e3iUO^B2NbwL_{>A;^xQ4`YY>xgPe78~+>o(_A58M`-b;G4iYea2;UL{jmP zjd^mU)3*ohjn)$(vPEh#-Xk+h(!kK9&!+7gd>sKQfzxK((SkeR7_h?m6&FOLGN0=6 zVT8^{Mn{53Q&Ai+2K3XSC`7W@wiNR#r7OvQ*;H7oqm3DpqcD>Mu%U-}&pF#kI05u6 zp7sAXs?qs>1Wf>sY1*+EcuDVx4ktQUCx(^G3FAzi7DZ|4mhDv1m5BJ~szvLGBn})Y z`oZ?xZtntL0^b6cbo)qiZy7ME)He3gOArMA0`b~{1;St1!TY1V#QUWeh4MnqzP)wK_8`F>} z#dSArKy=-XYB6L*ak=|a6j_tCx^73M4LN60l*35{7nPuE*;Lw)hl=N=ZyLWYsdS|b zX_8VR&vPw}aK;o?uJ2SSLk1MbhEKVxlgBOTJC)mzWL%z88sbP&6m zQg)vS&Z{9=bf;;_xJoT8+LG1}d+eqgmkGp#A#}~NQlsDK9FIZTC=F#zJnC`Gk{+iq zqiZQv%QRodFB=7O(szo56Os&S9m}QoM3>maA#$z6*z0e`O*qs7nMKaYvald)5)h*Aa#FZA3U5(+LeBw;b zWB|TnLxNp%xs9*}^-2d&XU)aNwj_W(mHs?k2wABWa?-MyS3ek-muES7CvhrXLk%?$ ZjDI&@ueTQM2JZj>002ovPDHLkV1i66m-PSu literal 0 HcmV?d00001 diff --git a/src/main/resources/org/to/telegramfinalproject/Icons/group_light.png b/src/main/resources/org/to/telegramfinalproject/Icons/group_light.png new file mode 100644 index 0000000000000000000000000000000000000000..4e802efdb72d4feb747a58fa79d8c261d275f7b4 GIT binary patch literal 920 zcmV;J184k+P)kzL zK&5OiySP-&QG2^raISie}g4=i_A@7a(0fbJg-9PT~qzt*~YpY_@| zlc}JB3W^5c2yg)y0G7rE2lz@j zHqA=a72Ao+!1@kGtpF21H_!%T&DCZ;n=sGh!bjjzF&wA`9_BFM6EFyza^PwLhJcM6 zeD{HxLcYkaEQQ~Iju55h$LR#VTih>2XCa(ewiG=Djz-kc08CptmZCXheldF`?gCX& z6l~X34GdelUI6=JklXJ>M-;1f*V$$1ycZ)nZCizOh?*Xx0bGt zddkR=+fH?xgtl1WH?kv>imT5xA+D5>Bddxln?|7Cs#=>$?FevM@rZ4Q(+bK2Fu_n6 zI5MJm=6%a}DWPjiW~3cweVvHN07TS6U@BjyFGYR(6Fc3wuHB|VJI}m=CgkDOuACMf(k0g#K0d9AFCesUzj%l0000P)W8?8k`Y;Iw4K+3(4QM z0eA+WrKVBTTF=s2&nMXw#)ssQAaM@0KCSiosuDNGXaHR#d##E40bDc2%q!*xpgj=r zrvMK7_R)I&-F4mRRUr=`NAg{X2>v3uA?VlK+&l)r8|%B1*=%-f{=L@P0C;4HI2}8A zrBoU~Pni@L6aiqd?W?t3VUg=er_&P(j6c`<=9@95F;)R24}>6}xBIp)fNj>hT@=ysM6@WAq45F2{{)1r{~iEhdJ%gJIrzg}n#V@#uL zU~3F7HUT6T4nx3h%ikk8;J+KtS}!XQq`j8!li0||u>!t`v(|gL$TrGmPN&lfzkoUl%V&h2Kklf>+IK2Qq$;MVnwKm(QPOSi7oZOaDz=g=$dXlXqy9<@l{JVmZ zPF+f=B~=$7k(BxCEL?%!2e1*jG(Lduj&Zwv@E6H2dIM3}S zs4M^+ii5mm2O~w$8N9ih0(+x1l%wB4Rhl+I>nFL1Dhs(RW=pQ$->J^7{qA)v!kL0)Wkt3OxW9t0dr@QwwCX*-7&EfaKShmjAQV pIH^=>Vod}jNRS{wf&|V8!5{Ri?c%chsUrXY002ovPDHLkV1h6*vhe@_ literal 0 HcmV?d00001 diff --git a/src/main/resources/org/to/telegramfinalproject/Icons/group_member_light.png b/src/main/resources/org/to/telegramfinalproject/Icons/group_member_light.png new file mode 100644 index 0000000000000000000000000000000000000000..1af6c7409bbe75956d783896e197a2784841686f GIT binary patch literal 339 zcmV-Z0j&OsP)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkP1xZ9fRCt{2(@!e}VHC&l!9o_q*2-)|DHe8@H{e^aHAX#1tML{XC;{g_6%{LrtsjUzDQZwJQdH1OK`8if7D4gsUGU3HoF+WMdus#JP(l|NNx;t^kY7s zUu~a@$mUiMcH)!#5qPIl`)Q0Rx&B-W`9>pw*aiT63&H?6*b2gp*m3}0rV9dK!e0&D zXw*8Ik0key{NCh;8bbI2PyukBDBDum3EfG07ozMa>5IMAb6$*u=BwvRhp4o*!B~6~^^#XY1hV)4+SEn|Val5si zc0Nj#YPJjY#CR9L<0f`1_Pa&^yCNp2Nv-ud6;}ZmYa`R%RB;ujCc^swPKE3@s0}lf z1P~GFZ{R=C48+bG>u&%b$w(d-k(I8wIa=$z0pFpv4(K`saYv?KyHFT@un+(aH}H?l zI7qh+u>p*`qtbx}{?Qo+>C&~r4U!uIo3XP2-_^+13Skfc`@?D!LTy7Zyu=C$` fGgYco|DApT*+(vd|A_#M00000NkvXXu0mjf-S|@T literal 0 HcmV?d00001 diff --git a/src/main/resources/org/to/telegramfinalproject/Icons/manage_light.png b/src/main/resources/org/to/telegramfinalproject/Icons/manage_light.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd2bf847b7ed29805b29ee72d5c0282cc63ee80 GIT binary patch literal 684 zcmV;d0#p5oP)o5lxQscCKiEc6|J=L1E$f0q!9!a0ybiy zSO^L#8dM_rXb4GVNLAN+xgA` zpak5?SW3WQtGzYAHH}RI?Y zBSE4^iU!p(#gwQ3^a4}CPO*_lpa9E(OS)jSNr5HqThnq$7J-CeL_!2um6V*R!~?2% z3baU|yT13D{9UhCLX*oeO)0JdoAvXRz_}c$%Q$ca*x)PD=X7?m4vBUr;WWI|*a(f` zp|8ja;EhxHT`dU@k>LduF#zj^ve3x>lG5_E;J7pcR|&zE(&OIi3(7O%n+U;DvLe^z~l7s)Zr;$ix$?*q8k{Aw< S7TWdz0000