150 lines
5.3 KiB
Rust
150 lines
5.3 KiB
Rust
// src/tui/functions/common/login.rs
|
|
|
|
use crate::services::auth::AuthClient;
|
|
use crate::state::pages::auth::AuthState;
|
|
use crate::state::pages::auth::LoginState;
|
|
use crate::state::app::state::AppState;
|
|
use crate::state::app::buffer::{AppView, BufferState};
|
|
use crate::state::pages::canvas_state::CanvasState;
|
|
use crate::ui::handlers::context::DialogPurpose;
|
|
use crate::modes::handlers::event::EventOutcome; // Make sure this is imported
|
|
|
|
/// Attempts to log the user in using the provided credentials via gRPC.
|
|
/// Updates AuthState and AppState on success or failure.
|
|
/// (This is your existing function - remains unchanged)
|
|
pub async fn save(
|
|
auth_state: &mut AuthState,
|
|
login_state: &mut LoginState,
|
|
auth_client: &mut AuthClient,
|
|
app_state: &mut AppState,
|
|
) -> Result<String, Box<dyn std::error::Error>> {
|
|
let identifier = login_state.username.clone();
|
|
let password = login_state.password.clone();
|
|
|
|
// --- Client-side validation ---
|
|
// Prevent login attempt if the identifier field is empty or whitespace.
|
|
if identifier.trim().is_empty() {
|
|
let error_message = "Username/Email cannot be empty.".to_string();
|
|
app_state.show_dialog(
|
|
"Login Failed",
|
|
&error_message,
|
|
vec!["OK".to_string()],
|
|
DialogPurpose::LoginFailed,
|
|
);
|
|
login_state.error_message = Some(error_message.clone());
|
|
return Ok(error_message);
|
|
}
|
|
|
|
// Clear previous error/dialog state before attempting
|
|
login_state.error_message = None;
|
|
app_state.hide_dialog(); // Hide any previous dialog
|
|
|
|
// Call the gRPC login method
|
|
match auth_client.login(identifier, password).await {
|
|
Ok(response) => {
|
|
// Store authentication details using correct field names
|
|
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());
|
|
login_state.set_has_unsaved_changes(false);
|
|
login_state.error_message = None;
|
|
|
|
// Format the success message using response data
|
|
let success_message = format!(
|
|
"Login Successful!\n\n\
|
|
Username: {}\n\
|
|
User ID: {}\n\
|
|
Role: {}",
|
|
response.username,
|
|
response.user_id,
|
|
response.role
|
|
);
|
|
|
|
app_state.show_dialog(
|
|
"Login Success",
|
|
&success_message,
|
|
vec!["Menu".to_string(), "Exit".to_string()],
|
|
DialogPurpose::LoginSuccess,
|
|
);
|
|
login_state.password.clear();
|
|
login_state.username.clear();
|
|
login_state.current_cursor_pos = 0;
|
|
Ok("Login successful, details shown in dialog.".to_string())
|
|
}
|
|
Err(e) => {
|
|
let error_message = format!("{}", e);
|
|
app_state.show_dialog(
|
|
"Login Failed",
|
|
&error_message,
|
|
vec!["OK".to_string()],
|
|
DialogPurpose::LoginFailed,
|
|
);
|
|
login_state.error_message = Some(error_message.clone());
|
|
login_state.set_has_unsaved_changes(true);
|
|
login_state.username.clear();
|
|
login_state.password.clear();
|
|
Ok(format!("Login failed: {}", error_message))
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Add this new function ---
|
|
/// Sets the stage for login: shows loading dialog and sets the pending flag.
|
|
/// Call this from the event handler when login is triggered.
|
|
pub async fn initiate_login(
|
|
app_state: &mut AppState,
|
|
login_state: &mut LoginState,
|
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
|
// Show the loading dialog immediately
|
|
app_state.show_loading_dialog("Logging In", "Please wait...");
|
|
// Set the flag in LoginState to indicate the actual save should run next loop
|
|
login_state.login_request_pending = true;
|
|
// Return immediately to allow redraw
|
|
Ok(EventOutcome::Ok("Login initiated.".to_string()))
|
|
}
|
|
// --- End of new function ---
|
|
|
|
|
|
/// Reverts the login form fields to empty and returns to the previous screen (Intro).
|
|
pub async fn revert(
|
|
login_state: &mut LoginState,
|
|
_app_state: &mut AppState, // Keep signature consistent if needed elsewhere
|
|
) -> String {
|
|
// Clear the input fields
|
|
login_state.username.clear();
|
|
login_state.password.clear();
|
|
login_state.error_message = None;
|
|
login_state.set_has_unsaved_changes(false);
|
|
login_state.login_request_pending = false; // Ensure flag is reset on revert
|
|
|
|
"Login reverted".to_string()
|
|
}
|
|
|
|
pub async fn back_to_main(
|
|
login_state: &mut LoginState,
|
|
app_state: &mut AppState,
|
|
buffer_state: &mut BufferState,
|
|
) -> String {
|
|
// Clear the input fields
|
|
login_state.username.clear();
|
|
login_state.password.clear();
|
|
login_state.error_message = None;
|
|
login_state.set_has_unsaved_changes(false);
|
|
login_state.login_request_pending = false; // Ensure flag is reset
|
|
|
|
// Ensure dialog is hidden if revert is called
|
|
app_state.hide_dialog();
|
|
|
|
// Navigation logic
|
|
buffer_state.close_active_buffer();
|
|
buffer_state.update_history(AppView::Intro);
|
|
|
|
// Reset focus state
|
|
app_state.ui.focus_outside_canvas = false;
|
|
app_state.focused_button_index= 0;
|
|
|
|
"Returned to main menu".to_string()
|
|
}
|
|
|