BREAKING CHANGES updating gRPC based on the enum now

This commit is contained in:
filipriec
2025-04-07 21:27:01 +02:00
parent bb103fac6c
commit 70678432c6
7 changed files with 316 additions and 182 deletions

View File

@@ -4,6 +4,8 @@ use crate::services::grpc_client::GrpcClient;
use crate::state::canvas_state::CanvasState;
use crate::state::pages::form::FormState;
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 std::any::Any;
@@ -11,48 +13,61 @@ pub async fn execute_common_action<S: CanvasState + Any>(
action: &str,
state: &mut S,
grpc_client: &mut GrpcClient,
is_saved: &mut bool,
current_position: &mut u64,
total_count: u64,
) -> Result<String, Box<dyn std::error::Error>> {
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
match action {
"save" | "revert" => {
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) =
(state as &mut dyn Any).downcast_mut::<FormState>()
{
match action {
"save" => {
save(
let save_result = save(
form_state,
grpc_client,
is_saved,
current_position,
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(
let revert_result = revert(
form_state,
grpc_client,
current_position,
total_count,
)
.await
).await;
match revert_result {
Ok(message) => Ok(EventOutcome::Ok(message)),
Err(e) => Err(e),
}
}
_ => unreachable!(),
}
} else {
Ok(format!(
Ok(EventOutcome::Ok(format!(
"Action '{}' not implemented for this state type.",
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,
state: &mut S,
ideal_cursor_column: &mut usize,
_grpc_client: &mut GrpcClient,
_is_saved: &mut bool,
_current_position: &mut u64,
_total_count: u64,
grpc_client: &mut GrpcClient,
is_saved: &mut bool,
current_position: &mut u64,
total_count: u64,
) -> Result<String, Box<dyn std::error::Error>> {
match action {
"insert_char" => {

View File

@@ -1,10 +1,12 @@
// src/modes/canvas/common.rs
// src/modes/canvas/common_mode.rs
use crate::tui::terminal::core::TerminalCore;
use crate::state::pages::{form::FormState, auth::AuthState};
use crate::state::state::AppState;
use crate::services::grpc_client::GrpcClient;
use crate::services::auth::AuthClient;
use crate::modes::handlers::event::EventOutcome;
use crate::tui::functions::common::form::SaveOutcome;
use crate::tui::functions::common::{
form::{save as form_save, revert as form_revert},
login::{save as login_save, revert as login_revert}
@@ -20,46 +22,54 @@ pub async fn handle_core_action(
app_state: &mut AppState,
current_position: &mut u64,
total_count: u64,
) -> Result<(bool, String), Box<dyn std::error::Error>> {
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
match action {
"save" => {
if app_state.ui.show_login {
let message = login_save(auth_state, auth_client, app_state).await?;
Ok((false, message))
Ok(EventOutcome::Ok(message))
} else {
let message = form_save(
let save_outcome = form_save(
form_state,
grpc_client,
&mut app_state.ui.is_saved,
current_position,
total_count,
).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" => {
terminal.cleanup()?;
Ok((true, "Force exiting without saving.".to_string()))
Ok(EventOutcome::Exit("Force exiting without saving.".to_string()))
},
"save_and_quit" => {
let message = if app_state.ui.show_login {
login_save(auth_state, auth_client, app_state).await?
} else {
form_save(
let save_outcome = form_save(
form_state,
grpc_client,
&mut app_state.ui.is_saved,
current_position,
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()?;
Ok((true, format!("{}. Exiting application.", message)))
Ok(EventOutcome::Exit(format!("{}. Exiting application.", message)))
},
"revert" => {
if app_state.ui.show_login {
let message = login_revert(auth_state, app_state).await;
Ok((false, message))
Ok(EventOutcome::Ok(message))
} else {
let message = form_revert(
form_state,
@@ -67,9 +77,9 @@ pub async fn handle_core_action(
current_position,
total_count,
).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))),
}
}

View File

@@ -1,4 +1,4 @@
// src/modes/handlers/command_mode.rs
// src/modes/common/command_mode.rs
use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
use crate::config::binds::config::Config;
@@ -7,6 +7,14 @@ use crate::state::pages::form::FormState;
use crate::modes::common::commands::CommandHandler;
use crate::tui::terminal::core::TerminalCore;
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(
key: KeyEvent,
@@ -19,15 +27,12 @@ pub async fn handle_command_event(
terminal: &mut TerminalCore,
current_position: &mut u64,
total_count: u64,
) -> Result<(bool, String, bool), Box<dyn std::error::Error>> {
// Return value: (should_exit, message, should_exit_command_mode)
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
// Exit command mode (via configurable keybinding)
if config.is_exit_command_mode(key.code, key.modifiers) {
command_input.clear();
*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)
@@ -48,7 +53,7 @@ pub async fn handle_command_event(
// Backspace (via configurable keybinding, defaults to Backspace)
if config.is_command_backspace(key.code, key.modifiers) {
command_input.pop();
return Ok((false, "".to_string(), false));
return Ok(EventOutcome::Ok("".to_string()));
}
// 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')
if key.modifiers.is_empty() || key.modifiers == KeyModifiers::SHIFT {
command_input.push(c);
return Ok((false, "".to_string(), false));
return Ok(EventOutcome::Ok("".to_string()));
}
}
// Ignore all other keys
Ok((false, "".to_string(), false))
Ok(EventOutcome::Ok("".to_string()))
}
async fn process_command(
@@ -74,12 +79,12 @@ async fn process_command(
terminal: &mut TerminalCore,
current_position: &mut 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
let command = command_input.trim().to_string();
if command.is_empty() {
*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)
@@ -92,18 +97,26 @@ async fn process_command(
.handle_command(action, terminal)
.await?;
command_input.clear();
Ok((should_exit, message, true))
if should_exit {
Ok(EventOutcome::Exit(message))
} else {
Ok(EventOutcome::Ok(message))
}
},
"save" => {
let message = save(
let outcome = save(
form_state,
grpc_client,
&mut command_handler.is_saved,
current_position,
total_count,
).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();
return Ok((false, message, true));
Ok(EventOutcome::DataSaved(outcome, message))
},
"revert" => {
let message = revert(
@@ -113,17 +126,12 @@ async fn process_command(
total_count,
).await?;
command_input.clear();
return Ok((false, message, true));
},
"unknown" => {
let message = format!("Unknown command: {}", command);
command_input.clear();
return Ok((false, message, true));
Ok(EventOutcome::Ok(message))
},
_ => {
let message = format!("Unhandled action: {}", action);
command_input.clear();
return Ok((false, message, true));
Ok(EventOutcome::Ok(message))
}
}
}

View File

@@ -17,6 +17,15 @@ use crate::modes::{
};
use crate::config::binds::key_sequences::KeySequenceTracker;
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 command_mode: bool,
@@ -55,7 +64,7 @@ impl EventHandler {
app_state: &mut crate::state::state::AppState,
total_count: 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);
app_state.update_mode(current_mode);
@@ -64,9 +73,10 @@ impl EventHandler {
let modifiers = key.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" }
)));
);
return Ok(EventOutcome::Ok(message));
}
match current_mode {
@@ -90,7 +100,7 @@ impl EventHandler {
self.edit_mode_cooldown = true;
self.command_message = "Edit mode".to_string();
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) &&
@@ -119,7 +129,7 @@ impl EventHandler {
self.edit_mode_cooldown = true;
self.command_message = "Edit mode (after cursor)".to_string();
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) {
@@ -127,7 +137,7 @@ impl EventHandler {
self.command_mode = true;
self.command_input.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,
key,
config,
@@ -167,7 +177,8 @@ impl EventHandler {
&mut self.command_message,
&mut self.edit_mode_cooldown,
&mut self.ideal_cursor_column,
).await;
).await?;
return Ok(EventOutcome::Ok(message));
},
AppMode::Edit => {
@@ -210,7 +221,7 @@ impl EventHandler {
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(
@@ -236,7 +247,7 @@ impl EventHandler {
}
}
let result = edit::handle_edit_event(
let message = edit::handle_edit_event(
app_state.ui.show_login,
key,
config,
@@ -244,18 +255,17 @@ impl EventHandler {
auth_state,
&mut self.ideal_cursor_column,
&mut self.command_message,
&mut app_state.ui.is_saved,
current_position,
total_count,
grpc_client,
).await?;
self.key_sequence_tracker.reset();
return Ok((false, result));
return Ok(EventOutcome::Ok(message));
},
AppMode::Command => {
let (should_exit, message, exit_command_mode) = command_mode::handle_command_event(
let outcome = command_mode::handle_command_event(
key,
config,
form_state,
@@ -268,16 +278,17 @@ impl EventHandler {
total_count,
).await?;
if exit_command_mode {
if let EventOutcome::Ok(msg) = &outcome {
if msg == "Exited command mode" {
self.command_mode = false;
}
return Ok((should_exit, message));
}
return Ok(outcome);
}
}
}
self.edit_mode_cooldown = false;
Ok((false, self.command_message.clone()))
Ok(EventOutcome::Ok(self.command_message.clone()))
}
}

View File

@@ -2,6 +2,7 @@
use crate::services::grpc_client::GrpcClient;
use crate::state::pages::form::FormState;
use crate::tui::functions::common::form::SaveOutcome;
use crate::state::state::AppState;
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(())
}
}

View File

@@ -4,17 +4,26 @@ use crate::services::grpc_client::GrpcClient;
use crate::state::pages::form::FormState;
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
pub async fn save(
form_state: &mut FormState,
grpc_client: &mut GrpcClient,
is_saved: &mut bool,
current_position: &mut 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 message = if is_new {
let outcome = if is_new {
let post_request = PostAdresarRequest {
firma: form_state.values[0].clone(),
kz: form_state.values[1].clone(),
@@ -33,10 +42,9 @@ pub async fn save(
fax: form_state.values[14].clone(),
};
let response = grpc_client.post_adresar(post_request).await?;
let new_total = grpc_client.get_adresar_count().await?;
*current_position = new_total;
form_state.id = response.into_inner().id;
"New entry created".to_string()
let new_id = response.into_inner().id;
form_state.id = new_id;
SaveOutcome::CreatedNew(new_id) // <-- Return CreatedNew with ID
} else {
let put_request = PutAdresarRequest {
id: form_state.id,
@@ -57,12 +65,11 @@ pub async fn save(
fax: form_state.values[14].clone(),
};
let _ = grpc_client.put_adresar(put_request).await?;
"Entry updated".to_string()
SaveOutcome::UpdatedExisting
};
*is_saved = true;
form_state.has_unsaved_changes = false;
Ok(message)
Ok(outcome)
}
/// Discard changes since last save

View File

@@ -1,26 +1,25 @@
// 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::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::modes::handlers::event::EventHandler;
use crate::state::pages::auth::AuthState;
use crate::state::pages::form::FormState;
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;
pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::load()?;
let mut terminal = TerminalCore::new()?;
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 theme = Theme::from_str(&config.colors.theme);
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()?;
// 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
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();
// 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();
loop {
@@ -73,7 +75,8 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
if !app_state.ui.focus_outside_canvas {
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
} else {
terminal.set_cursor_style(SetCursorStyle::SteadyUnderScore)?;
terminal
.set_cursor_style(SetCursorStyle::SteadyUnderScore)?;
}
terminal.show_cursor()?; // Ensure visible
}
@@ -84,28 +87,77 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
}
// --- 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;
// Store position before event handling to detect navigation
let position_before_event = current_position;
let event = event_reader.read_event()?;
let (should_exit, message) = event_handler.handle_event(
// Get the outcome from the event handler
let event_outcome_result = event_handler
.handle_event(
event,
&config,
&mut terminal,
&mut terminal, // Pass terminal mutably
&mut grpc_client,
&mut command_handler,
&mut form_state,
&mut auth_state, // Pass the single AuthState instance here too
&mut auth_state,
&mut app_state,
total_count,
total_count, // Pass the count *before* potential save
&mut current_position,
).await?;
)
.await;
// Update position based on handler's modification
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)
if app_state.ui.show_form {
@@ -116,40 +168,51 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
} else {
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
if app_state.current_position > total_count + 1 {
app_state.current_position = total_count + 1;
if app_state.current_position > current_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
form_state.reset_to_empty();
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
let current_position_to_load = app_state.current_position; // Use a copy
let load_message = UiService::load_adresar_by_position(
&mut grpc_client,
&mut app_state, // Pass app_state mutably if needed by the service
&mut form_state,
current_position_to_load
).await?;
current_position_to_load,
)
.await?;
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.is_empty()
{
current_input.len() - 1 // In readonly mode, limit to last character
} else {
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
if !load_message.starts_with("Loaded entry") || message.is_empty() {
if !load_message.starts_with("Loaded entry")
|| event_handler.command_message.is_empty()
{
event_handler.command_message = load_message;
}
} else {
// 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 =
1.min(current_total_count + 1); // Go to 1 or new entry if empty
if app_state.current_position > total_count {
form_state.reset_to_empty();
form_state.current_field = 0;
@@ -163,8 +226,8 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
} else {
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 {
// Handle cursor updates for AuthState if needed, similar to FormState
@@ -175,17 +238,14 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
} else {
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 {
// terminal.cleanup()?; // Optional: Drop handles this
return Ok(());
}
}