From 8d1adccec6187575b839862a962f0419f12bcf94 Mon Sep 17 00:00:00 2001 From: filipriec Date: Fri, 18 Apr 2025 22:36:30 +0200 Subject: [PATCH] async registration working --- client/src/modes/handlers/event.rs | 59 +++++++++++++++++++-- client/src/tui/functions/common/register.rs | 8 +++ client/src/ui/handlers/ui.rs | 54 ++++++++++++++++++- 3 files changed, 116 insertions(+), 5 deletions(-) diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 66241f3..a92cdca 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -45,6 +45,7 @@ use crate::config::binds::key_sequences::KeySequenceTracker; use tokio::spawn; use tokio::sync::mpsc; use crate::tui::functions::common::login::LoginResult; +use crate::tui::functions::common::register::RegisterResult; #[derive(Debug, Clone, PartialEq, Eq)] pub enum EventOutcome { @@ -65,10 +66,14 @@ pub struct EventHandler { pub key_sequence_tracker: KeySequenceTracker, pub auth_client: AuthClient, pub login_result_sender: mpsc::Sender, + pub register_result_sender: mpsc::Sender, } impl EventHandler { - pub async fn new(login_result_sender: mpsc::Sender) -> Result { + pub async fn new( + login_result_sender: mpsc::Sender, + register_result_sender: mpsc::Sender, + ) -> Result { Ok(EventHandler { command_mode: false, command_input: String::new(), @@ -80,6 +85,7 @@ impl EventHandler { key_sequence_tracker: KeySequenceTracker::new(800), auth_client: AuthClient::new().await?, login_result_sender, + register_result_sender, }) } @@ -279,11 +285,56 @@ impl EventHandler { login_action_message } UiContext::Register => { - match index { - 0 => register::save(register_state, &mut self.auth_client, app_state).await.context("Register save action failed")?, + let register_action_message = match index { + 0 => { // "Register" button pressed + // Clone necessary data + let username = register_state.username.clone(); + let email = register_state.email.clone(); + let password = register_state.password.clone(); + let password_confirmation = register_state.password_confirmation.clone(); + let role = register_state.role.clone(); + + // 1. Client-side validation (similar to register::save) + if username.trim().is_empty() { + app_state.show_dialog("Registration Failed", "Username cannot be empty.", vec!["OK".to_string()], DialogPurpose::RegisterFailed); + "Username cannot be empty.".to_string() + } else if !password.is_empty() && password != password_confirmation { + app_state.show_dialog("Registration Failed", "Passwords do not match.", vec!["OK".to_string()], DialogPurpose::RegisterFailed); + "Passwords do not match.".to_string() + } else { + // 2. Show Loading Dialog + app_state.show_loading_dialog("Registering", "Please wait..."); + + // 3. Clone sender for the task + let sender = self.register_result_sender.clone(); + + // 4. Spawn the registration task + spawn(async move { + let register_outcome = match AuthClient::new().await { + Ok(mut auth_client) => { + // Handle optional fields correctly for the gRPC call + let password_opt = if password.is_empty() { None } else { Some(password) }; + let password_conf_opt = if password_confirmation.is_empty() { None } else { Some(password_confirmation) }; + let role_opt = if role.is_empty() { None } else { Some(role) }; + + match auth_client.register(username.clone(), email, password_opt, password_conf_opt, role_opt).await { + Ok(response) => register::RegisterResult::Success(response), + Err(e) => register::RegisterResult::Failure(format!("{}", e)), + } + } + Err(e) => register::RegisterResult::ConnectionError(format!("Failed to create AuthClient: {}", e)), + }; + let _ = sender.send(register_outcome).await; + }); + + // 5. Return immediately + "Registration initiated.".to_string() + } + }, 1 => register::back_to_login(register_state, app_state, buffer_state).await, _ => "Invalid Login Option".to_string(), - } + }; + register_action_message } UiContext::Admin => { admin::handle_admin_selection(app_state, admin_state); diff --git a/client/src/tui/functions/common/register.rs b/client/src/tui/functions/common/register.rs index 49d558a..842bdf6 100644 --- a/client/src/tui/functions/common/register.rs +++ b/client/src/tui/functions/common/register.rs @@ -8,8 +8,16 @@ use crate::state::{ }; use crate::ui::handlers::context::DialogPurpose; use crate::state::app::buffer::{AppView, BufferState}; +use common::proto::multieko2::auth::AuthResponse; use anyhow::Result; +#[derive(Debug)] +pub enum RegisterResult { + Success(AuthResponse), + Failure(String), + ConnectionError(String), +} + /// Attempts to register the user using the provided details via gRPC. /// Updates RegisterState and AppState on success or failure. pub async fn save( diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 5b32b47..f92da49 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -22,6 +22,7 @@ use crate::ui::handlers::context::DialogPurpose; use crate::tui::terminal::{EventReader, TerminalCore}; use crate::ui::handlers::render::render_ui; use crate::tui::functions::common::login::LoginResult; +use crate::tui::functions::common::register::RegisterResult; use std::time::Instant; use anyhow::{Context, Result}; use crossterm::cursor::SetCursorStyle; @@ -39,7 +40,12 @@ pub async fn run_ui() -> Result<()> { // --- Channel for Login Results --- let (login_result_sender, mut login_result_receiver) = mpsc::channel::(1); - let mut event_handler = EventHandler::new(login_result_sender.clone()).await.context("Failed to create event handler")?; + let (register_result_sender, mut register_result_receiver) = + mpsc::channel::(1); + let mut event_handler = EventHandler::new( + login_result_sender.clone(), + register_result_sender.clone(), + ).await.context("Failed to create event handler")?; let event_reader = EventReader::new(); let mut auth_state = AuthState::default(); @@ -241,6 +247,52 @@ pub async fn run_ui() -> Result<()> { } } + // --- Check for Register Results from Channel --- + match register_result_receiver.try_recv() { + Ok(register_result) => { + // A result arrived from the register task! + match register_result { + RegisterResult::Success(response) => { + // Update Dialog + let success_message = format!( + "Registration Successful!\n\nUser ID: {}\nUsername: {}\nEmail: {}\nRole: {}", + response.id, response.username, response.email, response.role + ); + app_state.update_dialog_content( // Update loading dialog + &success_message, + vec!["OK".to_string()], // Simple OK for now + DialogPurpose::RegisterSuccess, + ); + info!(message = %success_message, "Registration successful"); + } + RegisterResult::Failure(err_msg) => { + app_state.update_dialog_content( // Update loading dialog + &err_msg, + vec!["OK".to_string()], + DialogPurpose::RegisterFailed, + ); + register_state.error_message = Some(err_msg.clone()); // Keep error message + error!(error = %err_msg, "Registration failed"); + } + RegisterResult::ConnectionError(err_msg) => { + app_state.update_dialog_content( // Update loading dialog + &err_msg, // Show connection error + vec!["OK".to_string()], + DialogPurpose::RegisterFailed, // Still a failure from user perspective + ); + register_state.error_message = Some(err_msg.clone()); + error!(error = %err_msg, "Registration connection error"); + } + } + register_state.set_has_unsaved_changes(false); // Clear unsaved changes flag after processing + needs_redraw = true; // Set flag: Register result processed, UI state (dialog) changed + } + Err(mpsc::error::TryRecvError::Empty) => { /* No message waiting */ } + Err(mpsc::error::TryRecvError::Disconnected) => { + error!("Register result channel disconnected unexpectedly."); + } + } + // --- Centralized Consequence Handling --- let mut should_exit = false; match event_outcome_result {