use eframe::egui; use std::sync::mpsc::Receiver; use crate::api::ApiClient; use crate::models::LoginResponse; use crate::session::SessionManager; pub struct LoginScreen { server_url: String, username: String, password: String, remember_server: bool, remember_username: bool, error_message: Option, is_logging_in: bool, // For async operations login_receiver: Option>>, } impl LoginScreen { pub fn new(session_manager: &SessionManager) -> Self { let server_url = session_manager .get_saved_server_url() .unwrap_or_else(|| "http://localhost:5777".to_string()); // Reminder to myself : Fucking remove this before release let username = session_manager.get_saved_username().unwrap_or_default(); let remember_username = !username.is_empty(); Self { server_url, username, password: String::new(), remember_server: true, remember_username, error_message: None, is_logging_in: false, login_receiver: None, } } pub fn show(&mut self, ctx: &egui::Context, on_success: &mut Option<(String, LoginResponse)>) { // Check if we have a login result from async operation if let Some(receiver) = &self.login_receiver { match receiver.try_recv() { Ok(result) => { log::info!("UI thread: Received login result!"); self.is_logging_in = false; self.login_receiver = None; match result { Ok((server_url, login_response)) => { log::info!("UI thread: Login successful, setting on_success"); *on_success = Some((server_url, login_response)); } Err(err) => { log::error!("UI thread: Login failed: {} tried suicide yet maybe that actually works?", err); self.error_message = Some(err); } } } Err(std::sync::mpsc::TryRecvError::Empty) => { // Still waiting, request repaint to check again ctx.request_repaint(); } Err(std::sync::mpsc::TryRecvError::Disconnected) => { log::error!("UI thread: Channel disconnected!"); self.is_logging_in = false; self.login_receiver = None; self.error_message = Some("Connection error".to_string()); } } } egui::CentralPanel::default().show(ctx, |ui| { // Center the login form both horizontally and vertically bruh let available_size = ui.available_size(); let panel_width = available_size.x.min(500.0); ui.allocate_ui_with_layout( available_size, egui::Layout::top_down(egui::Align::Center), |ui| { // Add vertical spacing to center ui.add_space(available_size.y * 0.15); // Logo/Title ui.heading(egui::RichText::new("BeepZone Login").size(48.0).strong()); ui.label( egui::RichText::new(format!( "BeepZone Desktop Client eGUI EMO Edition - v{}", env!("CARGO_PKG_VERSION") )) .size(18.0), ); ui.add_space(30.0); // Login form egui::Frame::new() .fill(ctx.style().visuals.window_fill) .stroke(ctx.style().visuals.window_stroke) .corner_radius(12.0) .inner_margin(32.0) .show(ui, |ui| { ui.set_width(panel_width * 0.8); // Server URL ui.horizontal(|ui: &mut egui::Ui| { ui.label("BeepZone Sekel API URL:"); ui.add_space(10.0); }); ui.text_edit_singleline(&mut self.server_url); ui.add_space(8.0); // Username field ui.label("Username:"); ui.text_edit_singleline(&mut self.username); ui.add_space(8.0); // Password field ui.label("Password:"); let password_response = ui .add(egui::TextEdit::singleline(&mut self.password).password(true)); // Enter key to submit if password_response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { self.do_login(); } ui.add_space(12.0); // Remember options ui.checkbox(&mut self.remember_server, "Remember Sekel URL"); ui.checkbox(&mut self.remember_username, "Remember Username"); ui.add_space(16.0); // Error message if let Some(error) = &self.error_message { ui.label(format!("Error: {}", error)); ui.add_space(12.0); } // Login button ui.add_enabled_ui(!self.is_logging_in, |ui| { let button_text = if self.is_logging_in { "Logging in..." } else { "Login" }; if ui .add_sized( [ui.available_width(), 40.0], egui::Button::new(button_text), ) .clicked() { self.do_login(); } }); if self.is_logging_in { ui.add_space(8.0); ui.horizontal(|ui| { ui.spinner(); ui.label("Connecting to server..."); }); } }); ui.add_space(20.0); ui.label( egui::RichText::new("- The only based Sigma Inventory System -") .size(12.0) .color(egui::Color32::GRAY), ); // Debug info if self.is_logging_in { ui.add_space(10.0); ui.label( egui::RichText::new(format!("Connecting to: {}", self.server_url)) .size(10.0) .color(egui::Color32::GRAY), ); } }, ); }); } fn do_login(&mut self) { // Validate inputs if self.server_url.trim().is_empty() { self.error_message = Some("Server URL is required!? Perhaps enter it you know?".to_string()); return; } if self.username.trim().is_empty() || self.password.trim().is_empty() { self.error_message = Some( "Username and password are required!? Please enter both you know?".to_string(), ); return; } self.error_message = None; self.is_logging_in = true; // Clone data for background thread let server_url = self.server_url.clone(); let username = self.username.clone(); let password = self.password.clone(); log::info!("Trying to sign in as : {}", username); // Create channel for communication let (tx, rx) = std::sync::mpsc::channel(); self.login_receiver = Some(rx); // Spawn background thread to perform login std::thread::spawn(move || { log::info!("Background thread: Connecting to {}", server_url); let result = match ApiClient::new(server_url.clone()) { Ok(client) => { log::info!("Background thread: API client created, attempting login..."); match client.login_password(&username, &password) { Ok(response) => { log::info!( "Background thread: Got response, success={}", response.success ); if response.success { log::info!( "Login successfulf for user: {}", response.user.username ); Ok((server_url, response)) } else { let error = "Login failed gay credentials".to_string(); log::error!("{}", error); Err(error) } } Err(e) => { let error = format!("Notwork error: {}", e); log::error!("{}", error); Err(error) } } } Err(e) => { let error = format!("Failed at connecting to server somehow: {}", e); log::error!("{}", error); Err(error) } }; log::info!("Background thread: Sending result back to UI"); if let Err(e) = tx.send(result) { log::error!("Background thread: Failed to send result: {:?}", e); } }); } }