compiled
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -170,9 +170,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.87"
|
version = "0.1.88"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97"
|
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -423,6 +423,7 @@ dependencies = [
|
|||||||
name = "client"
|
name = "client"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
"common",
|
"common",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"dirs 6.0.0",
|
"dirs 6.0.0",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-trait = "0.1.88"
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
|
|
||||||
crossterm = "0.28.1"
|
crossterm = "0.28.1"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crossterm::event::{KeyEvent};
|
use crossterm::event::{KeyEvent};
|
||||||
use crate::config::binds::config::Config;
|
use crate::config::binds::config::Config;
|
||||||
|
use crate::modes::handlers::read_only::ReadOnlyHandler;
|
||||||
use crate::state::pages::form::FormState;
|
use crate::state::pages::form::FormState;
|
||||||
use crate::config::binds::key_sequences::KeySequenceTracker;
|
use crate::config::binds::key_sequences::KeySequenceTracker;
|
||||||
use crate::tui::terminal::grpc_client::GrpcClient;
|
use crate::tui::terminal::grpc_client::GrpcClient;
|
||||||
@@ -16,7 +17,6 @@ enum CharType {
|
|||||||
pub async fn handle_read_only_event(
|
pub async fn handle_read_only_event(
|
||||||
key: KeyEvent,
|
key: KeyEvent,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
form_state: &mut FormState,
|
|
||||||
key_sequence_tracker: &mut KeySequenceTracker,
|
key_sequence_tracker: &mut KeySequenceTracker,
|
||||||
current_position: &mut u64,
|
current_position: &mut u64,
|
||||||
total_count: u64,
|
total_count: u64,
|
||||||
@@ -24,8 +24,9 @@ pub async fn handle_read_only_event(
|
|||||||
command_message: &mut String,
|
command_message: &mut String,
|
||||||
edit_mode_cooldown: &mut bool,
|
edit_mode_cooldown: &mut bool,
|
||||||
ideal_cursor_column: &mut usize,
|
ideal_cursor_column: &mut usize,
|
||||||
|
handler: &mut dyn ReadOnlyHandler,
|
||||||
) -> Result<(bool, String), Box<dyn std::error::Error>> {
|
) -> Result<(bool, String), Box<dyn std::error::Error>> {
|
||||||
// Check for entering Edit mode from Read-Only mode
|
// Check edit mode transitions first
|
||||||
if config.is_enter_edit_mode_before(key.code, key.modifiers) {
|
if config.is_enter_edit_mode_before(key.code, key.modifiers) {
|
||||||
*edit_mode_cooldown = true;
|
*edit_mode_cooldown = true;
|
||||||
*command_message = "Entering Edit mode".to_string();
|
*command_message = "Entering Edit mode".to_string();
|
||||||
@@ -33,88 +34,26 @@ pub async fn handle_read_only_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.is_enter_edit_mode_after(key.code, key.modifiers) {
|
if config.is_enter_edit_mode_after(key.code, key.modifiers) {
|
||||||
let current_input = form_state.get_current_input();
|
handler.adjust_cursor_position(ideal_cursor_column);
|
||||||
if !current_input.is_empty() && form_state.current_cursor_pos < current_input.len() {
|
|
||||||
form_state.current_cursor_pos += 1;
|
|
||||||
*ideal_cursor_column = form_state.current_cursor_pos;
|
|
||||||
}
|
|
||||||
*edit_mode_cooldown = true;
|
*edit_mode_cooldown = true;
|
||||||
*command_message = "Entering Edit mode (after cursor)".to_string();
|
*command_message = "Entering Edit mode (after cursor)".to_string();
|
||||||
return Ok((false, command_message.clone()));
|
return Ok((false, command_message.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Read-Only mode keybindings
|
// Process key bindings
|
||||||
if key.modifiers.is_empty() {
|
let sequence = key_sequence_tracker.get_sequence();
|
||||||
key_sequence_tracker.add_key(key.code);
|
let action = config.get_read_only_action_for_key(key.code, key.modifiers)
|
||||||
let sequence = key_sequence_tracker.get_sequence();
|
.or_else(|| config.matches_key_sequence_generalized(&sequence))
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
|
||||||
// Try to match the current sequence against Read-Only mode bindings
|
let result = handler.handle_read_only_action(
|
||||||
if let Some(action) = config.matches_key_sequence_generalized(&sequence) {
|
&action,
|
||||||
let result = execute_action(
|
grpc_client,
|
||||||
action,
|
current_position,
|
||||||
form_state,
|
total_count
|
||||||
ideal_cursor_column,
|
).await?;
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
current_position,
|
|
||||||
total_count,
|
|
||||||
grpc_client,
|
|
||||||
).await?;
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
return Ok((false, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this might be a prefix of a longer sequence
|
Ok((false, result))
|
||||||
if config.is_key_sequence_prefix(&sequence) {
|
|
||||||
return Ok((false, command_message.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since it's not part of a multi-key sequence, check for a direct action
|
|
||||||
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) {
|
|
||||||
let result = execute_action(
|
|
||||||
action,
|
|
||||||
form_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
current_position,
|
|
||||||
total_count,
|
|
||||||
grpc_client,
|
|
||||||
).await?;
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
return Ok((false, result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If modifiers are pressed, check for direct key bindings
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
|
|
||||||
if let Some(action) = config.get_read_only_action_for_key(key.code, key.modifiers) {
|
|
||||||
let result = execute_action(
|
|
||||||
action,
|
|
||||||
form_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
current_position,
|
|
||||||
total_count,
|
|
||||||
grpc_client,
|
|
||||||
).await?;
|
|
||||||
return Ok((false, result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show a helpful message when no binding was found
|
|
||||||
if !*edit_mode_cooldown {
|
|
||||||
let default_key = "i".to_string();
|
|
||||||
let edit_key = config.keybindings.read_only.get("enter_edit_mode_before")
|
|
||||||
.and_then(|keys| keys.first())
|
|
||||||
.unwrap_or(&default_key);
|
|
||||||
*command_message = format!("Read-only mode - press {} to edit", edit_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((false, command_message.clone()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_action(
|
async fn execute_action(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
// src/client/modes/handlers.rs
|
// src/client/modes/handlers.rs
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod mode_manager;
|
pub mod mode_manager;
|
||||||
|
pub mod read_only;
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ impl EventHandler {
|
|||||||
return read_only::handle_read_only_event(
|
return read_only::handle_read_only_event(
|
||||||
key,
|
key,
|
||||||
config,
|
config,
|
||||||
form_state,
|
|
||||||
&mut self.key_sequence_tracker,
|
&mut self.key_sequence_tracker,
|
||||||
current_position,
|
current_position,
|
||||||
total_count,
|
total_count,
|
||||||
@@ -151,6 +150,7 @@ 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,
|
||||||
|
form_state,
|
||||||
).await;
|
).await;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
17
client/src/modes/handlers/read_only.rs
Normal file
17
client/src/modes/handlers/read_only.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// src/modes/handlers/read_only.rs
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use crate::tui::terminal::grpc_client::GrpcClient;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait ReadOnlyHandler {
|
||||||
|
async fn handle_read_only_action(
|
||||||
|
&mut self,
|
||||||
|
action: &str,
|
||||||
|
grpc_client: &mut GrpcClient,
|
||||||
|
current_position: &mut u64,
|
||||||
|
total_count: u64,
|
||||||
|
) -> Result<String, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
// Add this method
|
||||||
|
fn adjust_cursor_position(&mut self, ideal_column: &mut usize);
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
use crate::config::colors::themes::Theme;
|
use crate::config::colors::themes::Theme;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use crate::modes::handlers::read_only::ReadOnlyHandler;
|
||||||
|
use crate::tui::terminal::GrpcClient;
|
||||||
|
|
||||||
pub struct FormState {
|
pub struct FormState {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
@@ -12,6 +15,50 @@ pub struct FormState {
|
|||||||
pub current_cursor_pos: usize,
|
pub current_cursor_pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ReadOnlyHandler for FormState {
|
||||||
|
async fn handle_read_only_action(
|
||||||
|
&mut self,
|
||||||
|
action: &str,
|
||||||
|
grpc_client: &mut GrpcClient,
|
||||||
|
current_position: &mut u64,
|
||||||
|
total_count: u64,
|
||||||
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
match action {
|
||||||
|
"previous_entry" => {
|
||||||
|
let new_position = current_position.saturating_sub(1);
|
||||||
|
if new_position >= 1 {
|
||||||
|
*current_position = new_position;
|
||||||
|
let response = grpc_client.get_adresar_by_position(*current_position).await?;
|
||||||
|
self.update_from_response(response);
|
||||||
|
Ok(format!("Loaded entry {}", *current_position))
|
||||||
|
} else {
|
||||||
|
Ok("Already at first entry".into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"next_entry" => {
|
||||||
|
if *current_position < total_count {
|
||||||
|
*current_position += 1;
|
||||||
|
let response = grpc_client.get_adresar_by_position(*current_position).await?;
|
||||||
|
self.update_from_response(response);
|
||||||
|
Ok(format!("Loaded entry {}", *current_position))
|
||||||
|
} else {
|
||||||
|
self.reset_to_empty();
|
||||||
|
Ok("New entry mode".into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Ok(format!("Unknown action: {}", action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjust_cursor_position(&mut self, ideal_column: &mut usize) {
|
||||||
|
let current_input = self.get_current_input();
|
||||||
|
let max_pos = current_input.len().saturating_sub(1);
|
||||||
|
self.current_cursor_pos = (*ideal_column).min(max_pos);
|
||||||
|
*ideal_column = self.current_cursor_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormState {
|
impl FormState {
|
||||||
/// Create a new FormState with dynamic fields.
|
/// Create a new FormState with dynamic fields.
|
||||||
pub fn new(fields: Vec<String>) -> Self {
|
pub fn new(fields: Vec<String>) -> Self {
|
||||||
@@ -70,4 +117,15 @@ impl FormState {
|
|||||||
.get_mut(self.current_field)
|
.get_mut(self.current_field)
|
||||||
.expect("Invalid current_field index")
|
.expect("Invalid current_field index")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_from_response(&mut self, response: common::proto::multieko2::adresar::AdresarResponse) {
|
||||||
|
self.id = response.id;
|
||||||
|
self.values = vec![
|
||||||
|
response.firma, response.kz, response.drc,
|
||||||
|
response.ulica, response.psc, response.mesto,
|
||||||
|
response.stat, response.banka, response.ucet,
|
||||||
|
response.skladm, response.ico, response.kontakt,
|
||||||
|
response.telefon, response.skladu, response.fax,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user