This commit is contained in:
Priec
2025-08-22 15:48:30 +02:00
parent 00dad5d673
commit 1d94e82f4b
11 changed files with 131 additions and 138 deletions

View File

@@ -5,7 +5,6 @@ pub mod text_editor;
pub mod background;
pub mod dialog;
pub mod autocomplete;
pub mod search_palette;
pub mod find_file_palette;
pub use command_line::*;
@@ -14,5 +13,4 @@ pub use text_editor::*;
pub use background::*;
pub use dialog::*;
pub use autocomplete::*;
pub use search_palette::*;
pub use find_file_palette::*;

View File

@@ -10,6 +10,7 @@ pub mod services;
pub mod utils;
pub mod buffer;
pub mod sidebar;
pub mod search;
pub use ui::run_ui;

View File

@@ -3,6 +3,7 @@ use crate::config::binds::config::Config;
use crate::config::binds::key_sequences::KeySequenceTracker;
use crate::buffer::{AppView, BufferState, switch_buffer, functions, toggle_buffer_list};
use crate::sidebar::toggle_sidebar;
use crate::search::event::handle_search_palette_event;
use crate::functions::modes::navigation::add_logic_nav;
use crate::functions::modes::navigation::add_logic_nav::SaveLogicResultSender;
use crate::functions::modes::navigation::add_table_nav::SaveTableResultSender;
@@ -20,7 +21,6 @@ use crate::services::grpc_client::GrpcClient;
use canvas::{FormEditor, AppMode as CanvasMode};
use crate::state::{
app::{
search::SearchState,
state::AppState,
},
pages::{
@@ -30,6 +30,7 @@ use crate::state::{
intro::IntroState,
},
};
use crate::search::state::SearchState;
use crate::tui::functions::common::login::LoginResult;
use crate::tui::functions::common::register::RegisterResult;
use crate::tui::{
@@ -215,132 +216,6 @@ impl EventHandler {
}
}
// This function handles state changes.
async fn handle_search_palette_event(
&mut self,
key_event: KeyEvent,
app_state: &mut AppState,
) -> Result<EventOutcome> {
let mut should_close = false;
let mut outcome_message = String::new();
let mut trigger_search = false;
// Step 1: Handle search_state logic in a short scope
let (maybe_data, maybe_id) = {
if let Some(search_state) = app_state.search_state.as_mut() {
match key_event.code {
KeyCode::Esc => {
should_close = true;
outcome_message = "Search cancelled".to_string();
(None, None)
}
KeyCode::Enter => {
if let Some(selected_hit) =
search_state.results.get(search_state.selected_index)
{
if let Ok(data) = serde_json::from_str::<
std::collections::HashMap<String, String>,
>(&selected_hit.content_json)
{
(Some(data), Some(selected_hit.id))
} else {
(None, None)
}
} else {
(None, None)
}
}
KeyCode::Up => {
search_state.previous_result();
(None, None)
}
KeyCode::Down => {
search_state.next_result();
(None, None)
}
KeyCode::Char(c) => {
search_state.input.insert(search_state.cursor_position, c);
search_state.cursor_position += 1;
trigger_search = true;
(None, None)
}
KeyCode::Backspace => {
if search_state.cursor_position > 0 {
search_state.cursor_position -= 1;
search_state.input.remove(search_state.cursor_position);
trigger_search = true;
}
(None, None)
}
KeyCode::Left => {
search_state.cursor_position =
search_state.cursor_position.saturating_sub(1);
(None, None)
}
KeyCode::Right => {
if search_state.cursor_position < search_state.input.len() {
search_state.cursor_position += 1;
}
(None, None)
}
_ => (None, None),
}
} else {
(None, None)
}
};
// Step 2: Now safe to borrow form_state
if let (Some(data), Some(id)) = (maybe_data, maybe_id) {
if let Some(fs) = app_state.form_state_mut() {
let detached_pos = fs.total_count + 2;
fs.update_from_response(&data, detached_pos);
}
should_close = true;
outcome_message = format!("Loaded record ID {}", id);
}
// Step 3: Trigger async search if needed
if trigger_search {
if let Some(search_state) = app_state.search_state.as_mut() {
search_state.is_loading = true;
search_state.results.clear();
search_state.selected_index = 0;
let query = search_state.input.clone();
let table_name = search_state.table_name.clone();
let sender = self.search_result_sender.clone();
let mut grpc_client = self.grpc_client.clone();
info!("--- 1. Spawning search task for query: '{}' ---", query);
tokio::spawn(async move {
info!("--- 2. Background task started. ---");
match grpc_client.search_table(table_name, query).await {
Ok(response) => {
info!(
"--- 3a. gRPC call successful. Found {} hits. ---",
response.hits.len()
);
let _ = sender.send(response.hits);
}
Err(e) => {
error!("--- 3b. gRPC call failed: {:?} ---", e);
let _ = sender.send(vec![]);
}
}
});
}
}
if should_close {
app_state.search_state = None;
app_state.ui.show_search_palette = false;
app_state.ui.focus_outside_canvas = false;
}
Ok(EventOutcome::Ok(outcome_message))
}
#[allow(clippy::too_many_arguments)]
pub async fn handle_event(
&mut self,
@@ -358,9 +233,16 @@ impl EventHandler {
) -> Result<EventOutcome> {
if app_state.ui.show_search_palette {
if let Event::Key(key_event) = event {
return self.handle_search_palette_event(key_event, app_state).await;
if let Some(message) = handle_search_palette_event(
key_event,
app_state,
&mut self.grpc_client,
self.search_result_sender.clone(),
).await? {
return Ok(EventOutcome::Ok(message));
}
return Ok(EventOutcome::Ok(String::new()));
}
return Ok(EventOutcome::Ok(String::new()));
}
let mut current_mode =

View File

@@ -7,4 +7,3 @@ pub mod canvas;
pub use handlers::*;
pub use general::*;
pub use common::*;
pub use canvas::*;

107
client/src/search/event.rs Normal file
View File

@@ -0,0 +1,107 @@
// src/search/event.rs
use crate::search::state::SearchState;
use crate::state::app::state::AppState;
use crate::services::grpc_client::GrpcClient;
use common::proto::komp_ac::search::search_response::Hit;
use crossterm::event::KeyCode;
use tokio::sync::mpsc;
use tracing::{error, info};
use std::collections::HashMap;
use anyhow::Result;
pub async fn handle_search_palette_event(
key_event: crossterm::event::KeyEvent,
app_state: &mut AppState,
grpc_client: &mut GrpcClient,
search_result_sender: mpsc::UnboundedSender<Vec<Hit>>,
) -> Result<Option<String>> {
let mut should_close = false;
let mut outcome_message = None;
let mut trigger_search = false;
if let Some(search_state) = app_state.search_state.as_mut() {
match key_event.code {
KeyCode::Esc => {
should_close = true;
outcome_message = Some("Search cancelled".to_string());
}
KeyCode::Enter => {
// Step 1: Extract the data we need while holding the borrow
let maybe_data = search_state
.results
.get(search_state.selected_index)
.map(|hit| (hit.id, hit.content_json.clone()));
// Step 2: Process outside the borrow
if let Some((id, content_json)) = maybe_data {
if let Ok(data) = serde_json::from_str::<HashMap<String, String>>(&content_json) {
if let Some(fs) = app_state.form_state_mut() {
let detached_pos = fs.total_count + 2;
fs.update_from_response(&data, detached_pos);
}
should_close = true;
outcome_message = Some(format!("Loaded record ID {}", id));
}
}
}
KeyCode::Up => search_state.previous_result(),
KeyCode::Down => search_state.next_result(),
KeyCode::Char(c) => {
search_state.input.insert(search_state.cursor_position, c);
search_state.cursor_position += 1;
trigger_search = true;
}
KeyCode::Backspace => {
if search_state.cursor_position > 0 {
search_state.cursor_position -= 1;
search_state.input.remove(search_state.cursor_position);
trigger_search = true;
}
}
KeyCode::Left => {
search_state.cursor_position =
search_state.cursor_position.saturating_sub(1);
}
KeyCode::Right => {
if search_state.cursor_position < search_state.input.len() {
search_state.cursor_position += 1;
}
}
_ => {}
}
}
if trigger_search {
if let Some(search_state) = app_state.search_state.as_mut() {
search_state.is_loading = true;
search_state.results.clear();
search_state.selected_index = 0;
let query = search_state.input.clone();
let table_name = search_state.table_name.clone();
let sender = search_result_sender.clone();
let mut grpc_client = grpc_client.clone();
info!("Spawning search task for query: '{}'", query);
tokio::spawn(async move {
match grpc_client.search_table(table_name, query).await {
Ok(response) => {
let _ = sender.send(response.hits);
}
Err(e) => {
error!("Search failed: {:?}", e);
let _ = sender.send(vec![]);
}
}
});
}
}
if should_close {
app_state.search_state = None;
app_state.ui.show_search_palette = false;
app_state.ui.focus_outside_canvas = false;
}
Ok(outcome_message)
}

7
client/src/search/mod.rs Normal file
View File

@@ -0,0 +1,7 @@
// src/search/mod.rs
pub mod state;
pub mod ui;
pub mod event;
pub use ui::*;

View File

@@ -1,4 +1,4 @@
// src/state/app/search.rs
// src/search/state.rs
use common::proto::komp_ac::search::search_response::Hit;

View File

@@ -1,7 +1,7 @@
// src/components/common/search_palette.rs
// src/search/ui.rs
use crate::config::colors::themes::Theme;
use crate::state::app::search::SearchState;
use crate::search::state::SearchState;
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Modifier, Style},

View File

@@ -1,4 +1,3 @@
// src/state/app.rs
pub mod state;
pub mod search;

View File

@@ -5,7 +5,7 @@ use common::proto::komp_ac::table_definition::ProfileTreeResponse;
// NEW: Import the types we need for the cache
use common::proto::komp_ac::table_structure::TableStructureResponse;
use crate::modes::handlers::mode_manager::AppMode;
use crate::state::app::search::SearchState;
use crate::search::state::SearchState;
use crate::ui::handlers::context::DialogPurpose;
use crate::state::pages::form::FormState;
use crate::config::binds::Config;

View File

@@ -6,7 +6,6 @@ use crate::components::{
auth::{login::render_login, register::render_register},
common::dialog::render_dialog,
common::find_file_palette,
common::search_palette::render_search_palette,
intro::intro::render_intro,
render_background,
render_command_line,
@@ -14,6 +13,7 @@ use crate::components::{
};
use crate::sidebar::{calculate_sidebar_layout, render_sidebar};
use crate::buffer::render_buffer_list;
use crate::search::render_search_palette;
use crate::config::colors::themes::Theme;
use crate::modes::general::command_navigation::NavigationState;
use crate::buffer::state::BufferState;