readonly and edit functionality to add table

This commit is contained in:
filipriec
2025-04-17 14:14:36 +02:00
parent 57f789290d
commit c5d7f56399
11 changed files with 967 additions and 254 deletions

View File

@@ -1,13 +1,17 @@
// src/modes/canvas/edit.rs
use crate::config::binds::config::Config;
use crate::services::grpc_client::GrpcClient;
use crate::state::pages::{auth::{LoginState, RegisterState}, canvas_state::CanvasState};
use crate::state::pages::{
auth::{LoginState, RegisterState},
canvas_state::CanvasState,
};
use crate::state::pages::form::FormState;
use crate::state::pages::add_table::AddTableState; // Added
use crate::modes::handlers::event::EventOutcome;
use crate::functions::modes::edit::{auth_e, form_e};
use crate::functions::modes::edit::add_table_e; // Added
use crate::state::app::state::AppState;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
// Removed duplicate/unused imports
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EditEventOutcome {
@@ -21,210 +25,319 @@ pub async fn handle_edit_event(
form_state: &mut FormState,
login_state: &mut LoginState,
register_state: &mut RegisterState,
add_table_state: &mut AddTableState, // Added
ideal_cursor_column: &mut usize,
// command_message: &mut String, // Removed as messages are returned
current_position: &mut u64,
total_count: u64,
grpc_client: &mut GrpcClient,
app_state: &AppState,
) -> Result<EditEventOutcome, Box<dyn std::error::Error>> {
// Global command mode check
// Global command mode check (should ideally be handled before calling this function)
if let Some("enter_command_mode") = config.get_action_for_key_in_mode(
&config.keybindings.global,
key.code,
key.modifiers
key.modifiers,
) {
return Ok(EditEventOutcome::Message("Switching to Command Mode...".to_string()));
// This mode change should likely be handled in event.rs
// Returning a message here might prevent the mode switch.
// Consider if this check is necessary here.
return Ok(EditEventOutcome::Message(
"Command mode entry handled globally.".to_string(),
));
}
// Common actions (save/revert)
if let Some(action) = config.get_action_for_key_in_mode(
&config.keybindings.common,
key.code,
key.modifiers
) {
key.modifiers,
).as_deref() {
if matches!(action, "save" | "revert") {
// Ensure all branches result in Result<String, Error> before the final Ok(...) wrap
let message_string: String = if app_state.ui.show_login {
auth_e::execute_common_action(
action,
login_state,
grpc_client,
current_position,
total_count
).await? // Results in String on success
total_count,
)
.await?
} else if app_state.ui.show_register {
// Keeping this block as requested
auth_e::execute_common_action(
action,
register_state,
grpc_client,
current_position,
total_count
).await? // Results in String on success
} else {
total_count,
)
.await? // Results in String on success
} else if app_state.ui.show_add_table {
// Placeholder - common actions for AddTable might be different
format!(
"Action '{}' not fully implemented for Add Table view here.",
action
)
} else { // Assuming FormState otherwise
let outcome = form_e::execute_common_action(
action,
form_state,
grpc_client,
current_position,
total_count
).await?; // This returns EventOutcome on success
// Extract the message string from the EventOutcome
total_count,
)
.await?;
match outcome {
EventOutcome::Ok(msg) => msg,
EventOutcome::DataSaved(_, msg) => msg,
_ => format!("Unexpected outcome from common action: {:?}", outcome),
_ => format!(
"Unexpected outcome from common action: {:?}",
outcome
),
}
};
// Wrap the resulting String message
return Ok(EditEventOutcome::Message(message_string));
}
}
// Edit-specific actions
if let Some(action) = config.get_edit_action_for_key(key.code, key.modifiers) {
if action == "exit" {
if let Some(action) =
config.get_edit_action_for_key(key.code, key.modifiers)
.as_deref() {
if action == "exit_edit_mode" {
// Handle exiting suggestion mode in Register view first
if app_state.ui.show_register && register_state.in_suggestion_mode {
// Call the action, get Result<String, Error>
let msg = auth_e::execute_edit_action(
"exit_suggestion_mode",
"exit_suggestion_mode", // Specific action for suggestion exit
key,
register_state,
ideal_cursor_column,
grpc_client, // Pass necessary args if needed by auth_e
current_position,
total_count,
)
.await?;
return Ok(EditEventOutcome::Message(msg));
} else {
// Signal exit from Edit mode
return Ok(EditEventOutcome::ExitEditMode);
}
}
// Special handling for role field suggestions (Register view only)
if app_state.ui.show_register && register_state.current_field() == 4 {
// Check if Tab was pressed to *enter* suggestion mode
if !register_state.in_suggestion_mode
&& key.code == KeyCode::Tab
&& key.modifiers == KeyModifiers::NONE
{
register_state.update_role_suggestions();
if !register_state.role_suggestions.is_empty() {
register_state.in_suggestion_mode = true;
register_state.selected_suggestion_index = Some(0); // Select first suggestion
return Ok(EditEventOutcome::Message(
"Suggestions shown".to_string(),
));
} else {
return Ok(EditEventOutcome::Message(
"No suggestions available".to_string(),
));
}
}
// Handle suggestion navigation/selection if already in suggestion mode
if register_state.in_suggestion_mode
&& matches!(
action,
"suggestion_down"
| "suggestion_up"
| "select_suggestion"
)
{
let msg = auth_e::execute_edit_action(
action, // Pass the specific suggestion action
key,
register_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
).await?; // Results in String on success
// Wrap the String message
)
.await?;
return Ok(EditEventOutcome::Message(msg));
} else {
// Signal exit
return Ok(EditEventOutcome::ExitEditMode);
}
}
// Special handling for role field suggestions
// Execute other edit actions based on the current view
let msg = if app_state.ui.show_login {
auth_e::execute_edit_action(
action,
key,
login_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
} else if app_state.ui.show_add_table {
add_table_e::execute_edit_action(
action,
key,
add_table_state,
ideal_cursor_column,
// Pass other necessary params if add_table_e needs them
)
.await?
} else if app_state.ui.show_register {
auth_e::execute_edit_action(
action,
key,
register_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
} else {
// Assuming FormState otherwise
form_e::execute_edit_action(
action,
key,
form_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
};
return Ok(EditEventOutcome::Message(msg));
}
// --- Character insertion ---
if let KeyCode::Char(c) = key.code {
// Exit suggestion mode in Register view if a character is typed
if app_state.ui.show_register && register_state.in_suggestion_mode {
register_state.in_suggestion_mode = false;
register_state.show_role_suggestions = false;
register_state.selected_suggestion_index = None;
}
// Execute insert_char action based on the current view
let msg = if app_state.ui.show_login {
auth_e::execute_edit_action(
"insert_char",
key, // Pass the key event containing the char
login_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
} else if app_state.ui.show_add_table {
add_table_e::execute_edit_action(
"insert_char",
key,
add_table_state,
ideal_cursor_column,
)
.await?
} else if app_state.ui.show_register {
auth_e::execute_edit_action(
"insert_char",
key,
register_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
} else {
// Assuming FormState otherwise
form_e::execute_edit_action(
"insert_char",
key,
form_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
};
// Update role suggestions after insertion if needed (Register view)
if app_state.ui.show_register && register_state.current_field() == 4 {
if !register_state.in_suggestion_mode && key.code == KeyCode::Tab && key.modifiers == KeyModifiers::NONE {
register_state.update_role_suggestions();
if !register_state.role_suggestions.is_empty() {
register_state.in_suggestion_mode = true;
register_state.selected_suggestion_index = Some(0);
return Ok(EditEventOutcome::Message("Suggestions shown".to_string()));
} else { // Added else here for clarity
return Ok(EditEventOutcome::Message("No suggestions available".to_string()));
}
}
register_state.update_role_suggestions();
}
// Execute other edit actions
let msg = if app_state.ui.show_login {
auth_e::execute_edit_action(
action,
key,
login_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
} else if app_state.ui.show_register {
auth_e::execute_edit_action(
action,
key,
register_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
} else {
form_e::execute_edit_action(
action,
key,
form_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
};
// Wrap the resulting String message
return Ok(EditEventOutcome::Message(msg));
}
// Character insertion
if let KeyCode::Char(_) = key.code {
if app_state.ui.show_register && register_state.in_suggestion_mode {
register_state.in_suggestion_mode = false;
register_state.show_role_suggestions = false;
register_state.selected_suggestion_index = None;
}
// Execute insert_char action
let msg = if app_state.ui.show_login {
auth_e::execute_edit_action(
"insert_char",
key,
login_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
} else if app_state.ui.show_register {
auth_e::execute_edit_action(
"insert_char",
key,
register_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
} else {
form_e::execute_edit_action(
"insert_char",
key,
form_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
};
// Wrap the resulting String message
return Ok(EditEventOutcome::Message(msg));
}
// Handle Backspace/Delete
// --- Handle Backspace/Delete ---
if matches!(key.code, KeyCode::Backspace | KeyCode::Delete) {
// Exit suggestion mode in Register view
if app_state.ui.show_register && register_state.in_suggestion_mode {
register_state.in_suggestion_mode = false;
register_state.show_role_suggestions = false;
register_state.selected_suggestion_index = None;
}
let action_str = if key.code == KeyCode::Backspace { "backspace" } else { "delete_char" };
// Ensure both branches result in a String *before* wrapping
let result_msg: String = if app_state.ui.show_register {
auth_e::execute_edit_action(
let action_str = if key.code == KeyCode::Backspace {
"delete_char_backward"
} else {
"delete_char_forward"
};
// Execute delete action based on the current view
let result_msg: String = if app_state.ui.show_login {
auth_e::execute_edit_action(
action_str,
key,
login_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count,
)
.await?
} else if app_state.ui.show_add_table {
add_table_e::execute_edit_action(
action_str,
key,
add_table_state,
ideal_cursor_column,
)
.await?
} else if app_state.ui.show_register {
auth_e::execute_edit_action(
action_str,
key,
register_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await? // Results in String
total_count,
)
.await?
} else {
// Return String directly, not Ok(String)
"Action not applicable here".to_string()
}; // Semicolon here ends the if/else expression
// Assuming FormState otherwise
form_e::execute_edit_action(
action_str,
key,
form_state,
ideal_cursor_column,
grpc_client,
current_position,
total_count
).await?
};
// Update role suggestions after deletion if needed (Register view)
if app_state.ui.show_register && register_state.current_field() == 4 {
register_state.update_role_suggestions();
}
// Wrap the resulting String message
return Ok(EditEventOutcome::Message(result_msg));
}

View File

@@ -6,8 +6,9 @@ use crate::services::grpc_client::GrpcClient;
use crate::state::pages::{canvas_state::CanvasState, auth::RegisterState};
use crate::state::pages::auth::LoginState;
use crate::state::pages::form::FormState;
use crate::state::pages::add_table::AddTableState;
use crate::state::app::state::AppState;
use crate::functions::modes::read_only::{auth_ro, form_ro};
use crate::functions::modes::read_only::{auth_ro, form_ro, add_table_ro};
use crossterm::event::KeyEvent;
pub async fn handle_read_only_event(
@@ -17,6 +18,7 @@ pub async fn handle_read_only_event(
form_state: &mut FormState,
login_state: &mut LoginState,
register_state: &mut RegisterState,
add_table_state: &mut AddTableState,
key_sequence_tracker: &mut KeySequenceTracker,
current_position: &mut u64,
total_count: u64,
@@ -32,34 +34,17 @@ pub async fn handle_read_only_event(
}
if config.is_enter_edit_mode_after(key.code, key.modifiers) {
let (current_input, current_pos) = if app_state.ui.show_login { // Check Login first
(
login_state.get_current_input(),
login_state.current_cursor_pos(),
)
} else if app_state.ui.show_register { // Then check Register
(
register_state.get_current_input(),
register_state.current_cursor_pos(),
)
} else {
(
form_state.get_current_input(),
form_state.current_cursor_pos(),
) // Default to Form
};
// Determine target state to adjust cursor
let target_state: &mut dyn CanvasState = if app_state.ui.show_login { login_state }
else if app_state.ui.show_register { register_state }
else if app_state.ui.show_add_table { add_table_state }
else { form_state };
let current_input = target_state.get_current_input();
let current_pos = target_state.current_cursor_pos();
if !current_input.is_empty() && current_pos < current_input.len() {
if app_state.ui.show_login {
login_state.set_current_cursor_pos(current_pos + 1);
*ideal_cursor_column = login_state.current_cursor_pos();
} else if app_state.ui.show_register {
register_state.set_current_cursor_pos(current_pos + 1);
*ideal_cursor_column = register_state.current_cursor_pos();
} else { // Default to Form
form_state.set_current_cursor_pos(current_pos + 1);
*ideal_cursor_column = form_state.current_cursor_pos();
}
target_state.set_current_cursor_pos(current_pos + 1);
*ideal_cursor_column = target_state.current_cursor_pos();
}
*edit_mode_cooldown = true;
*command_message = "Entering Edit mode (after cursor)".to_string();
@@ -83,7 +68,7 @@ pub async fn handle_read_only_event(
key_sequence_tracker.add_key(key.code);
let sequence = key_sequence_tracker.get_sequence();
if let Some(action) = config.matches_key_sequence_generalized(&sequence) {
if let Some(action) = config.matches_key_sequence_generalized(&sequence).as_deref() {
let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) {
crate::tui::functions::form::handle_action(
action,
@@ -96,6 +81,15 @@ pub async fn handle_read_only_event(
.await?
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { // Handle login context actions
crate::tui::functions::login::handle_action(action).await?
} else if app_state.ui.show_add_table {
add_table_ro::execute_action(
action,
app_state,
add_table_state,
ideal_cursor_column,
key_sequence_tracker,
command_message,
).await?
} else if app_state.ui.show_register{
auth_ro::execute_action(
action,
@@ -134,7 +128,7 @@ pub async fn handle_read_only_event(
}
if sequence.len() == 1 && !config.is_key_sequence_prefix(&sequence) {
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) {
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers).as_deref() {
let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) {
crate::tui::functions::form::handle_action(
action,
@@ -147,6 +141,15 @@ pub async fn handle_read_only_event(
.await?
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { // Handle login context actions
crate::tui::functions::login::handle_action(action).await?
} else if app_state.ui.show_add_table {
add_table_ro::execute_action(
action,
app_state,
add_table_state,
ideal_cursor_column,
key_sequence_tracker,
command_message,
).await?
} else if app_state.ui.show_register /* && CONTEXT_ACTIONS_REGISTER.contains(&action) */ { // Handle register general actions
auth_ro::execute_action(
action,
@@ -184,7 +187,7 @@ pub async fn handle_read_only_event(
} else {
key_sequence_tracker.reset();
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) {
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers).as_deref() {
let result = if app_state.ui.show_form && CONTEXT_ACTIONS_FORM.contains(&action) {
crate::tui::functions::form::handle_action(
action,
@@ -195,8 +198,17 @@ pub async fn handle_read_only_event(
ideal_cursor_column,
)
.await?
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { // Handle login context actions
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
crate::tui::functions::login::handle_action(action).await?
} else if app_state.ui.show_add_table {
add_table_ro::execute_action(
action,
app_state,
add_table_state,
ideal_cursor_column,
key_sequence_tracker,
command_message,
).await?
} else if app_state.ui.show_register /* && CONTEXT_ACTIONS_REGISTER.contains(&action) */ { // Handle register general actions
auth_ro::execute_action(
action,