Files
komp_ac/client/src/search/event.rs

113 lines
4.3 KiB
Rust

// src/search/event.rs
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) {
// Use current view path to access the active form
if let (Some(profile), Some(table)) = (
app_state.current_view_profile_name.clone(),
app_state.current_view_table_name.clone(),
) {
let path = format!("{}/{}", profile, table);
if let Some(fs) = app_state.form_state_for_path(&path) {
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;
}
Ok(outcome_message)
}