time for changing it all
This commit is contained in:
@@ -8,6 +8,7 @@ use crate::ui::handlers::rat_state::UiStateHandler;
|
|||||||
use crate::ui::handlers::context::UiContext;
|
use crate::ui::handlers::context::UiContext;
|
||||||
use crate::ui::handlers::context::DialogPurpose;
|
use crate::ui::handlers::context::DialogPurpose;
|
||||||
use crate::functions::common::buffer;
|
use crate::functions::common::buffer;
|
||||||
|
use std::error::Error;
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
terminal::core::TerminalCore,
|
terminal::core::TerminalCore,
|
||||||
functions::{
|
functions::{
|
||||||
@@ -41,6 +42,9 @@ use crate::modes::{
|
|||||||
};
|
};
|
||||||
use crate::functions::modes::navigation::{admin_nav, add_table_nav};
|
use crate::functions::modes::navigation::{admin_nav, add_table_nav};
|
||||||
use crate::config::binds::key_sequences::KeySequenceTracker;
|
use crate::config::binds::key_sequences::KeySequenceTracker;
|
||||||
|
use tokio::spawn;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use crate::tui::functions::common::login::LoginResult;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum EventOutcome {
|
pub enum EventOutcome {
|
||||||
@@ -60,10 +64,11 @@ pub struct EventHandler {
|
|||||||
pub ideal_cursor_column: usize,
|
pub ideal_cursor_column: usize,
|
||||||
pub key_sequence_tracker: KeySequenceTracker,
|
pub key_sequence_tracker: KeySequenceTracker,
|
||||||
pub auth_client: AuthClient,
|
pub auth_client: AuthClient,
|
||||||
|
pub login_result_sender: mpsc::Sender<LoginResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventHandler {
|
impl EventHandler {
|
||||||
pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
pub async fn new(login_result_sender: mpsc::Sender<LoginResult>) -> Result<Self, Box<dyn Error + Send + Sync>> {
|
||||||
Ok(EventHandler {
|
Ok(EventHandler {
|
||||||
command_mode: false,
|
command_mode: false,
|
||||||
command_input: String::new(),
|
command_input: String::new(),
|
||||||
@@ -74,6 +79,7 @@ impl EventHandler {
|
|||||||
ideal_cursor_column: 0,
|
ideal_cursor_column: 0,
|
||||||
key_sequence_tracker: KeySequenceTracker::new(800),
|
key_sequence_tracker: KeySequenceTracker::new(800),
|
||||||
auth_client: AuthClient::new().await?,
|
auth_client: AuthClient::new().await?,
|
||||||
|
login_result_sender,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,14 +228,47 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
UiContext::Login => {
|
UiContext::Login => {
|
||||||
let login_action_message = match index {
|
let login_action_message = match index {
|
||||||
0 => { // Index 0 corresponds to the "Login" button
|
0 => { // "Login" button pressed
|
||||||
match login::initiate_login(app_state, login_state).await {
|
let username = login_state.username.clone();
|
||||||
Ok(outcome) => return Ok(outcome),
|
let password = login_state.password.clone();
|
||||||
Err(e) => {
|
|
||||||
app_state.show_dialog("Error", &format!("Failed to initiate login: {}", e), vec!["OK".to_string()], DialogPurpose::LoginFailed);
|
// 1. Client-side validation
|
||||||
login_state.login_request_pending = false;
|
if username.trim().is_empty() {
|
||||||
"Error initiating login".to_string()
|
app_state.show_dialog(
|
||||||
}
|
"Login Failed",
|
||||||
|
"Username/Email cannot be empty.",
|
||||||
|
vec!["OK".to_string()],
|
||||||
|
DialogPurpose::LoginFailed,
|
||||||
|
);
|
||||||
|
// Return a message, no need to modify login_state here
|
||||||
|
// as it will be cleared when result is processed
|
||||||
|
"Username cannot be empty.".to_string()
|
||||||
|
} else {
|
||||||
|
// 2. Show Loading Dialog
|
||||||
|
app_state.show_loading_dialog("Logging In", "Please wait...");
|
||||||
|
|
||||||
|
// 3. Clone sender for the task (needs sender from ui.rs)
|
||||||
|
// NOTE: We need access to login_result_sender here.
|
||||||
|
// This requires passing it into EventHandler or handle_event.
|
||||||
|
// Let's assume it's added to EventHandler state for now.
|
||||||
|
let sender = self.login_result_sender.clone(); // Assumes sender is part of EventHandler state
|
||||||
|
|
||||||
|
// 4. Spawn the login task
|
||||||
|
spawn(async move {
|
||||||
|
let login_outcome = match AuthClient::new().await {
|
||||||
|
Ok(mut auth_client) => {
|
||||||
|
match auth_client.login(username, password).await {
|
||||||
|
Ok(response) => login::LoginResult::Success(response),
|
||||||
|
Err(e) => login::LoginResult::Failure(format!("{}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => login::LoginResult::ConnectionError(format!("Failed to create AuthClient: {}", e)),
|
||||||
|
};
|
||||||
|
let _ = sender.send(login_outcome).await; // Handle error?
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. Return immediately
|
||||||
|
"Login initiated.".to_string()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
1 => login::back_to_main(login_state, app_state, buffer_state).await,
|
1 => login::back_to_main(login_state, app_state, buffer_state).await,
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ use common::proto::multieko2::auth::{
|
|||||||
LoginRequest, LoginResponse,
|
LoginRequest, LoginResponse,
|
||||||
RegisterRequest, AuthResponse,
|
RegisterRequest, AuthResponse,
|
||||||
};
|
};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
pub struct AuthClient {
|
pub struct AuthClient {
|
||||||
client: AuthServiceClient<Channel>,
|
client: AuthServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthClient {
|
impl AuthClient {
|
||||||
pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
pub async fn new() -> Result<Self, Box<dyn Error + Send + Sync>> {
|
||||||
let client = AuthServiceClient::connect("http://[::1]:50051").await?;
|
let client = AuthServiceClient::connect("http://[::1]:50051").await?;
|
||||||
Ok(Self { client })
|
Ok(Self { client })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,15 @@ use crate::state::app::state::AppState;
|
|||||||
use crate::state::app::buffer::{AppView, BufferState};
|
use crate::state::app::buffer::{AppView, BufferState};
|
||||||
use crate::state::pages::canvas_state::CanvasState;
|
use crate::state::pages::canvas_state::CanvasState;
|
||||||
use crate::ui::handlers::context::DialogPurpose;
|
use crate::ui::handlers::context::DialogPurpose;
|
||||||
use crate::modes::handlers::event::EventOutcome; // Make sure this is imported
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
|
use common::proto::multieko2::auth::LoginResponse;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LoginResult {
|
||||||
|
Success(LoginResponse),
|
||||||
|
Failure(String),
|
||||||
|
ConnectionError(String),
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to log the user in using the provided credentials via gRPC.
|
/// Attempts to log the user in using the provided credentials via gRPC.
|
||||||
/// Updates AuthState and AppState on success or failure.
|
/// Updates AuthState and AppState on success or failure.
|
||||||
|
|||||||
@@ -22,20 +22,26 @@ use crate::ui::handlers::context::DialogPurpose; // <-- Add DialogPurpose import
|
|||||||
// Import SaveOutcome
|
// Import SaveOutcome
|
||||||
use crate::tui::terminal::{EventReader, TerminalCore};
|
use crate::tui::terminal::{EventReader, TerminalCore};
|
||||||
use crate::ui::handlers::render::render_ui;
|
use crate::ui::handlers::render::render_ui;
|
||||||
use crate::tui::functions::common::login; // <-- Add login module import
|
use crate::tui::functions::common::login;
|
||||||
|
use crate::tui::functions::common::login::LoginResult;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use std::error::Error;
|
||||||
use crossterm::cursor::SetCursorStyle;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
use crossterm::event as crossterm_event;
|
use crossterm::event as crossterm_event;
|
||||||
use tracing::{info, error};
|
use tracing::{info, error};
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn run_ui() -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
let config = Config::load()?;
|
let config = Config::load()?;
|
||||||
let theme = Theme::from_str(&config.colors.theme);
|
let theme = Theme::from_str(&config.colors.theme);
|
||||||
let mut terminal = TerminalCore::new()?;
|
let mut terminal = TerminalCore::new()?;
|
||||||
let mut grpc_client = GrpcClient::new().await?;
|
let mut grpc_client = GrpcClient::new().await?;
|
||||||
let mut command_handler = CommandHandler::new();
|
let mut command_handler = CommandHandler::new();
|
||||||
|
|
||||||
let mut event_handler = EventHandler::new().await?;
|
// --- Channel for Login Results ---
|
||||||
|
let (login_result_sender, mut login_result_receiver) =
|
||||||
|
mpsc::channel::<LoginResult>(1);
|
||||||
|
let mut event_handler = EventHandler::new(login_result_sender.clone()).await?;
|
||||||
let event_reader = EventReader::new();
|
let event_reader = EventReader::new();
|
||||||
|
|
||||||
let mut auth_state = AuthState::default();
|
let mut auth_state = AuthState::default();
|
||||||
@@ -136,47 +142,6 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
// --- End Cursor Visibility Logic ---
|
// --- End Cursor Visibility Logic ---
|
||||||
|
|
||||||
// --- 2. Check for Pending Login Action ---
|
|
||||||
if login_state.login_request_pending {
|
|
||||||
// Reset the flag *before* calling save
|
|
||||||
login_state.login_request_pending = false;
|
|
||||||
|
|
||||||
// Create AuthClient and call save
|
|
||||||
match AuthClient::new().await {
|
|
||||||
Ok(mut auth_client_instance) => {
|
|
||||||
// Call the ORIGINAL save function from the login module
|
|
||||||
let save_result = login::save(
|
|
||||||
&mut auth_state,
|
|
||||||
&mut login_state,
|
|
||||||
&mut auth_client_instance, // Pass the new client instance
|
|
||||||
&mut app_state,
|
|
||||||
).await;
|
|
||||||
|
|
||||||
// Use tracing for logging the outcome
|
|
||||||
match save_result {
|
|
||||||
// save returns Result<String, Error>, Ok contains the message
|
|
||||||
Ok(msg) => info!(message = %msg, "Login save result"), // Use tracing::info!
|
|
||||||
Err(e) => error!(error = %e, "Error during login save"), // Use tracing::error!
|
|
||||||
}
|
|
||||||
// Note: save already handles showing the final dialog (success/failure)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
// Handle client connection error - show dialog directly
|
|
||||||
// Ensure flag is already false here
|
|
||||||
app_state.show_dialog( // Use show_dialog, not update_dialog_content
|
|
||||||
"Login Failed",
|
|
||||||
&format!("Connection Error: {}", e),
|
|
||||||
vec!["OK".to_string()],
|
|
||||||
DialogPurpose::LoginFailed, // Use appropriate purpose
|
|
||||||
);
|
|
||||||
login_state.error_message = Some(format!("Connection Error: {}", e));
|
|
||||||
error!(error = %e, "Failed to create AuthClient"); // Use tracing::error!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// After save runs, the state (dialog content, etc.) is updated.
|
|
||||||
// The *next* iteration's draw call will show the final result.
|
|
||||||
} // --- End Pending Login Check ---
|
|
||||||
|
|
||||||
let total_count = app_state.total_count;
|
let total_count = app_state.total_count;
|
||||||
let mut current_position = app_state.current_position;
|
let mut current_position = app_state.current_position;
|
||||||
let position_before_event = current_position;
|
let position_before_event = current_position;
|
||||||
@@ -211,6 +176,63 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// This happens *after* the event is handled
|
// This happens *after* the event is handled
|
||||||
app_state.current_position = current_position;
|
app_state.current_position = current_position;
|
||||||
|
|
||||||
|
// --- Check for Login Results from Channel ---
|
||||||
|
match login_result_receiver.try_recv() {
|
||||||
|
Ok(login_result) => {
|
||||||
|
// A result arrived from the login task!
|
||||||
|
match login_result {
|
||||||
|
LoginResult::Success(response) => {
|
||||||
|
// Update AuthState
|
||||||
|
auth_state.auth_token = Some(response.access_token.clone());
|
||||||
|
auth_state.user_id = Some(response.user_id.clone());
|
||||||
|
auth_state.role = Some(response.role.clone());
|
||||||
|
auth_state.decoded_username = Some(response.username.clone());
|
||||||
|
|
||||||
|
// Update Dialog
|
||||||
|
let success_message = format!(
|
||||||
|
"Login Successful!\n\nUsername: {}\nUser ID: {}\nRole: {}",
|
||||||
|
response.username, response.user_id, response.role
|
||||||
|
);
|
||||||
|
// Use update_dialog_content if loading dialog is shown, otherwise show_dialog
|
||||||
|
app_state.update_dialog_content( // Assuming loading dialog was shown
|
||||||
|
&success_message,
|
||||||
|
vec!["Menu".to_string(), "Exit".to_string()],
|
||||||
|
DialogPurpose::LoginSuccess,
|
||||||
|
);
|
||||||
|
info!(message = %success_message, "Login successful");
|
||||||
|
}
|
||||||
|
LoginResult::Failure(err_msg) => {
|
||||||
|
app_state.update_dialog_content( // Update loading dialog
|
||||||
|
&err_msg,
|
||||||
|
vec!["OK".to_string()],
|
||||||
|
DialogPurpose::LoginFailed,
|
||||||
|
);
|
||||||
|
login_state.error_message = Some(err_msg.clone()); // Keep error message
|
||||||
|
error!(error = %err_msg, "Login failed");
|
||||||
|
}
|
||||||
|
LoginResult::ConnectionError(err_msg) => {
|
||||||
|
app_state.update_dialog_content( // Update loading dialog
|
||||||
|
&err_msg, // Show connection error
|
||||||
|
vec!["OK".to_string()],
|
||||||
|
DialogPurpose::LoginFailed,
|
||||||
|
);
|
||||||
|
login_state.error_message = Some(err_msg.clone());
|
||||||
|
error!(error = %err_msg, "Login connection error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear login form state regardless of outcome now that it's processed
|
||||||
|
login_state.username.clear();
|
||||||
|
login_state.password.clear();
|
||||||
|
login_state.set_has_unsaved_changes(false);
|
||||||
|
login_state.current_cursor_pos = 0;
|
||||||
|
}
|
||||||
|
Err(mpsc::error::TryRecvError::Empty) => { /* No message waiting */ }
|
||||||
|
Err(mpsc::error::TryRecvError::Disconnected) => {
|
||||||
|
error!("Login result channel disconnected unexpectedly.");
|
||||||
|
// Optionally show an error dialog here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Centralized Consequence Handling ---
|
// --- Centralized Consequence Handling ---
|
||||||
let mut should_exit = false;
|
let mut should_exit = false;
|
||||||
match event_outcome_result {
|
match event_outcome_result {
|
||||||
|
|||||||
Reference in New Issue
Block a user