Merge remote-tracking branch 'origin/Sidebar-Menu' into Sidebar-Menu

# Conflicts:
#	src/main/java/org/to/telegramfinalproject/Client/SidebarHandler.java
This commit is contained in:
2025-08-13 11:07:09 +03:30
35 changed files with 563 additions and 152 deletions
@@ -2555,16 +2555,16 @@ public class ClientHandler implements Runnable {
break; break;
} }
case "get_saved_messages": { // case "get_saved_messages": {
UUID user_Id = UUID.fromString(requestJson.getString("user_id")); // UUID user_Id = UUID.fromString(requestJson.getString("user_id"));
response = SidebarService.handleGetSavedMessages(user_Id); // response = SidebarService.handleGetSavedMessages(user_Id);
break; // break;
} // }
//
case "send_saved_messages": { // case "send_saved_messages": {
response = SidebarService.handleSendMessage(requestJson); // response = SidebarService.handleSendMessage(requestJson);
break; // break;
} // }
case "search_contacts": { case "search_contacts": {
String user_id = requestJson.getString("user_id"); String user_id = requestJson.getString("user_id");
@@ -211,86 +211,81 @@ public class SidebarService {
} }
} }
public static ResponseModel handleGetSavedMessages(UUID userId) { // public static ResponseModel handleGetSavedMessages(UUID userId) {
try { // try {
//
UUID chatId = PrivateChatDatabase.getOrCreateSavedMessagesChat(userId); // UUID chatId = PrivateChatDatabase.getOrCreateSavedMessagesChat(userId);
if (chatId == null) { // if (chatId == null) {
return new ResponseModel("error", "Failed to create or find saved messages chat."); // return new ResponseModel("error", "Failed to create or find saved messages chat.");
} // }
//
List<Message> messages = MessageDatabase.privateChatHistory(chatId, userId); // List<Message> messages = MessageDatabase.privateChatHistory(chatId, userId);
//
JSONArray messageArray = new JSONArray(); // JSONArray messageArray = new JSONArray();
if (!messages.isEmpty()) { // if (!messages.isEmpty()) {
for (Message msg : messages) { // for (Message msg : messages) {
JSONObject msgJson = new JSONObject(); // JSONObject msgJson = new JSONObject();
msgJson.put("message_id", msg.getMessage_id().toString()); // msgJson.put("message_id", msg.getMessage_id().toString());
msgJson.put("sender_id", msg.getSender_id().toString()); // msgJson.put("sender_id", msg.getSender_id().toString());
msgJson.put("receiver_type", msg.getReceiver_type()); // msgJson.put("receiver_type", msg.getReceiver_type());
msgJson.put("receiver_id", msg.getReceiver_id().toString()); // msgJson.put("receiver_id", msg.getReceiver_id().toString());
msgJson.put("content", msg.getContent()); // msgJson.put("content", msg.getContent());
msgJson.put("message_type", msg.getMessage_type()); // msgJson.put("message_type", msg.getMessage_type());
msgJson.put("send_at", msg.getSend_at().toString()); // LocalDateTime // msgJson.put("send_at", msg.getSend_at().toString()); // LocalDateTime
msgJson.put("status", msg.getStatus()); // msgJson.put("status", msg.getStatus());
msgJson.put("reply_to_id", msg.getReply_to_id() != null ? msg.getReply_to_id().toString() : JSONObject.NULL); // msgJson.put("reply_to_id", msg.getReply_to_id() != null ? msg.getReply_to_id().toString() : JSONObject.NULL);
msgJson.put("is_edited", msg.isIs_edited()); // msgJson.put("is_edited", msg.isIs_edited());
msgJson.put("original_message_id", msg.getOriginal_message_id() != null ? msg.getOriginal_message_id().toString() : JSONObject.NULL); // msgJson.put("original_message_id", msg.getOriginal_message_id() != null ? msg.getOriginal_message_id().toString() : JSONObject.NULL);
msgJson.put("forwarded_by", msg.getForwarded_by() != null ? msg.getForwarded_by().toString() : JSONObject.NULL); // msgJson.put("forwarded_by", msg.getForwarded_by() != null ? msg.getForwarded_by().toString() : JSONObject.NULL);
msgJson.put("forwarded_from", msg.getForwarded_from() != null ? msg.getForwarded_from().toString() : JSONObject.NULL); // msgJson.put("forwarded_from", msg.getForwarded_from() != null ? msg.getForwarded_from().toString() : JSONObject.NULL);
//
messageArray.put(msgJson); // messageArray.put(msgJson);
} // }
} // }
//
JSONObject data = new JSONObject(); // JSONObject data = new JSONObject();
data.put("chat_id", chatId.toString()); // data.put("chat_id", chatId.toString());
data.put("messages", messageArray); // data.put("messages", messageArray);
//
return new ResponseModel("success", "Saved messages retrieved successfully", data); // return new ResponseModel("success", "Saved messages retrieved successfully", data);
//
} catch (Exception e) { // } catch (Exception e) {
e.printStackTrace(); // e.printStackTrace();
return new ResponseModel("error", "Unexpected server error."); // return new ResponseModel("error", "Unexpected server error.");
} // }
} // }
// Save messages to DB // Save messages to DB
public static ResponseModel handleSendMessage(JSONObject requestJson) { // public static ResponseModel handleSendMessage(JSONObject requestJson) {
try { // try {
Message message = new Message( // Message message = new Message(
UUID.fromString(requestJson.getString("message_id")), // UUID.fromString(requestJson.getString("message_id")),
UUID.fromString(requestJson.getString("sender_id")), // UUID.fromString(requestJson.getString("sender_id")),
requestJson.getString("receiver_type"), // requestJson.getString("receiver_type"),
UUID.fromString(requestJson.getString("receiver_id")), // UUID.fromString(requestJson.getString("receiver_id")),
requestJson.optString("content", null), // requestJson.optString("content", null),
requestJson.optString("message_type", "TEXT"), // requestJson.optString("message_type", "TEXT"),
LocalDateTime.now(), // send_at // LocalDateTime.now(), // send_at
requestJson.optString("status", "SEND"), // requestJson.optString("status", "SEND"),
requestJson.isNull("reply_to_id") ? null : UUID.fromString(requestJson.getString("reply_to_id")), // requestJson.isNull("reply_to_id") ? null : UUID.fromString(requestJson.getString("reply_to_id")),
requestJson.optBoolean("is_edited", false), // requestJson.optBoolean("is_edited", false),
requestJson.isNull("original_message_id") ? null : UUID.fromString(requestJson.getString("original_message_id")), // requestJson.isNull("original_message_id") ? null : UUID.fromString(requestJson.getString("original_message_id")),
requestJson.isNull("forwarded_by") ? null : UUID.fromString(requestJson.getString("forwarded_by")), // requestJson.isNull("forwarded_by") ? null : UUID.fromString(requestJson.getString("forwarded_by")),
requestJson.isNull("forwarded_from") ? null : UUID.fromString(requestJson.getString("forwarded_from")), // requestJson.isNull("forwarded_from") ? null : UUID.fromString(requestJson.getString("forwarded_from")),
requestJson.optBoolean("is_deleted_globally", false), // requestJson.optBoolean("is_deleted_globally", false),
requestJson.isNull("edited_at") ? null : // requestJson.isNull("edited_at") ? null :
LocalDateTime.ofInstant( // LocalDateTime.ofInstant(
Instant.ofEpochMilli(requestJson.getLong("edited_at")), // Instant.ofEpochMilli(requestJson.getLong("edited_at")),
ZoneId.systemDefault() // ZoneId.systemDefault()
) // )
); // );
//
MessageDatabase.insertSavedMessage(message); // MessageDatabase.insertSavedMessage(message);
return new ResponseModel("success", "Message saved successfully."); // return new ResponseModel("success", "Message saved successfully.");
//
} catch (Exception e) { // } catch (Exception e) {
e.printStackTrace(); // e.printStackTrace();
return new ResponseModel("error", "Unexpected server error."); // return new ResponseModel("error", "Unexpected server error.");
} // }
} // }
// Changes username
public static boolean updateUserName(String userId, String newUserName) {
return false;
}
} }
@@ -1,6 +1,5 @@
package org.to.telegramfinalproject.UI; package org.to.telegramfinalproject.UI;
import javafx.animation.TranslateTransition;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
@@ -10,12 +9,10 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.util.Duration;
import javafx.scene.shape.Circle; import javafx.scene.shape.Circle;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
@@ -125,7 +122,7 @@ public class IntroController {
@FXML @FXML
private void handleStartMessaging(ActionEvent event) { private void handleStartMessaging(ActionEvent event) {
try { try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/login_view.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/Fxml/login_view.fxml"));
Scene loginScene = new Scene(loader.load()); Scene loginScene = new Scene(loader.load());
// Get the current stage and its dimensions // Get the current stage and its dimensions
@@ -0,0 +1,30 @@
package org.to.telegramfinalproject.UI;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import org.to.telegramfinalproject.Models.User;
public class MyProfileController {
@FXML private ImageView profileImage;
@FXML private Label displayName;
@FXML private Label status;
@FXML private Label bio;
@FXML private Label userId;
// public void initialize() {
// // Load data from your logged-in user object
// // Example:
// displayName.setText(CurrentUser.getName());
// status.setText(CurrentUser.isOnline() ? "online" : "offline");
// bio.setText(CurrentUser.getBio());
// userId.setText(CurrentUser.getUserId());
//
// if (CurrentUser.getProfileImagePath() != null) {
// profileImage.setImage(new Image(CurrentUser.getProfileImagePath()));
// }
// }
}
@@ -1,11 +1,25 @@
package org.to.telegramfinalproject.UI; package org.to.telegramfinalproject.UI;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.*; import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import java.net.URL;
public class SidebarMenuController { public class SidebarMenuController {
private static final String ICON_PATH = "/org/to/telegramfinalproject/Icons/";
@FXML private VBox sidebarRoot;
@FXML private ImageView profileImage; @FXML private ImageView profileImage;
@FXML private Label usernameLabel; @FXML private Label usernameLabel;
@@ -18,14 +32,149 @@ public class SidebarMenuController {
@FXML private Button telegramFeaturesButton; @FXML private Button telegramFeaturesButton;
@FXML private Button telegramQnAButton; @FXML private Button telegramQnAButton;
@FXML private ToggleButton nightModeToggle; // Custom Telegram-style toggle
@FXML private HBox nightModeToggle;
@FXML private Region toggleThumb;
@FXML private ImageView nightModeIcon;
// convenience handle
private final ThemeManager themeManager = ThemeManager.getInstance();
@FXML @FXML
public void initialize() { public void initialize() {
Image image = new Image(getClass().getResource("/org/to/telegramfinalproject/Images/profile.png").toExternalForm()); // Load default profile image (if present)
profileImage.setImage(image); Image profile = loadImage("/org/to/telegramfinalproject/Images/profile.png");
if (profile != null) profileImage.setImage(profile);
// Called automatically after FXML is loaded setupButtonActions();
usernameLabel.setText("Asal"); setupToggleAction();
// When the Scene is ready:
sidebarRoot.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene != null) {
// 1) Register with ThemeManager so this scene auto-updates on theme changes
themeManager.registerScene(newScene);
// 2) Make sure we start in LIGHT mode (if that's what you want)
// (If you already set the initial mode elsewhere, remove the next line)
themeManager.setDarkMode(false);
// 3) Sync icons & toggle with current mode
boolean dark = themeManager.isDarkMode();
updateIcons(dark);
syncToggleVisual(dark, /*animate=*/true);
// Ensure the thumb is correctly positioned after layout pass
Platform.runLater(() -> syncToggleVisual(themeManager.isDarkMode(), false));
} }
});
// If theme is changed from somewhere else (another screen), keep sidebar in sync
themeManager.darkModeProperty().addListener((o, wasDark, isDark) -> {
updateIcons(isDark);
syncToggleVisual(isDark, /*animate=*/true);
});
}
private void setupButtonActions() {
myProfileButton.setOnAction(e -> openMyProfile());
newGroupButton.setOnAction(e -> createNewGroup());
newChannelButton.setOnAction(e -> createNewChannel());
contactsButton.setOnAction(e -> openContacts());
savedMessagesButton.setOnAction(e -> openSavedMessages());
settingsButton.setOnAction(e -> openSettings());
telegramFeaturesButton.setOnAction(e -> openTelegramFeatures());
telegramQnAButton.setOnAction(e -> openTelegramQnA());
}
private void setupToggleAction() {
nightModeToggle.setOnMouseClicked(e -> {
boolean newDark = !themeManager.isDarkMode();
// Update the global theme via ThemeManager
themeManager.setDarkMode(newDark);
// Animate the thumb to its new position (listener will handle icons & final position sync)
syncToggleVisual(newDark, /*animate=*/true);
});
}
/** Update all button icons to match theme.
* darkMode == true => white icons => use *_light.png
* darkMode == false => dark icons => use *_dark.png
*/
private void updateIcons(boolean darkMode) {
String suffix = darkMode ? "_light.png" : "_dark.png";
myProfileButton.setGraphic(makeIcon(ICON_PATH + "my_profile" + suffix));
newGroupButton.setGraphic(makeIcon(ICON_PATH + "new_group" + suffix));
newChannelButton.setGraphic(makeIcon(ICON_PATH + "new_channel" + suffix));
contactsButton.setGraphic(makeIcon(ICON_PATH + "contacts" + suffix));
savedMessagesButton.setGraphic(makeIcon(ICON_PATH + "saved_messages" + suffix));
settingsButton.setGraphic(makeIcon(ICON_PATH + "settings" + suffix));
telegramFeaturesButton.setGraphic(makeIcon(ICON_PATH + "telegram_features" + suffix));
telegramQnAButton.setGraphic(makeIcon(ICON_PATH + "telegram_qna" + suffix));
// Night-mode moon icon
Image moon = loadImage(ICON_PATH + "night_mode" + suffix);
if (moon != null) nightModeIcon.setImage(moon);
}
/** Keep the toggles CSS class and thumb position in sync with the current mode. */
private void syncToggleVisual(boolean darkMode, boolean animate) {
// CSS class "on" on the track
if (darkMode) {
if (!nightModeToggle.getStyleClass().contains("on")) {
nightModeToggle.getStyleClass().add("on");
}
} else {
nightModeToggle.getStyleClass().remove("on");
}
// Compute target X for the thumb
double offX = 2;
double onX = Math.max(2, nightModeToggle.getWidth() - toggleThumb.getWidth() - 4);
double targetX = darkMode ? onX : offX;
if (animate) {
TranslateTransition tt = new TranslateTransition(Duration.millis(200), toggleThumb);
tt.setToX(targetX);
tt.play();
} else {
toggleThumb.setTranslateX(targetX);
}
}
// --- helpers -------------------------------------------------------------
private Image loadImage(String path) {
URL res = getClass().getResource(path);
if (res == null) {
System.err.println("Resource not found: " + path);
return null;
}
return new Image(res.toExternalForm());
}
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;
}
// Example button actions
private void openMyProfile() { System.out.println("Opening My Profile..."); }
private void createNewGroup() { System.out.println("Creating New Group..."); }
private void createNewChannel() { System.out.println("Creating New Channel..."); }
private void openContacts() { System.out.println("Opening Contacts..."); }
private void openSavedMessages() { System.out.println("Opening Saved Messages..."); }
private void openSettings() { System.out.println("Opening Settings..."); }
private void openTelegramFeatures() { System.out.println("Opening Telegram Features..."); }
private void openTelegramQnA() { System.out.println("Opening Telegram Q&A..."); }
} }
@@ -5,17 +5,16 @@ import javafx.application.Application;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.to.telegramfinalproject.HelloApplication;
import java.io.IOException; import java.io.IOException;
public class TelegramApplication extends Application { public class TelegramApplication extends Application {
@Override @Override
public void start(Stage stage) throws IOException { public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(TelegramApplication.class.getResource("/org/to/telegramfinalproject/sidebar_menu.fxml")); FXMLLoader fxmlLoader = new FXMLLoader(TelegramApplication.class.getResource("/org/to/telegramfinalproject/Fxml/sidebar_menu.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 1480, 820); Scene scene = new Scene(fxmlLoader.load(), 1480, 820);
scene.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/sidebar_menu.css").toExternalForm()); scene.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/light_theme.css").toExternalForm());
stage.setTitle("Telegram"); stage.setTitle("Telegram");
stage.setScene(scene); stage.setScene(scene);
@@ -0,0 +1,58 @@
package org.to.telegramfinalproject.UI;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import java.util.ArrayList;
import java.util.List;
public class ThemeManager {
private static final ThemeManager instance = new ThemeManager();
private final BooleanProperty darkMode = new SimpleBooleanProperty(false);
private final List<Scene> registeredScenes = new ArrayList<>();
private ThemeManager() {
// Whenever darkMode changes, update all registered scenes
darkMode.addListener((obs, oldVal, newVal) -> applyThemeToAll());
}
public static ThemeManager getInstance() {
return instance;
}
public BooleanProperty darkModeProperty() {
return darkMode;
}
public boolean isDarkMode() {
return darkMode.get();
}
public void setDarkMode(boolean dark) {
darkMode.set(dark);
}
public void registerScene(Scene scene) {
if (!registeredScenes.contains(scene)) {
registeredScenes.add(scene);
applyTheme(scene); // Apply current theme immediately
}
}
private void applyThemeToAll() {
for (Scene scene : registeredScenes) {
applyTheme(scene);
}
}
private void applyTheme(Scene scene) {
scene.getStylesheets().clear();
if (isDarkMode()) {
scene.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/dark_theme.css").toExternalForm());
} else {
scene.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/light_theme.css").toExternalForm());
}
}
}
@@ -0,0 +1,52 @@
/* Sidebar buttons */
.sidebar-btn {
-fx-background-color: transparent;
-fx-text-fill: white;
-fx-font-size: 14px;
-fx-pref-width: 230;
-fx-alignment: CENTER_LEFT;
-fx-padding: 8 0 8 16;
}
.sidebar-btn:hover {
-fx-background-color: #2f3e50;
-fx-cursor: hand;
}
/* Telegram-style toggle switch */
.telegram-toggle {
-fx-background-color: #555;
-fx-background-radius: 15;
-fx-padding: 2;
-fx-pref-width: 40;
-fx-pref-height: 20;
-fx-alignment: center-left;
-fx-cursor: hand;
}
.telegram-toggle.on {
-fx-background-color: #4fa8f0;
}
.toggle-thumb {
-fx-background-color: white;
-fx-background-radius: 50%;
-fx-pref-width: 16;
-fx-pref-height: 16;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 3, 0, 0, 1);
}
/* Separator */
.thin-separator {
-fx-background-color: transparent;
-fx-padding: 0;
}
.thin-separator .line {
-fx-border-color: #2e3948; /* Telegram's subtle dark line */
-fx-border-width: 0.5px;
}
/* Theme background & text */
#sidebarRoot {
-fx-background-color: #1b2735; /* dark theme */
}
.button, .label {
-fx-text-fill: white;
}
@@ -0,0 +1,52 @@
/* Sidebar buttons */
.sidebar-btn {
-fx-background-color: transparent;
-fx-text-fill: black;
-fx-font-size: 14px;
-fx-pref-width: 230;
-fx-alignment: CENTER_LEFT;
-fx-padding: 8 0 8 16;
}
.sidebar-btn:hover {
-fx-background-color: #e6e6e6;
-fx-cursor: hand;
}
/* Telegram-style toggle switch */
.telegram-toggle {
-fx-background-color: #aaa;
-fx-background-radius: 15;
-fx-padding: 2;
-fx-pref-width: 40;
-fx-pref-height: 20;
-fx-alignment: center-left;
-fx-cursor: hand;
}
.telegram-toggle.on {
-fx-background-color: #4fa8f0;
}
.toggle-thumb {
-fx-background-color: white;
-fx-background-radius: 50%;
-fx-pref-width: 16;
-fx-pref-height: 16;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 3, 0, 0, 1);
}
/* Separator */
.thin-separator {
-fx-background-color: transparent;
-fx-padding: 0;
}
.thin-separator .line {
-fx-border-color: #ccc;
-fx-border-width: 0.5px;
}
/* Theme background & text */
#sidebarRoot {
-fx-background-color: white;
}
.button, .label {
-fx-text-fill: black;
}
@@ -0,0 +1,26 @@
.my-profile-root {
-fx-background-color: white;
}
.profile-image {
-fx-clip-radius: 40; /* Rounded image */
}
.profile-name {
-fx-font-size: 18px;
-fx-font-weight: bold;
}
.profile-status {
-fx-text-fill: green;
-fx-font-size: 14px;
}
.field-label {
-fx-font-weight: bold;
-fx-text-fill: #333;
}
.field-value {
-fx-text-fill: #555;
}
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.text.Text?>
<AnchorPane xmlns:fx="http://javafx.com/fxml" fx:controller="org.to.telegramfinalproject.UI.MyProfileController"
prefWidth="400" prefHeight="600"
styleClass="my-profile-root">
<VBox spacing="15" padding="20">
<!-- Top Bar -->
<HBox alignment="CENTER_LEFT" spacing="10">
<ImageView fx:id="profileImage" fitHeight="80" fitWidth="80"
styleClass="profile-image"/>
<VBox>
<Label fx:id="displayName" text="Display Name" styleClass="profile-name"/>
<Label fx:id="status" text="online" styleClass="profile-status"/>
</VBox>
</HBox>
<Separator/>
<!-- Bio -->
<HBox spacing="10" alignment="CENTER_LEFT">
<Label text="Bio:" styleClass="field-label"/>
<Label fx:id="bio" text="This is the bio..." wrapText="true" styleClass="field-value"/>
</HBox>
<!-- User ID -->
<HBox spacing="10" alignment="CENTER_LEFT">
<Label text="User ID:" styleClass="field-label"/>
<Label fx:id="userId" text="@user_id" styleClass="field-value"/>
</HBox>
</VBox>
</AnchorPane>
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Separator?>
<VBox fx:id="sidebarRoot" spacing="20" alignment="TOP_LEFT"
prefWidth="250.0"
xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.to.telegramfinalproject.UI.SidebarMenuController">
<padding>
<Insets top="20" left="10" right="10" bottom="10"/>
</padding>
<!-- Profile Section -->
<VBox alignment="TOP_LEFT" spacing="10">
<padding>
<Insets top="10" left="10" bottom="0" right="0"/>
</padding>
<ImageView fx:id="profileImage" fitWidth="80" fitHeight="80" pickOnBounds="true" preserveRatio="true"/>
<Label fx:id="usernameLabel" text=" Asal Lotfi" textFill="white" style="-fx-font-size: 14px; -fx-font-weight: bold;"/>
</VBox>
<!-- Menu Buttons -->
<VBox spacing="8" alignment="TOP_LEFT">
<!-- Thin Telegram-style line -->
<Separator orientation="HORIZONTAL" styleClass="thin-separator" />
<Button fx:id="myProfileButton" text=" My Profile" styleClass="sidebar-btn"/>
<!-- Thin Telegram-style line -->
<Separator orientation="HORIZONTAL" styleClass="thin-separator" />
<Button fx:id="newGroupButton" text=" New Group" styleClass="sidebar-btn"/>
<Button fx:id="newChannelButton" text=" New Channel" styleClass="sidebar-btn"/>
<Button fx:id="contactsButton" text=" Contacts" styleClass="sidebar-btn"/>
<Button fx:id="savedMessagesButton" text=" Saved Messages" styleClass="sidebar-btn"/>
<Button fx:id="settingsButton" text=" Settings" styleClass="sidebar-btn"/>
<Button fx:id="telegramFeaturesButton" text=" Telegram Features" styleClass="sidebar-btn"/>
<Button fx:id="telegramQnAButton" text=" Telegram Q&amp;A" styleClass="sidebar-btn"/>
<!-- Night Mode Toggle -->
<HBox spacing="10" alignment="CENTER_LEFT">
<padding>
<!-- Match button's left padding -->
<Insets left="15"/>
</padding>
<ImageView fx:id="nightModeIcon" fitWidth="22" fitHeight="22" pickOnBounds="true" preserveRatio="true"/>
<Label text="Night Mode" textFill="white" style="-fx-font-size: 13px;"/>
<HBox fx:id="nightModeToggle" styleClass="telegram-toggle">
<Region fx:id="toggleThumb" styleClass="toggle-thumb"/>
</HBox>
</HBox>
</VBox>
</VBox>
Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Separator?>
<VBox fx:id="sidebar" spacing="20" alignment="TOP_LEFT"
prefWidth="250.0"
style="-fx-background-color: #1b2735;"
xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.to.telegramfinalproject.UI.SidebarMenuController">
<padding>
<Insets top="20" left="10" right="10" bottom="10"/>
</padding>
<!-- Profile Section -->
<VBox alignment="TOP_LEFT" spacing="10">
<padding>
<Insets top="10" left="10" bottom="0" right="0"/>
</padding>
<ImageView fx:id="profileImage" fitWidth="80" fitHeight="80" pickOnBounds="true" preserveRatio="true"/>
<Label fx:id="usernameLabel" text="Asal Lotfi" textFill="white" style="-fx-font-size: 14px; -fx-font-weight: bold;"/>
</VBox>
<Separator prefWidth="200"/>
<!-- Menu Buttons -->
<VBox spacing="8" alignment="TOP_LEFT">
<Button fx:id="btnMyProfile" text=" My Profile" styleClass="sidebar-btn"/>
<Button fx:id="btnNewGroup" text=" New Group" styleClass="sidebar-btn"/>
<Button fx:id="btnNewChannel" text=" New Channel" styleClass="sidebar-btn"/>
<Button fx:id="btnContacts" text=" Contacts" styleClass="sidebar-btn"/>
<Button fx:id="btnCalls" text=" Calls" styleClass="sidebar-btn"/>
<Button fx:id="btnSavedMessages" text=" Saved Messages" styleClass="sidebar-btn"/>
<Button fx:id="btnSettings" text=" Settings" styleClass="sidebar-btn"/>
<!-- Night Mode Toggle -->
<HBox spacing="10" alignment="CENTER_LEFT">
<Label text="Night Mode" textFill="white" style="-fx-font-size: 13px;"/>
<ToggleButton fx:id="nightModeToggle"/>
</HBox>
</VBox>
</VBox>