BREAKING CHANGES updating gRPC based on the enum now
This commit is contained in:
@@ -4,6 +4,8 @@ use crate::services::grpc_client::GrpcClient;
|
|||||||
use crate::state::canvas_state::CanvasState;
|
use crate::state::canvas_state::CanvasState;
|
||||||
use crate::state::pages::form::FormState;
|
use crate::state::pages::form::FormState;
|
||||||
use crate::tui::functions::common::form::{revert, save};
|
use crate::tui::functions::common::form::{revert, save};
|
||||||
|
use crate::tui::functions::common::form::SaveOutcome;
|
||||||
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
@@ -11,48 +13,61 @@ pub async fn execute_common_action<S: CanvasState + Any>(
|
|||||||
action: &str,
|
action: &str,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
grpc_client: &mut GrpcClient,
|
grpc_client: &mut GrpcClient,
|
||||||
is_saved: &mut bool,
|
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
||||||
match action {
|
match action {
|
||||||
"save" | "revert" => {
|
"save" | "revert" => {
|
||||||
if !state.has_unsaved_changes() {
|
if !state.has_unsaved_changes() {
|
||||||
return Ok("No changes to save or revert.".to_string());
|
return Ok(EventOutcome::Ok("No changes to save or revert.".to_string()));
|
||||||
}
|
}
|
||||||
if let Some(form_state) =
|
if let Some(form_state) =
|
||||||
(state as &mut dyn Any).downcast_mut::<FormState>()
|
(state as &mut dyn Any).downcast_mut::<FormState>()
|
||||||
{
|
{
|
||||||
match action {
|
match action {
|
||||||
"save" => {
|
"save" => {
|
||||||
save(
|
let save_result = save(
|
||||||
form_state,
|
form_state,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
is_saved,
|
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
)
|
).await;
|
||||||
.await
|
|
||||||
|
match save_result {
|
||||||
|
Ok(save_outcome) => {
|
||||||
|
let message = match save_outcome {
|
||||||
|
SaveOutcome::NoChange => "No changes to save.".to_string(),
|
||||||
|
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
||||||
|
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
||||||
|
};
|
||||||
|
Ok(EventOutcome::DataSaved(save_outcome, message))
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"revert" => {
|
"revert" => {
|
||||||
revert(
|
let revert_result = revert(
|
||||||
form_state,
|
form_state,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
)
|
).await;
|
||||||
.await
|
|
||||||
|
match revert_result {
|
||||||
|
Ok(message) => Ok(EventOutcome::Ok(message)),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(format!(
|
Ok(EventOutcome::Ok(format!(
|
||||||
"Action '{}' not implemented for this state type.",
|
"Action '{}' not implemented for this state type.",
|
||||||
action
|
action
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Ok(format!("Common action '{}' not handled here.", action)),
|
_ => Ok(EventOutcome::Ok(format!("Common action '{}' not handled here.", action))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,10 +76,10 @@ pub async fn execute_edit_action<S: CanvasState>(
|
|||||||
key: KeyEvent,
|
key: KeyEvent,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
ideal_cursor_column: &mut usize,
|
ideal_cursor_column: &mut usize,
|
||||||
_grpc_client: &mut GrpcClient,
|
grpc_client: &mut GrpcClient,
|
||||||
_is_saved: &mut bool,
|
is_saved: &mut bool,
|
||||||
_current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
_total_count: u64,
|
total_count: u64,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
match action {
|
match action {
|
||||||
"insert_char" => {
|
"insert_char" => {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
// src/modes/canvas/common.rs
|
// src/modes/canvas/common_mode.rs
|
||||||
|
|
||||||
use crate::tui::terminal::core::TerminalCore;
|
use crate::tui::terminal::core::TerminalCore;
|
||||||
use crate::state::pages::{form::FormState, auth::AuthState};
|
use crate::state::pages::{form::FormState, auth::AuthState};
|
||||||
use crate::state::state::AppState;
|
use crate::state::state::AppState;
|
||||||
use crate::services::grpc_client::GrpcClient;
|
use crate::services::grpc_client::GrpcClient;
|
||||||
use crate::services::auth::AuthClient;
|
use crate::services::auth::AuthClient;
|
||||||
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
|
use crate::tui::functions::common::form::SaveOutcome;
|
||||||
use crate::tui::functions::common::{
|
use crate::tui::functions::common::{
|
||||||
form::{save as form_save, revert as form_revert},
|
form::{save as form_save, revert as form_revert},
|
||||||
login::{save as login_save, revert as login_revert}
|
login::{save as login_save, revert as login_revert}
|
||||||
@@ -20,46 +22,54 @@ pub async fn handle_core_action(
|
|||||||
app_state: &mut AppState,
|
app_state: &mut AppState,
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
) -> Result<(bool, String), Box<dyn std::error::Error>> {
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
||||||
match action {
|
match action {
|
||||||
"save" => {
|
"save" => {
|
||||||
if app_state.ui.show_login {
|
if app_state.ui.show_login {
|
||||||
let message = login_save(auth_state, auth_client, app_state).await?;
|
let message = login_save(auth_state, auth_client, app_state).await?;
|
||||||
Ok((false, message))
|
Ok(EventOutcome::Ok(message))
|
||||||
} else {
|
} else {
|
||||||
let message = form_save(
|
let save_outcome = form_save(
|
||||||
form_state,
|
form_state,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
&mut app_state.ui.is_saved,
|
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
).await?;
|
).await?;
|
||||||
Ok((false, message))
|
let message = match save_outcome {
|
||||||
|
SaveOutcome::NoChange => "No changes to save.".to_string(),
|
||||||
|
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
||||||
|
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
||||||
|
};
|
||||||
|
Ok(EventOutcome::DataSaved(save_outcome, message))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"force_quit" => {
|
"force_quit" => {
|
||||||
terminal.cleanup()?;
|
terminal.cleanup()?;
|
||||||
Ok((true, "Force exiting without saving.".to_string()))
|
Ok(EventOutcome::Exit("Force exiting without saving.".to_string()))
|
||||||
},
|
},
|
||||||
"save_and_quit" => {
|
"save_and_quit" => {
|
||||||
let message = if app_state.ui.show_login {
|
let message = if app_state.ui.show_login {
|
||||||
login_save(auth_state, auth_client, app_state).await?
|
login_save(auth_state, auth_client, app_state).await?
|
||||||
} else {
|
} else {
|
||||||
form_save(
|
let save_outcome = form_save(
|
||||||
form_state,
|
form_state,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
&mut app_state.ui.is_saved,
|
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
).await?
|
).await?;
|
||||||
|
match save_outcome {
|
||||||
|
SaveOutcome::NoChange => "No changes to save.".to_string(),
|
||||||
|
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
||||||
|
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
terminal.cleanup()?;
|
terminal.cleanup()?;
|
||||||
Ok((true, format!("{}. Exiting application.", message)))
|
Ok(EventOutcome::Exit(format!("{}. Exiting application.", message)))
|
||||||
},
|
},
|
||||||
"revert" => {
|
"revert" => {
|
||||||
if app_state.ui.show_login {
|
if app_state.ui.show_login {
|
||||||
let message = login_revert(auth_state, app_state).await;
|
let message = login_revert(auth_state, app_state).await;
|
||||||
Ok((false, message))
|
Ok(EventOutcome::Ok(message))
|
||||||
} else {
|
} else {
|
||||||
let message = form_revert(
|
let message = form_revert(
|
||||||
form_state,
|
form_state,
|
||||||
@@ -67,9 +77,9 @@ pub async fn handle_core_action(
|
|||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
).await?;
|
).await?;
|
||||||
Ok((false, message))
|
Ok(EventOutcome::Ok(message))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => Ok((false, format!("Core action not handled: {}", action))),
|
_ => Ok(EventOutcome::Ok(format!("Core action not handled: {}", action))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/modes/handlers/command_mode.rs
|
// src/modes/common/command_mode.rs
|
||||||
|
|
||||||
use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
|
use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
|
||||||
use crate::config::binds::config::Config;
|
use crate::config::binds::config::Config;
|
||||||
@@ -7,6 +7,14 @@ use crate::state::pages::form::FormState;
|
|||||||
use crate::modes::common::commands::CommandHandler;
|
use crate::modes::common::commands::CommandHandler;
|
||||||
use crate::tui::terminal::core::TerminalCore;
|
use crate::tui::terminal::core::TerminalCore;
|
||||||
use crate::tui::functions::common::form::{save, revert};
|
use crate::tui::functions::common::form::{save, revert};
|
||||||
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
|
use crate::tui::functions::common::form::SaveOutcome;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum CommandOutcome {
|
||||||
|
ExitCommandMode(String),
|
||||||
|
// Other command-specific outcomes if needed
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_command_event(
|
pub async fn handle_command_event(
|
||||||
key: KeyEvent,
|
key: KeyEvent,
|
||||||
@@ -19,15 +27,12 @@ pub async fn handle_command_event(
|
|||||||
terminal: &mut TerminalCore,
|
terminal: &mut TerminalCore,
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
) -> Result<(bool, String, bool), Box<dyn std::error::Error>> {
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
// Return value: (should_exit, message, should_exit_command_mode)
|
|
||||||
|
|
||||||
// Exit command mode (via configurable keybinding)
|
// Exit command mode (via configurable keybinding)
|
||||||
if config.is_exit_command_mode(key.code, key.modifiers) {
|
if config.is_exit_command_mode(key.code, key.modifiers) {
|
||||||
command_input.clear();
|
command_input.clear();
|
||||||
*command_message = "".to_string();
|
*command_message = "".to_string();
|
||||||
return Ok((false, "".to_string(), true));
|
return Ok(EventOutcome::Ok("Exited command mode".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute command (via configurable keybinding, defaults to Enter)
|
// Execute command (via configurable keybinding, defaults to Enter)
|
||||||
@@ -48,7 +53,7 @@ pub async fn handle_command_event(
|
|||||||
// Backspace (via configurable keybinding, defaults to Backspace)
|
// Backspace (via configurable keybinding, defaults to Backspace)
|
||||||
if config.is_command_backspace(key.code, key.modifiers) {
|
if config.is_command_backspace(key.code, key.modifiers) {
|
||||||
command_input.pop();
|
command_input.pop();
|
||||||
return Ok((false, "".to_string(), false));
|
return Ok(EventOutcome::Ok("".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular character input - accept any character in command mode
|
// Regular character input - accept any character in command mode
|
||||||
@@ -56,12 +61,12 @@ pub async fn handle_command_event(
|
|||||||
// Accept regular or shifted characters (e.g., 'a' or 'A')
|
// Accept regular or shifted characters (e.g., 'a' or 'A')
|
||||||
if key.modifiers.is_empty() || key.modifiers == KeyModifiers::SHIFT {
|
if key.modifiers.is_empty() || key.modifiers == KeyModifiers::SHIFT {
|
||||||
command_input.push(c);
|
command_input.push(c);
|
||||||
return Ok((false, "".to_string(), false));
|
return Ok(EventOutcome::Ok("".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore all other keys
|
// Ignore all other keys
|
||||||
Ok((false, "".to_string(), false))
|
Ok(EventOutcome::Ok("".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_command(
|
async fn process_command(
|
||||||
@@ -74,12 +79,12 @@ async fn process_command(
|
|||||||
terminal: &mut TerminalCore,
|
terminal: &mut TerminalCore,
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
) -> Result<(bool, String, bool), Box<dyn std::error::Error>> {
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
||||||
// Clone the trimmed command to avoid borrow issues
|
// Clone the trimmed command to avoid borrow issues
|
||||||
let command = command_input.trim().to_string();
|
let command = command_input.trim().to_string();
|
||||||
if command.is_empty() {
|
if command.is_empty() {
|
||||||
*command_message = "Empty command".to_string();
|
*command_message = "Empty command".to_string();
|
||||||
return Ok((false, command_message.clone(), false));
|
return Ok(EventOutcome::Ok(command_message.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the action for the command (now checks global and common bindings too)
|
// Get the action for the command (now checks global and common bindings too)
|
||||||
@@ -92,18 +97,26 @@ async fn process_command(
|
|||||||
.handle_command(action, terminal)
|
.handle_command(action, terminal)
|
||||||
.await?;
|
.await?;
|
||||||
command_input.clear();
|
command_input.clear();
|
||||||
Ok((should_exit, message, true))
|
if should_exit {
|
||||||
|
Ok(EventOutcome::Exit(message))
|
||||||
|
} else {
|
||||||
|
Ok(EventOutcome::Ok(message))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"save" => {
|
"save" => {
|
||||||
let message = save(
|
let outcome = save(
|
||||||
form_state,
|
form_state,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
&mut command_handler.is_saved,
|
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
).await?;
|
).await?;
|
||||||
|
let message = match outcome {
|
||||||
|
SaveOutcome::CreatedNew(_) => "New entry created".to_string(),
|
||||||
|
SaveOutcome::UpdatedExisting => "Entry updated".to_string(),
|
||||||
|
SaveOutcome::NoChange => "No changes to save".to_string(),
|
||||||
|
};
|
||||||
command_input.clear();
|
command_input.clear();
|
||||||
return Ok((false, message, true));
|
Ok(EventOutcome::DataSaved(outcome, message))
|
||||||
},
|
},
|
||||||
"revert" => {
|
"revert" => {
|
||||||
let message = revert(
|
let message = revert(
|
||||||
@@ -113,17 +126,12 @@ async fn process_command(
|
|||||||
total_count,
|
total_count,
|
||||||
).await?;
|
).await?;
|
||||||
command_input.clear();
|
command_input.clear();
|
||||||
return Ok((false, message, true));
|
Ok(EventOutcome::Ok(message))
|
||||||
},
|
|
||||||
"unknown" => {
|
|
||||||
let message = format!("Unknown command: {}", command);
|
|
||||||
command_input.clear();
|
|
||||||
return Ok((false, message, true));
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let message = format!("Unhandled action: {}", action);
|
let message = format!("Unhandled action: {}", action);
|
||||||
command_input.clear();
|
command_input.clear();
|
||||||
return Ok((false, message, true));
|
Ok(EventOutcome::Ok(message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ use crate::modes::{
|
|||||||
};
|
};
|
||||||
use crate::config::binds::key_sequences::KeySequenceTracker;
|
use crate::config::binds::key_sequences::KeySequenceTracker;
|
||||||
use crate::modes::handlers::mode_manager::{ModeManager, AppMode};
|
use crate::modes::handlers::mode_manager::{ModeManager, AppMode};
|
||||||
|
use crate::tui::functions::common::form::SaveOutcome;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum EventOutcome {
|
||||||
|
Ok(String), // Normal operation, display message
|
||||||
|
Exit(String), // Signal app exit, display message
|
||||||
|
DataSaved(SaveOutcome, String), // Data save attempted, include outcome and message
|
||||||
|
// Add other outcomes like QuitRequested, SaveAndQuitRequested later if needed
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EventHandler {
|
pub struct EventHandler {
|
||||||
pub command_mode: bool,
|
pub command_mode: bool,
|
||||||
@@ -55,7 +64,7 @@ impl EventHandler {
|
|||||||
app_state: &mut crate::state::state::AppState,
|
app_state: &mut crate::state::state::AppState,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
) -> Result<(bool, String), Box<dyn std::error::Error>> {
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
||||||
let current_mode = ModeManager::derive_mode(app_state, self);
|
let current_mode = ModeManager::derive_mode(app_state, self);
|
||||||
app_state.update_mode(current_mode);
|
app_state.update_mode(current_mode);
|
||||||
|
|
||||||
@@ -64,9 +73,10 @@ impl EventHandler {
|
|||||||
let modifiers = key.modifiers;
|
let modifiers = key.modifiers;
|
||||||
|
|
||||||
if UiStateHandler::toggle_sidebar(&mut app_state.ui, config, key_code, modifiers) {
|
if UiStateHandler::toggle_sidebar(&mut app_state.ui, config, key_code, modifiers) {
|
||||||
return Ok((false, format!("Sidebar {}",
|
let message = format!("Sidebar {}",
|
||||||
if app_state.ui.show_sidebar { "shown" } else { "hidden" }
|
if app_state.ui.show_sidebar { "shown" } else { "hidden" }
|
||||||
)));
|
);
|
||||||
|
return Ok(EventOutcome::Ok(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
match current_mode {
|
match current_mode {
|
||||||
@@ -90,7 +100,7 @@ impl EventHandler {
|
|||||||
self.edit_mode_cooldown = true;
|
self.edit_mode_cooldown = true;
|
||||||
self.command_message = "Edit mode".to_string();
|
self.command_message = "Edit mode".to_string();
|
||||||
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
|
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
|
||||||
return Ok((false, self.command_message.clone()));
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.is_enter_edit_mode_after(key_code, modifiers) &&
|
if config.is_enter_edit_mode_after(key_code, modifiers) &&
|
||||||
@@ -119,7 +129,7 @@ impl EventHandler {
|
|||||||
self.edit_mode_cooldown = true;
|
self.edit_mode_cooldown = true;
|
||||||
self.command_message = "Edit mode (after cursor)".to_string();
|
self.command_message = "Edit mode (after cursor)".to_string();
|
||||||
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
|
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
|
||||||
return Ok((false, self.command_message.clone()));
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(action) = config.get_read_only_action_for_key(key_code, modifiers) {
|
if let Some(action) = config.get_read_only_action_for_key(key_code, modifiers) {
|
||||||
@@ -127,7 +137,7 @@ impl EventHandler {
|
|||||||
self.command_mode = true;
|
self.command_mode = true;
|
||||||
self.command_input.clear();
|
self.command_input.clear();
|
||||||
self.command_message.clear();
|
self.command_message.clear();
|
||||||
return Ok((false, String::new()));
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +164,7 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return read_only::handle_read_only_event(
|
let message = read_only::handle_read_only_event(
|
||||||
app_state,
|
app_state,
|
||||||
key,
|
key,
|
||||||
config,
|
config,
|
||||||
@@ -167,26 +177,27 @@ impl EventHandler {
|
|||||||
&mut self.command_message,
|
&mut self.command_message,
|
||||||
&mut self.edit_mode_cooldown,
|
&mut self.edit_mode_cooldown,
|
||||||
&mut self.ideal_cursor_column,
|
&mut self.ideal_cursor_column,
|
||||||
).await;
|
).await?;
|
||||||
|
return Ok(EventOutcome::Ok(message));
|
||||||
},
|
},
|
||||||
|
|
||||||
AppMode::Edit => {
|
AppMode::Edit => {
|
||||||
if config.is_exit_edit_mode(key_code, modifiers) {
|
if config.is_exit_edit_mode(key_code, modifiers) {
|
||||||
self.is_edit_mode = false;
|
self.is_edit_mode = false;
|
||||||
self.edit_mode_cooldown = true;
|
self.edit_mode_cooldown = true;
|
||||||
|
|
||||||
let has_changes = if app_state.ui.show_login {
|
let has_changes = if app_state.ui.show_login {
|
||||||
auth_state.has_unsaved_changes()
|
auth_state.has_unsaved_changes()
|
||||||
} else {
|
} else {
|
||||||
form_state.has_unsaved_changes()
|
form_state.has_unsaved_changes()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.command_message = if has_changes {
|
self.command_message = if has_changes {
|
||||||
"Exited edit mode (unsaved changes remain)".to_string()
|
"Exited edit mode (unsaved changes remain)".to_string()
|
||||||
} else {
|
} else {
|
||||||
"Read-only mode".to_string()
|
"Read-only mode".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
|
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
|
||||||
|
|
||||||
let current_input = if app_state.ui.show_login {
|
let current_input = if app_state.ui.show_login {
|
||||||
@@ -210,7 +221,7 @@ impl EventHandler {
|
|||||||
self.ideal_cursor_column = form_state.current_cursor_pos();
|
self.ideal_cursor_column = form_state.current_cursor_pos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok((false, self.command_message.clone()));
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(action) = config.get_action_for_key_in_mode(
|
if let Some(action) = config.get_action_for_key_in_mode(
|
||||||
@@ -220,23 +231,23 @@ impl EventHandler {
|
|||||||
) {
|
) {
|
||||||
match action {
|
match action {
|
||||||
"save" | "force_quit" | "save_and_quit" | "revert" => {
|
"save" | "force_quit" | "save_and_quit" | "revert" => {
|
||||||
return common_mode::handle_core_action(
|
return common_mode::handle_core_action(
|
||||||
action,
|
action,
|
||||||
form_state,
|
form_state,
|
||||||
auth_state,
|
auth_state,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
&mut self.auth_client,
|
&mut self.auth_client,
|
||||||
terminal,
|
terminal,
|
||||||
app_state,
|
app_state,
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
).await;
|
).await;
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = edit::handle_edit_event(
|
let message = edit::handle_edit_event(
|
||||||
app_state.ui.show_login,
|
app_state.ui.show_login,
|
||||||
key,
|
key,
|
||||||
config,
|
config,
|
||||||
@@ -244,18 +255,17 @@ impl EventHandler {
|
|||||||
auth_state,
|
auth_state,
|
||||||
&mut self.ideal_cursor_column,
|
&mut self.ideal_cursor_column,
|
||||||
&mut self.command_message,
|
&mut self.command_message,
|
||||||
&mut app_state.ui.is_saved,
|
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
grpc_client,
|
grpc_client,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
self.key_sequence_tracker.reset();
|
self.key_sequence_tracker.reset();
|
||||||
return Ok((false, result));
|
return Ok(EventOutcome::Ok(message));
|
||||||
},
|
},
|
||||||
|
|
||||||
AppMode::Command => {
|
AppMode::Command => {
|
||||||
let (should_exit, message, exit_command_mode) = command_mode::handle_command_event(
|
let outcome = command_mode::handle_command_event(
|
||||||
key,
|
key,
|
||||||
config,
|
config,
|
||||||
form_state,
|
form_state,
|
||||||
@@ -267,17 +277,18 @@ impl EventHandler {
|
|||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
if exit_command_mode {
|
if let EventOutcome::Ok(msg) = &outcome {
|
||||||
self.command_mode = false;
|
if msg == "Exited command mode" {
|
||||||
|
self.command_mode = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return Ok(outcome);
|
||||||
return Ok((should_exit, message));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.edit_mode_cooldown = false;
|
self.edit_mode_cooldown = false;
|
||||||
Ok((false, self.command_message.clone()))
|
Ok(EventOutcome::Ok(self.command_message.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::services::grpc_client::GrpcClient;
|
use crate::services::grpc_client::GrpcClient;
|
||||||
use crate::state::pages::form::FormState;
|
use crate::state::pages::form::FormState;
|
||||||
|
use crate::tui::functions::common::form::SaveOutcome;
|
||||||
use crate::state::state::AppState;
|
use crate::state::state::AppState;
|
||||||
|
|
||||||
pub struct UiService;
|
pub struct UiService;
|
||||||
@@ -85,5 +86,27 @@ impl UiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles the consequences of a save operation, like updating counts.
|
||||||
|
pub async fn handle_save_outcome(
|
||||||
|
save_outcome: SaveOutcome,
|
||||||
|
grpc_client: &mut GrpcClient,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
form_state: &mut FormState, // Needed to potentially update position/ID
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
match save_outcome {
|
||||||
|
SaveOutcome::CreatedNew(new_id) => {
|
||||||
|
// A new record was created, update the count!
|
||||||
|
UiService::update_adresar_count(grpc_client, app_state).await?;
|
||||||
|
// Navigate to the new record (now that count is updated)
|
||||||
|
app_state.update_current_position(app_state.total_count);
|
||||||
|
form_state.id = new_id; // Ensure ID is set (might be redundant if save already did it)
|
||||||
|
}
|
||||||
|
SaveOutcome::UpdatedExisting | SaveOutcome::NoChange => {
|
||||||
|
// No count update needed for these outcomes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,26 @@ use crate::services::grpc_client::GrpcClient;
|
|||||||
use crate::state::pages::form::FormState;
|
use crate::state::pages::form::FormState;
|
||||||
use common::proto::multieko2::adresar::{PostAdresarRequest, PutAdresarRequest};
|
use common::proto::multieko2::adresar::{PostAdresarRequest, PutAdresarRequest};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum SaveOutcome {
|
||||||
|
NoChange, // Nothing needed saving
|
||||||
|
UpdatedExisting, // An existing record was updated
|
||||||
|
CreatedNew(i64), // A new record was created (include its new ID)
|
||||||
|
}
|
||||||
|
|
||||||
/// Shared logic for saving the current form state
|
/// Shared logic for saving the current form state
|
||||||
pub async fn save(
|
pub async fn save(
|
||||||
form_state: &mut FormState,
|
form_state: &mut FormState,
|
||||||
grpc_client: &mut GrpcClient,
|
grpc_client: &mut GrpcClient,
|
||||||
is_saved: &mut bool,
|
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<SaveOutcome, Box<dyn std::error::Error>> { // <-- Return SaveOutcome
|
||||||
|
if !form_state.has_unsaved_changes {
|
||||||
|
return Ok(SaveOutcome::NoChange); // Early exit if no changes
|
||||||
|
}
|
||||||
let is_new = *current_position == total_count + 1;
|
let is_new = *current_position == total_count + 1;
|
||||||
|
|
||||||
let message = if is_new {
|
let outcome = if is_new {
|
||||||
let post_request = PostAdresarRequest {
|
let post_request = PostAdresarRequest {
|
||||||
firma: form_state.values[0].clone(),
|
firma: form_state.values[0].clone(),
|
||||||
kz: form_state.values[1].clone(),
|
kz: form_state.values[1].clone(),
|
||||||
@@ -33,10 +42,9 @@ pub async fn save(
|
|||||||
fax: form_state.values[14].clone(),
|
fax: form_state.values[14].clone(),
|
||||||
};
|
};
|
||||||
let response = grpc_client.post_adresar(post_request).await?;
|
let response = grpc_client.post_adresar(post_request).await?;
|
||||||
let new_total = grpc_client.get_adresar_count().await?;
|
let new_id = response.into_inner().id;
|
||||||
*current_position = new_total;
|
form_state.id = new_id;
|
||||||
form_state.id = response.into_inner().id;
|
SaveOutcome::CreatedNew(new_id) // <-- Return CreatedNew with ID
|
||||||
"New entry created".to_string()
|
|
||||||
} else {
|
} else {
|
||||||
let put_request = PutAdresarRequest {
|
let put_request = PutAdresarRequest {
|
||||||
id: form_state.id,
|
id: form_state.id,
|
||||||
@@ -57,12 +65,11 @@ pub async fn save(
|
|||||||
fax: form_state.values[14].clone(),
|
fax: form_state.values[14].clone(),
|
||||||
};
|
};
|
||||||
let _ = grpc_client.put_adresar(put_request).await?;
|
let _ = grpc_client.put_adresar(put_request).await?;
|
||||||
"Entry updated".to_string()
|
SaveOutcome::UpdatedExisting
|
||||||
};
|
};
|
||||||
|
|
||||||
*is_saved = true;
|
|
||||||
form_state.has_unsaved_changes = false;
|
form_state.has_unsaved_changes = false;
|
||||||
Ok(message)
|
Ok(outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discard changes since last save
|
/// Discard changes since last save
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
// src/ui/handlers/ui.rs
|
// src/ui/handlers/ui.rs
|
||||||
|
|
||||||
use crate::tui::terminal::TerminalCore;
|
use crate::config::binds::config::Config;
|
||||||
|
use crate::config::colors::themes::Theme;
|
||||||
|
use crate::modes::common::commands::CommandHandler;
|
||||||
|
use crate::modes::handlers::event::{EventHandler, EventOutcome}; // Import EventOutcome
|
||||||
|
use crate::modes::handlers::mode_manager::{AppMode, ModeManager};
|
||||||
use crate::services::grpc_client::GrpcClient;
|
use crate::services::grpc_client::GrpcClient;
|
||||||
use crate::services::ui_service::UiService;
|
use crate::services::ui_service::UiService;
|
||||||
use crate::tui::terminal::EventReader;
|
|
||||||
use crate::modes::common::commands::CommandHandler;
|
|
||||||
use crate::modes::handlers::mode_manager::{AppMode, ModeManager};
|
|
||||||
use crate::config::colors::themes::Theme;
|
|
||||||
use crate::config::binds::config::Config;
|
|
||||||
use crate::ui::handlers::render::render_ui;
|
|
||||||
use crate::state::pages::form::FormState;
|
|
||||||
use crate::state::pages::auth::AuthState;
|
|
||||||
use crate::state::canvas_state::CanvasState;
|
use crate::state::canvas_state::CanvasState;
|
||||||
use crate::modes::handlers::event::EventHandler;
|
use crate::state::pages::auth::AuthState;
|
||||||
|
use crate::state::pages::form::FormState;
|
||||||
use crate::state::state::AppState;
|
use crate::state::state::AppState;
|
||||||
|
use crate::tui::functions::common::form::SaveOutcome; // Import SaveOutcome
|
||||||
|
use crate::tui::terminal::{EventReader, TerminalCore};
|
||||||
|
use crate::ui::handlers::render::render_ui;
|
||||||
use crossterm::cursor::SetCursorStyle;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
|
|
||||||
pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let config = Config::load()?;
|
let config = Config::load()?;
|
||||||
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 auth_client = AuthClient::new().await?; // AuthClient is now inside EventHandler
|
|
||||||
let mut command_handler = CommandHandler::new();
|
let mut command_handler = CommandHandler::new();
|
||||||
let theme = Theme::from_str(&config.colors.theme);
|
let theme = Theme::from_str(&config.colors.theme);
|
||||||
let mut auth_state = AuthState::default(); // The single source of truth for AuthState
|
let mut auth_state = AuthState::default(); // The single source of truth for AuthState
|
||||||
@@ -29,7 +28,9 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let mut app_state = AppState::new()?;
|
let mut app_state = AppState::new()?;
|
||||||
|
|
||||||
// Initialize app state with profile tree and table structure
|
// Initialize app state with profile tree and table structure
|
||||||
let column_names = UiService::initialize_app_state(&mut grpc_client, &mut app_state).await?;
|
let column_names =
|
||||||
|
UiService::initialize_app_state(&mut grpc_client, &mut app_state)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Initialize FormState with dynamic fields
|
// Initialize FormState with dynamic fields
|
||||||
let mut form_state = FormState::new(column_names);
|
let mut form_state = FormState::new(column_names);
|
||||||
@@ -39,7 +40,8 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let event_reader = EventReader::new();
|
let event_reader = EventReader::new();
|
||||||
|
|
||||||
// Fetch the total count of Adresar entries
|
// Fetch the total count of Adresar entries
|
||||||
UiService::initialize_adresar_count(&mut grpc_client, &mut app_state).await?;
|
UiService::initialize_adresar_count(&mut grpc_client, &mut app_state)
|
||||||
|
.await?;
|
||||||
form_state.reset_to_empty();
|
form_state.reset_to_empty();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -63,97 +65,158 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
);
|
);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// --- Cursor Visibility Logic ---
|
// --- Cursor Visibility Logic ---
|
||||||
let current_mode = ModeManager::derive_mode(&app_state, &event_handler);
|
let current_mode = ModeManager::derive_mode(&app_state, &event_handler);
|
||||||
match current_mode {
|
match current_mode {
|
||||||
AppMode::Edit => {
|
AppMode::Edit => {
|
||||||
terminal.show_cursor()?;
|
terminal.show_cursor()?;
|
||||||
}
|
}
|
||||||
AppMode::ReadOnly => {
|
AppMode::ReadOnly => {
|
||||||
if !app_state.ui.focus_outside_canvas {
|
if !app_state.ui.focus_outside_canvas {
|
||||||
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
|
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
|
||||||
} else {
|
} else {
|
||||||
terminal.set_cursor_style(SetCursorStyle::SteadyUnderScore)?;
|
terminal
|
||||||
}
|
.set_cursor_style(SetCursorStyle::SteadyUnderScore)?;
|
||||||
terminal.show_cursor()?; // Ensure visible
|
}
|
||||||
}
|
terminal.show_cursor()?; // Ensure visible
|
||||||
AppMode::General | AppMode::Command => {
|
}
|
||||||
terminal.set_cursor_style(SetCursorStyle::SteadyUnderScore)?;
|
AppMode::General | AppMode::Command => {
|
||||||
terminal.show_cursor()?; // Ensure visible (though might not be positioned meaningfully)
|
terminal.set_cursor_style(SetCursorStyle::SteadyUnderScore)?;
|
||||||
}
|
terminal.show_cursor()?; // Ensure visible (though might not be positioned meaningfully)
|
||||||
}
|
}
|
||||||
// --- End Cursor Visibility Logic ---
|
}
|
||||||
|
// --- End Cursor Visibility Logic ---
|
||||||
let total_count = app_state.total_count;
|
|
||||||
|
let total_count = app_state.total_count; // Keep track for save logic
|
||||||
let mut current_position = app_state.current_position;
|
let mut current_position = app_state.current_position;
|
||||||
// Store position before event handling to detect navigation
|
|
||||||
let position_before_event = current_position;
|
let position_before_event = current_position;
|
||||||
|
|
||||||
let event = event_reader.read_event()?;
|
let event = event_reader.read_event()?;
|
||||||
let (should_exit, message) = event_handler.handle_event(
|
|
||||||
event,
|
|
||||||
&config,
|
|
||||||
&mut terminal,
|
|
||||||
&mut grpc_client,
|
|
||||||
&mut command_handler,
|
|
||||||
&mut form_state,
|
|
||||||
&mut auth_state, // Pass the single AuthState instance here too
|
|
||||||
&mut app_state,
|
|
||||||
total_count,
|
|
||||||
&mut current_position,
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
|
// Get the outcome from the event handler
|
||||||
|
let event_outcome_result = event_handler
|
||||||
|
.handle_event(
|
||||||
|
event,
|
||||||
|
&config,
|
||||||
|
&mut terminal, // Pass terminal mutably
|
||||||
|
&mut grpc_client,
|
||||||
|
&mut command_handler,
|
||||||
|
&mut form_state,
|
||||||
|
&mut auth_state,
|
||||||
|
&mut app_state,
|
||||||
|
total_count, // Pass the count *before* potential save
|
||||||
|
&mut current_position,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Update position based on handler's modification
|
||||||
app_state.current_position = current_position;
|
app_state.current_position = current_position;
|
||||||
|
|
||||||
let position_changed = app_state.current_position != position_before_event;
|
// --- Centralized Consequence Handling ---
|
||||||
|
let mut should_exit = false;
|
||||||
|
match event_outcome_result {
|
||||||
|
// Handle the Result first
|
||||||
|
Ok(outcome) => match outcome {
|
||||||
|
// Handle the Ok variant containing EventOutcome
|
||||||
|
EventOutcome::Ok(message) => {
|
||||||
|
if !message.is_empty() {
|
||||||
|
event_handler.command_message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventOutcome::Exit(message) => {
|
||||||
|
event_handler.command_message = message;
|
||||||
|
should_exit = true;
|
||||||
|
}
|
||||||
|
EventOutcome::DataSaved(save_outcome, message) => {
|
||||||
|
event_handler.command_message = message; // Show save status
|
||||||
|
|
||||||
|
// *** Delegate outcome handling to UiService ***
|
||||||
|
if let Err(e) = UiService::handle_save_outcome(
|
||||||
|
save_outcome,
|
||||||
|
&mut grpc_client,
|
||||||
|
&mut app_state,
|
||||||
|
&mut form_state,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
// Handle potential errors from the outcome handler itself
|
||||||
|
event_handler.command_message =
|
||||||
|
format!("Error handling save outcome: {}", e);
|
||||||
|
}
|
||||||
|
// No count update needed for UpdatedExisting or NoChange
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
// Handle errors from handle_event, e.g., log or display
|
||||||
|
event_handler.command_message = format!("Error: {}", e);
|
||||||
|
// Decide if the error is fatal, maybe set should_exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Position Change Handling (after outcome processing) ---
|
||||||
|
let position_changed =
|
||||||
|
app_state.current_position != position_before_event; // Calculate after potential update
|
||||||
|
// Recalculate total_count *after* potential update
|
||||||
|
let current_total_count = app_state.total_count;
|
||||||
|
|
||||||
// Handle position changes and update form state (Only when form is shown)
|
// Handle position changes and update form state (Only when form is shown)
|
||||||
if app_state.ui.show_form {
|
if app_state.ui.show_form {
|
||||||
if position_changed && !event_handler.is_edit_mode {
|
if position_changed && !event_handler.is_edit_mode {
|
||||||
let current_input = form_state.get_current_input();
|
let current_input = form_state.get_current_input();
|
||||||
let max_cursor_pos = if !current_input.is_empty() {
|
let max_cursor_pos = if !current_input.is_empty() {
|
||||||
current_input.len() - 1 // Limit to last character in readonly mode
|
current_input.len() - 1 // Limit to last character in readonly mode
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
|
form_state.current_cursor_pos =
|
||||||
|
event_handler.ideal_cursor_column.min(max_cursor_pos);
|
||||||
|
|
||||||
// Ensure position never exceeds total_count + 1
|
// Ensure position never exceeds total_count + 1
|
||||||
if app_state.current_position > total_count + 1 {
|
if app_state.current_position > current_total_count + 1 {
|
||||||
app_state.current_position = total_count + 1;
|
app_state.current_position = current_total_count + 1;
|
||||||
}
|
}
|
||||||
if app_state.current_position > total_count {
|
if app_state.current_position > current_total_count {
|
||||||
// New entry - reset form
|
// New entry - reset form
|
||||||
form_state.reset_to_empty();
|
form_state.reset_to_empty();
|
||||||
form_state.current_field = 0;
|
form_state.current_field = 0;
|
||||||
} else if app_state.current_position >= 1 && app_state.current_position <= total_count {
|
} else if app_state.current_position >= 1
|
||||||
|
&& app_state.current_position <= current_total_count
|
||||||
|
{
|
||||||
// Existing entry - load data
|
// Existing entry - load data
|
||||||
let current_position_to_load = app_state.current_position; // Use a copy
|
let current_position_to_load = app_state.current_position; // Use a copy
|
||||||
let load_message = UiService::load_adresar_by_position(
|
let load_message = UiService::load_adresar_by_position(
|
||||||
&mut grpc_client,
|
&mut grpc_client,
|
||||||
&mut app_state, // Pass app_state mutably if needed by the service
|
&mut app_state, // Pass app_state mutably if needed by the service
|
||||||
&mut form_state,
|
&mut form_state,
|
||||||
current_position_to_load
|
current_position_to_load,
|
||||||
).await?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let current_input = form_state.get_current_input();
|
let current_input = form_state.get_current_input();
|
||||||
let max_cursor_pos = if !event_handler.is_edit_mode && !current_input.is_empty() {
|
let max_cursor_pos = if !event_handler.is_edit_mode
|
||||||
current_input.len() - 1 // In readonly mode, limit to last character
|
&& !current_input.is_empty()
|
||||||
|
{
|
||||||
|
current_input.len() - 1 // In readonly mode, limit to last character
|
||||||
} else {
|
} else {
|
||||||
current_input.len()
|
current_input.len()
|
||||||
};
|
};
|
||||||
form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
|
form_state.current_cursor_pos = event_handler
|
||||||
|
.ideal_cursor_column
|
||||||
|
.min(max_cursor_pos);
|
||||||
// Don't overwrite message from handle_event if load_message is simple success
|
// Don't overwrite message from handle_event if load_message is simple success
|
||||||
if !load_message.starts_with("Loaded entry") || message.is_empty() {
|
if !load_message.starts_with("Loaded entry")
|
||||||
event_handler.command_message = load_message;
|
|| event_handler.command_message.is_empty()
|
||||||
|
{
|
||||||
|
event_handler.command_message = load_message;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Invalid position (e.g., 0) - reset to first entry or new entry mode
|
// Invalid position (e.g., 0) - reset to first entry or new entry mode
|
||||||
app_state.current_position = 1.min(total_count + 1); // Go to 1 or new entry if empty
|
app_state.current_position =
|
||||||
if app_state.current_position > total_count {
|
1.min(current_total_count + 1); // Go to 1 or new entry if empty
|
||||||
form_state.reset_to_empty();
|
if app_state.current_position > total_count {
|
||||||
form_state.current_field = 0;
|
form_state.reset_to_empty();
|
||||||
}
|
form_state.current_field = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if !position_changed && !event_handler.is_edit_mode {
|
} else if !position_changed && !event_handler.is_edit_mode {
|
||||||
// If position didn't change but we are in read-only, just adjust cursor
|
// If position didn't change but we are in read-only, just adjust cursor
|
||||||
@@ -163,29 +226,26 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
|
form_state.current_cursor_pos =
|
||||||
|
event_handler.ideal_cursor_column.min(max_cursor_pos);
|
||||||
}
|
}
|
||||||
} else if app_state.ui.show_login {
|
} else if app_state.ui.show_login {
|
||||||
// Handle cursor updates for AuthState if needed, similar to FormState
|
// Handle cursor updates for AuthState if needed, similar to FormState
|
||||||
if !event_handler.is_edit_mode {
|
if !event_handler.is_edit_mode {
|
||||||
let current_input = auth_state.get_current_input();
|
let current_input = auth_state.get_current_input();
|
||||||
let max_cursor_pos = if !current_input.is_empty() {
|
let max_cursor_pos = if !current_input.is_empty() {
|
||||||
current_input.len() - 1
|
current_input.len() - 1
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
auth_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos);
|
auth_state.current_cursor_pos =
|
||||||
}
|
event_handler.ideal_cursor_column.min(max_cursor_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Only update command message if handle_event provided one
|
|
||||||
if !message.is_empty() {
|
|
||||||
event_handler.command_message = message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check exit condition *after* processing outcome
|
||||||
if should_exit {
|
if should_exit {
|
||||||
|
// terminal.cleanup()?; // Optional: Drop handles this
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user