gRPC implementation of a registration working
This commit is contained in:
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
use crate::config::binds::config::Config;
|
use crate::config::binds::config::Config;
|
||||||
use crate::state::state::AppState;
|
|
||||||
use crate::ui::handlers::context::DialogPurpose;
|
use crate::ui::handlers::context::DialogPurpose;
|
||||||
|
use crate::state::state::AppState;
|
||||||
use crate::state::pages::auth::AuthState;
|
use crate::state::pages::auth::AuthState;
|
||||||
|
use crate::state::pages::auth::RegisterState;
|
||||||
use crate::services::auth::AuthClient;
|
use crate::services::auth::AuthClient;
|
||||||
use crate::modes::handlers::event::EventOutcome;
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
use crate::tui::functions::common::login;
|
use crate::tui::functions::common::{login, register};
|
||||||
|
|
||||||
/// Handles key events specifically when a dialog is active.
|
/// Handles key events specifically when a dialog is active.
|
||||||
/// Returns Some(Result<EventOutcome, Error>) if the event was handled (consumed),
|
/// Returns Some(Result<EventOutcome, Error>) if the event was handled (consumed),
|
||||||
@@ -17,6 +18,7 @@ pub async fn handle_dialog_event(
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
app_state: &mut AppState,
|
app_state: &mut AppState,
|
||||||
auth_state: &mut AuthState,
|
auth_state: &mut AuthState,
|
||||||
|
register_state: &mut RegisterState,
|
||||||
auth_client: &mut AuthClient,
|
auth_client: &mut AuthClient,
|
||||||
) -> Option<Result<EventOutcome, Box<dyn std::error::Error>>> {
|
) -> Option<Result<EventOutcome, Box<dyn std::error::Error>>> {
|
||||||
if let Event::Key(key) = event {
|
if let Event::Key(key) = event {
|
||||||
@@ -85,7 +87,32 @@ pub async fn handle_dialog_event(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add cases for other DialogPurpose variants here if needed
|
DialogPurpose::RegisterSuccess => { // Add this arm
|
||||||
|
match selected_index {
|
||||||
|
0 => { // "OK" button for RegisterSuccess
|
||||||
|
app_state.hide_dialog();
|
||||||
|
// Go back to intro after successful registration dialog
|
||||||
|
let message = register::back_to_main(register_state, app_state).await;
|
||||||
|
return Some(Ok(EventOutcome::Ok(message)));
|
||||||
|
}
|
||||||
|
_ => { // Default for RegisterSuccess
|
||||||
|
app_state.hide_dialog();
|
||||||
|
return Some(Ok(EventOutcome::Ok("Unknown dialog button selected".to_string())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DialogPurpose::RegisterFailed => { // Add this arm
|
||||||
|
match selected_index {
|
||||||
|
0 => { // "OK" button for RegisterFailed
|
||||||
|
app_state.hide_dialog(); // Just dismiss
|
||||||
|
return Some(Ok(EventOutcome::Ok("Register failed dialog dismissed".to_string())));
|
||||||
|
}
|
||||||
|
_ => { // Default for RegisterFailed
|
||||||
|
app_state.hide_dialog();
|
||||||
|
return Some(Ok(EventOutcome::Ok("Unknown dialog button selected".to_string())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {} // Ignore other general actions when dialog is shown
|
_ => {} // Ignore other general actions when dialog is shown
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ pub async fn handle_navigation_event(
|
|||||||
(UiContext::Intro, app_state.ui.intro_state.selected_option)
|
(UiContext::Intro, app_state.ui.intro_state.selected_option)
|
||||||
} else if app_state.ui.show_login && app_state.ui.focus_outside_canvas {
|
} else if app_state.ui.show_login && app_state.ui.focus_outside_canvas {
|
||||||
(UiContext::Login, app_state.general.selected_item)
|
(UiContext::Login, app_state.general.selected_item)
|
||||||
|
} else if app_state.ui.show_register && app_state.ui.focus_outside_canvas {
|
||||||
|
(UiContext::Register, app_state.general.selected_item)
|
||||||
} else if app_state.ui.show_admin {
|
} else if app_state.ui.show_admin {
|
||||||
(UiContext::Admin, app_state.general.selected_item)
|
(UiContext::Admin, app_state.general.selected_item)
|
||||||
} else if app_state.ui.dialog.dialog_show {
|
} else if app_state.ui.dialog.dialog_show {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::state::canvas_state::CanvasState;
|
|||||||
use crate::ui::handlers::rat_state::UiStateHandler;
|
use crate::ui::handlers::rat_state::UiStateHandler;
|
||||||
use crate::ui::handlers::context::UiContext;
|
use crate::ui::handlers::context::UiContext;
|
||||||
use crate::tui::functions::{intro, admin};
|
use crate::tui::functions::{intro, admin};
|
||||||
use crate::tui::functions::common::login;
|
use crate::tui::functions::common::{login, register};
|
||||||
use crate::modes::{
|
use crate::modes::{
|
||||||
common::command_mode,
|
common::command_mode,
|
||||||
canvas::{edit, read_only, common_mode},
|
canvas::{edit, read_only, common_mode},
|
||||||
@@ -76,7 +76,7 @@ impl EventHandler {
|
|||||||
// --- DIALOG MODALITY ---
|
// --- DIALOG MODALITY ---
|
||||||
if app_state.ui.dialog.dialog_show {
|
if app_state.ui.dialog.dialog_show {
|
||||||
if let Some(dialog_result) = dialog::handle_dialog_event(
|
if let Some(dialog_result) = dialog::handle_dialog_event(
|
||||||
&event, config, app_state, auth_state, &mut self.auth_client
|
&event, config, app_state, auth_state, register_state, &mut self.auth_client
|
||||||
).await {
|
).await {
|
||||||
return dialog_result;
|
return dialog_result;
|
||||||
}
|
}
|
||||||
@@ -122,6 +122,13 @@ impl EventHandler {
|
|||||||
_ => "Invalid Login Option".to_string(),
|
_ => "Invalid Login Option".to_string(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
UiContext::Register => {
|
||||||
|
message = match index {
|
||||||
|
0 => register::save(register_state, &mut self.auth_client, app_state).await?,
|
||||||
|
1 => register::back_to_main(register_state, app_state).await,
|
||||||
|
_ => "Invalid Login Option".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
UiContext::Admin => {
|
UiContext::Admin => {
|
||||||
// Assuming handle_admin_selection uses app_state.general.selected_item
|
// Assuming handle_admin_selection uses app_state.general.selected_item
|
||||||
admin::handle_admin_selection(app_state);
|
admin::handle_admin_selection(app_state);
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
pub mod form;
|
pub mod form;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
|
pub mod register;
|
||||||
|
|||||||
148
client/src/tui/functions/common/register.rs
Normal file
148
client/src/tui/functions/common/register.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// src/tui/functions/common/register.rs
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
services::auth::AuthClient,
|
||||||
|
state::{
|
||||||
|
pages::auth::RegisterState,
|
||||||
|
state::AppState,
|
||||||
|
canvas_state::CanvasState,
|
||||||
|
},
|
||||||
|
ui::handlers::context::DialogPurpose,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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, Box<dyn std::error::Error>> {
|
||||||
|
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())
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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).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,
|
||||||
|
_app_state: &mut AppState, // Keep signature consistent if needed elsewhere
|
||||||
|
) -> String {
|
||||||
|
register_state.username.clear();
|
||||||
|
register_state.email.clear();
|
||||||
|
register_state.password.clear();
|
||||||
|
register_state.password_confirmation.clear();
|
||||||
|
register_state.error_message = None;
|
||||||
|
register_state.set_has_unsaved_changes(false);
|
||||||
|
register_state.current_field = 0; // Reset focus to first field
|
||||||
|
register_state.current_cursor_pos = 0;
|
||||||
|
"Registration form cleared".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the form and returns to the intro screen.
|
||||||
|
pub async fn back_to_main(
|
||||||
|
register_state: &mut RegisterState,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
) -> String {
|
||||||
|
// Clear fields first
|
||||||
|
let _ = revert(register_state, app_state).await;
|
||||||
|
|
||||||
|
// Ensure dialog is hidden
|
||||||
|
app_state.hide_dialog();
|
||||||
|
|
||||||
|
// Navigation logic
|
||||||
|
app_state.ui.show_register = false;
|
||||||
|
app_state.ui.show_intro = true;
|
||||||
|
|
||||||
|
// Reset focus state
|
||||||
|
app_state.ui.focus_outside_canvas = false;
|
||||||
|
app_state.general.selected_item = 0; // Reset intro selection
|
||||||
|
|
||||||
|
"Returned to main menu".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
pub enum UiContext {
|
pub enum UiContext {
|
||||||
Intro,
|
Intro,
|
||||||
Login,
|
Login,
|
||||||
|
Register,
|
||||||
Admin,
|
Admin,
|
||||||
Dialog,
|
Dialog,
|
||||||
}
|
}
|
||||||
@@ -12,6 +13,8 @@ pub enum UiContext {
|
|||||||
pub enum DialogPurpose {
|
pub enum DialogPurpose {
|
||||||
LoginSuccess,
|
LoginSuccess,
|
||||||
LoginFailed,
|
LoginFailed,
|
||||||
|
RegisterSuccess,
|
||||||
|
RegisterFailed,
|
||||||
// TODO in the future:
|
// TODO in the future:
|
||||||
// ConfirmQuit,
|
// ConfirmQuit,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user