refactor happend and its perfectly fine

This commit is contained in:
filipriec
2025-04-18 23:16:22 +02:00
parent 8d1adccec6
commit dc6c1ce43c
4 changed files with 99 additions and 196 deletions

View File

@@ -11,7 +11,7 @@ use anyhow::{Context, Result};
use crate::tui::functions::common::{
form::{save as form_save, revert as form_revert},
login::{save as login_save, revert as login_revert},
register::{save as register_save, revert as register_revert},
register::{revert as register_revert},
};
pub async fn handle_core_action(
@@ -32,9 +32,6 @@ pub async fn handle_core_action(
if app_state.ui.show_login {
let message = login_save(auth_state, login_state, auth_client, app_state).await.context("Login save action failed")?;
Ok(EventOutcome::Ok(message))
} else if app_state.ui.show_register {
let message = register_save(register_state, auth_client, app_state).await.context("Register save_and_quit action failed")?;
Ok(EventOutcome::Ok(message))
} else {
let save_outcome = form_save(
form_state,
@@ -57,8 +54,6 @@ pub async fn handle_core_action(
"save_and_quit" => {
let message = if app_state.ui.show_login {
login_save(auth_state, login_state, auth_client, app_state).await.context("Login save n quit action failed")?
} else if app_state.ui.show_register {
register_save(register_state, auth_client, app_state).await.context("Register save n quit action failed")?
} else {
let save_outcome = form_save(
form_state,

View File

@@ -12,11 +12,7 @@ use anyhow::{Context, Result};
use crate::tui::{
terminal::core::TerminalCore,
functions::{
common::{
form::SaveOutcome,
login,
register,
},
common::{form::SaveOutcome, login, register},
},
{intro, admin},
};
@@ -42,8 +38,8 @@ use crate::modes::{
};
use crate::functions::modes::navigation::{admin_nav, add_table_nav};
use crate::config::binds::key_sequences::KeySequenceTracker;
use tokio::spawn;
use tokio::sync::mpsc;
use tokio::spawn;
use crate::tui::functions::common::login::LoginResult;
use crate::tui::functions::common::register::RegisterResult;
@@ -286,50 +282,8 @@ impl EventHandler {
}
UiContext::Register => {
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()
}
0 => {
register::initiate_registration(register_state, app_state, self.register_result_sender.clone())
},
1 => register::back_to_login(register_state, app_state, buffer_state).await,
_ => "Invalid Login Option".to_string(),

View File

@@ -9,7 +9,10 @@ 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;
use anyhow::Context;
use tokio::spawn;
use tokio::sync::mpsc;
use tracing::{info, error};
#[derive(Debug)]
pub enum RegisterResult {
@@ -18,109 +21,6 @@ pub enum RegisterResult {
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(
register_state: &mut RegisterState,
auth_client: &mut AuthClient,
app_state: &mut AppState,
) -> Result<String> {
let username = register_state.username.clone();
let email = register_state.email.clone();
// Handle optional passwords: send None if empty, Some(value) otherwise
let password = if register_state.password.is_empty() {
None
} else {
Some(register_state.password.clone())
};
let password_confirmation = if register_state.password_confirmation.is_empty() {
None
} else {
Some(register_state.password_confirmation.clone())
};
let role = if register_state.role.is_empty() {
None
} else {
Some(register_state.role.clone())
};
// Basic client-side validation (example)
if username.is_empty() {
app_state.show_dialog(
"Registration Failed",
"Username cannot be empty.",
vec!["OK".to_string()],
DialogPurpose::RegisterFailed,
);
register_state.error_message = Some("Username cannot be empty.".to_string());
return Ok("Registration failed: Username cannot be empty.".to_string());
}
if password.is_some() && password != password_confirmation {
app_state.show_dialog(
"Registration Failed",
"Passwords do not match.",
vec!["OK".to_string()],
DialogPurpose::RegisterFailed,
);
register_state.error_message = Some("Passwords do not match.".to_string());
return Ok("Registration failed: Passwords do not match.".to_string());
}
// Clear previous error/dialog state before attempting
register_state.error_message = None;
app_state.hide_dialog();
// Call the gRPC register method
match auth_client.register(username, email, password, password_confirmation, role).await {
Ok(response) => {
// Clear fields on success? Optional, maybe wait for dialog confirmation.
// register_state.username.clear();
// register_state.email.clear();
// register_state.password.clear();
// register_state.password_confirmation.clear();
register_state.set_has_unsaved_changes(false);
let success_message = format!(
"Registration Successful!\n\n\
User ID: {}\n\
Username: {}\n\
Email: {}\n\
Role: {}",
response.id,
response.username,
response.email,
response.role
);
// Show success dialog
app_state.show_dialog(
"Registration Success",
&success_message,
vec!["OK".to_string()], // Simple OK for now
DialogPurpose::RegisterSuccess,
);
Ok("Registration successful, details shown in dialog.".to_string())
}
Err(e) => {
let error_message = format!("{}", e);
register_state.error_message = Some(error_message.clone());
register_state.set_has_unsaved_changes(true); // Keep changes on error
// Show error dialog
app_state.show_dialog(
"Registration Failed",
&error_message,
vec!["OK".to_string()],
DialogPurpose::RegisterFailed,
);
Ok(format!("Registration failed: {}", error_message))
}
}
}
/// Clears the registration form fields.
pub async fn revert(
register_state: &mut RegisterState,
@@ -161,3 +61,89 @@ pub async fn back_to_login(
"Returned to main menu".to_string()
}
/// Validates input, shows loading, and spawns the registration task.
pub fn initiate_registration(
register_state: &RegisterState,
app_state: &mut AppState,
sender: mpsc::Sender<RegisterResult>,
) -> String {
// 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
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. 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
.with_context(|| format!("Spawned register task failed for username: {}", username))
{
Ok(response) => RegisterResult::Success(response),
Err(e) => RegisterResult::Failure(format!("{}", e)),
}
}
Err(e) => RegisterResult::ConnectionError(format!("Failed to create AuthClient: {}", e)),
};
// Send result back to the main UI thread
if let Err(e) = sender.send(register_outcome).await {
error!("Failed to send registration result: {}", e);
}
});
// 4. Return immediately
"Registration initiated.".to_string()
}
}
/// Handles the result received from the registration task.
/// Returns true if a redraw is needed.
pub fn handle_registration_result(
result: RegisterResult,
app_state: &mut AppState,
register_state: &mut RegisterState,
) -> bool {
match result {
RegisterResult::Success(response) => {
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(
&success_message,
vec!["OK".to_string()],
DialogPurpose::RegisterSuccess,
);
info!(message = %success_message, "Registration successful");
}
RegisterResult::Failure(err_msg) | RegisterResult::ConnectionError(err_msg) => {
app_state.update_dialog_content(
&err_msg,
vec!["OK".to_string()],
DialogPurpose::RegisterFailed,
);
register_state.error_message = Some(err_msg.clone());
error!(error = %err_msg, "Registration failed/connection error");
}
}
register_state.set_has_unsaved_changes(false); // Clear flag after processing
true // Request redraw as dialog content changed
}

View File

@@ -23,6 +23,7 @@ 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 crate::tui::functions::common::register;
use std::time::Instant;
use anyhow::{Context, Result};
use crossterm::cursor::SetCursorStyle;
@@ -249,43 +250,10 @@ 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");
}
Ok(result) => {
if register::handle_registration_result(result, &mut app_state, &mut register_state) {
needs_redraw = true;
}
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) => {